xref: /core/sc/source/core/data/table3.cxx (revision b60b6bfa)
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 <editeng/brushitem.hxx>
23 #include <editeng/colritem.hxx>
24 #include <unotools/textsearch.hxx>
25 #include <svl/numformat.hxx>
26 #include <svl/zforlist.hxx>
27 #include <svl/zformat.hxx>
28 #include <unotools/charclass.hxx>
29 #include <unotools/collatorwrapper.hxx>
30 #include <stdlib.h>
31 #include <unotools/transliterationwrapper.hxx>
32 #include <com/sun/star/i18n/KParseTokens.hpp>
33 #include <com/sun/star/i18n/KParseType.hpp>
34 #include <sal/log.hxx>
35 #include <osl/diagnose.h>
36 
37 #include <refdata.hxx>
38 #include <table.hxx>
39 #include <scitems.hxx>
40 #include <formulacell.hxx>
41 #include <document.hxx>
42 #include <globstr.hrc>
43 #include <scresid.hxx>
44 #include <global.hxx>
45 #include <stlpool.hxx>
46 #include <patattr.hxx>
47 #include <subtotal.hxx>
48 #include <docoptio.hxx>
49 #include <markdata.hxx>
50 #include <rangelst.hxx>
51 #include <userlist.hxx>
52 #include <progress.hxx>
53 #include <cellform.hxx>
54 #include <queryparam.hxx>
55 #include <queryentry.hxx>
56 #include <subtotalparam.hxx>
57 #include <docpool.hxx>
58 #include <cellvalue.hxx>
59 #include <tokenarray.hxx>
60 #include <mtvcellfunc.hxx>
61 #include <columnspanset.hxx>
62 #include <fstalgorithm.hxx>
63 #include <listenercontext.hxx>
64 #include <sharedformula.hxx>
65 #include <stlsheet.hxx>
66 #include <refhint.hxx>
67 #include <listenerquery.hxx>
68 #include <bcaslot.hxx>
69 #include <reordermap.hxx>
70 #include <drwlayer.hxx>
71 #include <conditio.hxx>
72 #include <colorscale.hxx>
73 
74 #include <svl/sharedstringpool.hxx>
75 
76 #include <memory>
77 #include <set>
78 #include <unordered_set>
79 #include <vector>
80 #include <mdds/flat_segment_tree.hpp>
81 
82 using namespace ::com::sun::star;
83 
84 namespace naturalsort {
85 
86 using namespace ::com::sun::star::i18n;
87 
88 /** Splits a given string into three parts: the prefix, number string, and
89     the suffix.
90 
91     @param sWhole
92     Original string to be split into pieces
93 
94     @param sPrefix
95     Prefix string that consists of the part before the first number token.
96     If no number was found, sPrefix is unchanged.
97 
98     @param sSuffix
99     String after the last number token.  This may still contain number strings.
100     If no number was found, sSuffix is unchanged.
101 
102     @param fNum
103     Number converted from the middle number string
104     If no number was found, fNum is unchanged.
105 
106     @return Returns TRUE if a numeral element is found in a given string, or
107     FALSE if no numeral element is found.
108 */
109 static bool SplitString( const OUString &sWhole,
110     OUString &sPrefix, OUString &sSuffix, double &fNum )
111 {
112     // Get prefix element, search for any digit and stop.
113     sal_Int32 nPos = 0;
114     while (nPos < sWhole.getLength())
115     {
116         const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos);
117         if (nType & KCharacterType::DIGIT)
118             break;
119         sWhole.iterateCodePoints( &nPos );
120     }
121 
122     // Return FALSE if no numeral element is found
123     if ( nPos == sWhole.getLength() )
124         return false;
125 
126     // Get numeral element
127     const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep();
128     ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken(
129         KParseType::ANY_NUMBER, sWhole, nPos,
130         KParseTokens::ANY_NUMBER, "", KParseTokens::ANY_NUMBER, sUser );
131 
132     if ( aPRNum.EndPos == nPos )
133     {
134         SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
135                 nPos << " : " << sWhole);
136         return false;
137     }
138 
139     sPrefix = sWhole.copy( 0, nPos );
140     fNum = aPRNum.Value;
141     sSuffix = sWhole.copy( aPRNum.EndPos );
142 
143     return true;
144 }
145 
146 /** Naturally compares two given strings.
147 
148     This is the main function that should be called externally.  It returns
149     either 1, 0, or -1 depending on the comparison result of given two strings.
150 
151     @param sInput1
152     Input string 1
153 
154     @param sInput2
155     Input string 2
156 
157     @param bCaseSens
158     Boolean value for case sensitivity
159 
160     @param pData
161     Pointer to user defined sort list
162 
163     @param pCW
164     Pointer to collator wrapper for normal string comparison
165 
166     @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
167     sInput2 is greater.
168 */
169 static short Compare( const OUString &sInput1, const OUString &sInput2,
170                const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
171 {
172     OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
173 
174     do
175     {
176         double nNum1, nNum2;
177         bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
178         bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
179 
180         short nPreRes; // Prefix comparison result
181         if ( pData )
182         {
183             if ( bCaseSens )
184             {
185                 if ( !bNumFound1 || !bNumFound2 )
186                     return static_cast<short>(pData->Compare( sStr1, sStr2 ));
187                 else
188                     nPreRes = pData->Compare( sPre1, sPre2 );
189             }
190             else
191             {
192                 if ( !bNumFound1 || !bNumFound2 )
193                     return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
194                 else
195                     nPreRes = pData->ICompare( sPre1, sPre2 );
196             }
197         }
198         else
199         {
200             if ( !bNumFound1 || !bNumFound2 )
201                 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
202             else
203                 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
204         }
205 
206         // Prefix strings differ.  Return immediately.
207         if ( nPreRes != 0 ) return nPreRes;
208 
209         if ( nNum1 != nNum2 )
210         {
211             if ( nNum1 < nNum2 ) return -1;
212             return (nNum1 > nNum2) ? 1 : 0;
213         }
214 
215         // The prefix and the first numerical elements are equal, but the suffix
216         // strings may still differ.  Stay in the loop.
217 
218         sStr1 = sSuf1;
219         sStr2 = sSuf2;
220 
221     } while (true);
222 
223     return 0;
224 }
225 
226 }
227 
228 namespace {
229 
230 struct ScSortInfo final
231 {
232     ScRefCellValue maCell;
233     SCCOLROW        nOrg;
234 };
235 
236 }
237 
238 class ScSortInfoArray
239 {
240 public:
241 
242     struct Cell
243     {
244         ScRefCellValue maCell;
245         const sc::CellTextAttr* mpAttr;
246         const ScPostIt* mpNote;
247         std::vector<SdrObject*> maDrawObjects;
248         const ScPatternAttr* mpPattern;
249 
250         Cell() : mpAttr(nullptr), mpNote(nullptr),  mpPattern(nullptr) {}
251     };
252 
253     struct Row
254     {
255         std::vector<Cell> maCells;
256 
257         bool mbHidden:1;
258         bool mbFiltered:1;
259 
260         explicit Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
261     };
262 
263     typedef std::vector<Row> RowsType;
264 
265 private:
266     std::unique_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
267 
268     std::vector<std::unique_ptr<ScSortInfo[]>> mvppInfo;
269     SCCOLROW        nStart;
270     SCCOLROW        mnLastIndex; /// index of last non-empty cell position.
271 
272     std::vector<SCCOLROW> maOrderIndices;
273     bool mbKeepQuery;
274     bool mbUpdateRefs;
275 
276 public:
277     ScSortInfoArray(const ScSortInfoArray&) = delete;
278     const ScSortInfoArray& operator=(const ScSortInfoArray&) = delete;
279 
280     ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
281         mvppInfo(nSorts),
282         nStart( nInd1 ),
283         mnLastIndex(nInd2),
284         mbKeepQuery(false),
285         mbUpdateRefs(false)
286     {
287         SCSIZE nCount( nInd2 - nInd1 + 1 );
288         if (nSorts)
289         {
290             for ( sal_uInt16 nSort = 0; nSort < nSorts; nSort++ )
291             {
292                 mvppInfo[nSort].reset(new ScSortInfo[nCount]);
293             }
294         }
295 
296         for (size_t i = 0; i < nCount; ++i)
297             maOrderIndices.push_back(i+nStart);
298     }
299 
300     void SetKeepQuery( bool b ) { mbKeepQuery = b; }
301 
302     bool IsKeepQuery() const { return mbKeepQuery; }
303 
304     void SetUpdateRefs( bool b ) { mbUpdateRefs = b; }
305 
306     bool IsUpdateRefs() const { return mbUpdateRefs; }
307 
308     /**
309      * Call this only during normal sorting, not from reordering.
310      */
311     std::unique_ptr<ScSortInfo[]> const & GetFirstArray() const
312     {
313         return mvppInfo[0];
314     }
315 
316     /**
317      * Call this only during normal sorting, not from reordering.
318      */
319     ScSortInfo & Get( sal_uInt16 nSort, SCCOLROW nInd )
320     {
321         return mvppInfo[nSort][ nInd - nStart ];
322     }
323 
324     /**
325      * Call this only during normal sorting, not from reordering.
326      */
327     void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
328     {
329         if (nInd1 == nInd2) // avoid self-move-assign
330             return;
331         SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
332         SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
333         for ( sal_uInt16 nSort = 0; nSort < static_cast<sal_uInt16>(mvppInfo.size()); nSort++ )
334         {
335             auto & ppInfo = mvppInfo[nSort];
336             std::swap(ppInfo[n1], ppInfo[n2]);
337         }
338 
339         std::swap(maOrderIndices[n1], maOrderIndices[n2]);
340 
341         if (mpRows)
342         {
343             // Swap rows in data table.
344             RowsType& rRows = *mpRows;
345             std::swap(rRows[n1], rRows[n2]);
346         }
347     }
348 
349     void SetOrderIndices( const std::vector<SCCOLROW>& rIndices )
350     {
351         maOrderIndices = rIndices;
352     }
353 
354     /**
355      * @param rIndices indices are actual row positions on the sheet, not an
356      *                 offset from the top row.
357      */
358     void ReorderByRow( const std::vector<SCCOLROW>& rIndices )
359     {
360         if (!mpRows)
361             return;
362 
363         RowsType& rRows = *mpRows;
364 
365         std::vector<SCCOLROW> aOrderIndices2;
366         aOrderIndices2.reserve(rIndices.size());
367 
368         RowsType aRows2;
369         aRows2.reserve(rRows.size());
370 
371         for (const auto& rIndex : rIndices)
372         {
373             size_t nPos = rIndex - nStart; // switch to an offset to top row.
374             aRows2.push_back(rRows[nPos]);
375             aOrderIndices2.push_back(maOrderIndices[nPos]);
376         }
377 
378         rRows.swap(aRows2);
379         maOrderIndices.swap(aOrderIndices2);
380     }
381 
382     sal_uInt16      GetUsedSorts() const { return mvppInfo.size(); }
383 
384     SCCOLROW    GetStart() const { return nStart; }
385     SCCOLROW GetLast() const { return mnLastIndex; }
386 
387     const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; }
388 
389     RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
390     {
391         mpRows.reset(new RowsType);
392         mpRows->resize(nRowSize, Row(nColSize));
393         return *mpRows;
394     }
395 
396     RowsType* GetDataRows()
397     {
398         return mpRows.get();
399     }
400 };
401 
402 // Assume that we can handle 512MB, which with a ~100 bytes
403 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
404 // overhead in one chunk.
405 constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell);
406 
407 namespace {
408 
409 void initDataRows(
410     ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols,
411     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
412     bool bHiddenFiltered, bool bPattern, bool bCellNotes, bool bCellDrawObjects, bool bOnlyDataAreaExtras )
413 {
414     // Fill row-wise data table.
415     ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
416 
417     const std::vector<SCCOLROW>& rOrderIndices = rArray.GetOrderIndices();
418     assert(!bOnlyDataAreaExtras || (rOrderIndices.size() == static_cast<size_t>(nRow2 - nRow1 + 1)
419                 && nRow1 == rArray.GetStart()));
420 
421     ScDrawLayer* pDrawLayer = (bCellDrawObjects ? rTab.GetDoc().GetDrawLayer() : nullptr);
422     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
423     {
424         ScColumn& rCol = rCols[nCol];
425 
426         // Skip reordering of cell formats if the whole span is on the same pattern entry.
427         bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
428 
429         sc::ColumnBlockConstPosition aBlockPos;
430         rCol.InitBlockPosition(aBlockPos);
431         std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects;
432         if (pDrawLayer)
433             aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2);
434 
435         for (SCROW nR = nRow1; nR <= nRow2; ++nR)
436         {
437             const SCROW nRow = (bOnlyDataAreaExtras ? rOrderIndices[nR - rArray.GetStart()] : nR);
438             ScSortInfoArray::Row& rRow = rRows[nR-nRow1];
439             ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
440             if (!bOnlyDataAreaExtras)
441             {
442                 rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
443                 rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
444             }
445             if (bCellNotes)
446                 rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
447             if (pDrawLayer)
448                 rCell.maDrawObjects = aRowDrawObjects[nRow];
449 
450             if (!bUniformPattern && bPattern)
451                 rCell.mpPattern = rCol.GetPattern(nRow);
452         }
453     }
454 
455     if (!bOnlyDataAreaExtras && bHiddenFiltered)
456     {
457         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
458         {
459             ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
460             rRow.mbHidden = rTab.RowHidden(nRow);
461             rRow.mbFiltered = rTab.RowFiltered(nRow);
462         }
463     }
464 }
465 
466 }
467 
468 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
469 {
470     std::unique_ptr<ScSortInfoArray> pArray;
471 
472     if (rParam.mbByRow)
473     {
474         // Create a sort info array with just the data table.
475         SCROW nRow1 = rParam.maSortRange.aStart.Row();
476         SCROW nRow2 = rParam.maSortRange.aEnd.Row();
477         SCCOL nCol1 = rParam.maSortRange.aStart.Col();
478         SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
479 
480         pArray.reset(new ScSortInfoArray(0, nRow1, nRow2));
481         pArray->SetKeepQuery(rParam.mbHiddenFiltered);
482         pArray->SetUpdateRefs(rParam.mbUpdateRefs);
483 
484         initDataRows( *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2, rParam.mbHiddenFiltered,
485                 rParam.maDataAreaExtras.mbCellFormats, true, true, false);
486     }
487     else
488     {
489         SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
490         SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
491 
492         pArray.reset(new ScSortInfoArray(0, nCol1, nCol2));
493         pArray->SetKeepQuery(rParam.mbHiddenFiltered);
494         pArray->SetUpdateRefs(rParam.mbUpdateRefs);
495     }
496 
497     return pArray;
498 }
499 
500 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray(
501     const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
502     bool bKeepQuery, bool bUpdateRefs )
503 {
504     sal_uInt16 nUsedSorts = 1;
505     while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
506         nUsedSorts++;
507     std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ));
508     pArray->SetKeepQuery(bKeepQuery);
509     pArray->SetUpdateRefs(bUpdateRefs);
510 
511     if ( rSortParam.bByRow )
512     {
513         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
514         {
515             SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
516             ScColumn* pCol = &aCol[nCol];
517             sc::ColumnBlockConstPosition aBlockPos;
518             pCol->InitBlockPosition(aBlockPos);
519             for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
520             {
521                 ScSortInfo & rInfo = pArray->Get( nSort, nRow );
522                 rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow);
523                 rInfo.nOrg = nRow;
524             }
525         }
526 
527         initDataRows( *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2, bKeepQuery,
528                 rSortParam.aDataAreaExtras.mbCellFormats, true, true, false);
529     }
530     else
531     {
532         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
533         {
534             SCROW nRow = rSortParam.maKeyState[nSort].nField;
535             for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
536                     nCol <= static_cast<SCCOL>(nInd2); nCol++ )
537             {
538                 ScSortInfo & rInfo = pArray->Get( nSort, nCol );
539                 rInfo.maCell = GetCellValue(nCol, nRow);
540                 rInfo.nOrg = nCol;
541             }
542         }
543     }
544     return pArray;
545 }
546 
547 namespace {
548 
549 struct SortedColumn
550 {
551     typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
552 
553     sc::CellStoreType maCells;
554     sc::CellTextAttrStoreType maCellTextAttrs;
555     sc::BroadcasterStoreType maBroadcasters;
556     sc::CellNoteStoreType maCellNotes;
557     std::vector<std::vector<SdrObject*>> maCellDrawObjects;
558 
559     PatRangeType maPatterns;
560     PatRangeType::const_iterator miPatternPos;
561 
562     SortedColumn(const SortedColumn&) = delete;
563     const SortedColumn operator=(const SortedColumn&) = delete;
564 
565     explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) :
566         maCells(nTopEmptyRows),
567         maCellTextAttrs(nTopEmptyRows),
568         maBroadcasters(nTopEmptyRows),
569         maCellNotes(nTopEmptyRows),
570         maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr),
571         miPatternPos(maPatterns.begin()) {}
572 
573     void setPattern( SCROW nRow, const ScPatternAttr* pPat )
574     {
575         miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
576     }
577 };
578 
579 struct SortedRowFlags
580 {
581     typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
582 
583     FlagsType maRowsHidden;
584     FlagsType maRowsFiltered;
585     FlagsType::const_iterator miPosHidden;
586     FlagsType::const_iterator miPosFiltered;
587 
588     SortedRowFlags(const ScSheetLimits& rSheetLimits) :
589         maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false),
590         maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false),
591         miPosHidden(maRowsHidden.begin()),
592         miPosFiltered(maRowsFiltered.begin()) {}
593 
594     void setRowHidden( SCROW nRow, bool b )
595     {
596         miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
597     }
598 
599     void setRowFiltered( SCROW nRow, bool b )
600     {
601         miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
602     }
603 
604     void swap( SortedRowFlags& r )
605     {
606         maRowsHidden.swap(r.maRowsHidden);
607         maRowsFiltered.swap(r.maRowsFiltered);
608 
609         // Just reset the position hints.
610         miPosHidden = maRowsHidden.begin();
611         miPosFiltered = maRowsFiltered.begin();
612     }
613 };
614 
615 struct PatternSpan
616 {
617     SCROW mnRow1;
618     SCROW mnRow2;
619     const ScPatternAttr* mpPattern;
620 
621     PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
622         mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
623 };
624 
625 }
626 
627 bool ScTable::IsSortCollatorGlobal() const
628 {
629     return  pSortCollator == &ScGlobal::GetCollator() ||
630             pSortCollator == &ScGlobal::GetCaseCollator();
631 }
632 
633 void ScTable::InitSortCollator( const ScSortParam& rPar )
634 {
635     if ( !rPar.aCollatorLocale.Language.isEmpty() )
636     {
637         if ( !pSortCollator || IsSortCollatorGlobal() )
638             pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
639         pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
640             rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
641     }
642     else
643     {   // SYSTEM
644         DestroySortCollator();
645         pSortCollator = &ScGlobal::GetCollator(rPar.bCaseSens);
646     }
647 }
648 
649 void ScTable::DestroySortCollator()
650 {
651     if ( pSortCollator )
652     {
653         if ( !IsSortCollatorGlobal() )
654             delete pSortCollator;
655         pSortCollator = nullptr;
656     }
657 }
658 
659 namespace {
660 
661 template<typename Hint, typename ReorderMap, typename Index>
662 class ReorderNotifier
663 {
664     Hint maHint;
665 public:
666     ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) :
667         maHint(rMap, nTab, nPos1, nPos2) {}
668 
669     void operator() ( SvtListener* p )
670     {
671         p->Notify(maHint);
672     }
673 };
674 
675 class FormulaGroupPosCollector
676 {
677     sc::RefQueryFormulaGroup& mrQuery;
678 
679 public:
680     explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
681 
682     void operator() ( const SvtListener* p )
683     {
684         p->Query(mrQuery);
685     }
686 };
687 
688 void fillSortedColumnArray(
689     std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
690     SortedRowFlags& rRowFlags,
691     std::vector<SvtListener*>& rCellListeners,
692     ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable,
693     bool bOnlyDataAreaExtras )
694 {
695     assert(!bOnlyDataAreaExtras || !pArray->IsUpdateRefs());
696 
697     SCROW nRow1 = pArray->GetStart();
698     ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
699     std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices();
700 
701     size_t nColCount = nCol2 - nCol1 + 1;
702     std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
703     SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
704     aSortedCols.reserve(nColCount);
705     for (size_t i = 0; i < nColCount; ++i)
706     {
707         // In the sorted column container, element positions and row
708         // positions must match, else formula cells may mis-behave during
709         // grouping.
710         aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
711     }
712 
713     for (size_t i = 0; i < pRows->size(); ++i)
714     {
715         const SCROW nRow = nRow1 + i;
716 
717         ScSortInfoArray::Row& rRow = (*pRows)[i];
718         for (size_t j = 0; j < rRow.maCells.size(); ++j)
719         {
720             ScSortInfoArray::Cell& rCell = rRow.maCells[j];
721 
722             // If bOnlyDataAreaExtras,
723             // sc::CellStoreType aSortedCols.at(j)->maCells
724             // and
725             // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs
726             // are by definition all empty mdds::multi_type_vector, so nothing
727             // needs to be done to push *all* empty.
728 
729             if (!bOnlyDataAreaExtras)
730             {
731                 sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells;
732                 switch (rCell.maCell.meType)
733                 {
734                     case CELLTYPE_STRING:
735                         assert(rCell.mpAttr);
736                         rCellStore.push_back(*rCell.maCell.mpString);
737                     break;
738                     case CELLTYPE_VALUE:
739                         assert(rCell.mpAttr);
740                         rCellStore.push_back(rCell.maCell.mfValue);
741                     break;
742                     case CELLTYPE_EDIT:
743                         assert(rCell.mpAttr);
744                         rCellStore.push_back(rCell.maCell.mpEditText->Clone().release());
745                     break;
746                     case CELLTYPE_FORMULA:
747                         {
748                             assert(rCell.mpAttr);
749                             ScAddress aOldPos = rCell.maCell.mpFormula->aPos;
750 
751                             const ScAddress aCellPos(nCol1 + j, nRow, nTab);
752                             ScFormulaCell* pNew = rCell.maCell.mpFormula->Clone( aCellPos );
753                             if (pArray->IsUpdateRefs())
754                             {
755                                 pNew->CopyAllBroadcasters(*rCell.maCell.mpFormula);
756                                 pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
757                             }
758                             else
759                             {
760                                 pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
761                             }
762 
763                             if (!rCellListeners.empty())
764                             {
765                                 // Original source cells will be deleted during
766                                 // sc::CellStoreType::transfer(), SvtListener is a base
767                                 // class, so we need to replace it.
768                                 auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.mpFormula));
769                                 if (it != rCellListeners.end())
770                                     *it = pNew;
771                             }
772 
773                             rCellStore.push_back(pNew);
774                         }
775                     break;
776                     default:
777                         //assert(!rCell.mpAttr);
778                         // This assert doesn't hold, for example
779                         // CopyCellsFromClipHandler may omit copying cells during
780                         // PasteSpecial for which CopyTextAttrsFromClipHandler
781                         // still copies a CellTextAttr. So if that really is not
782                         // expected then fix it there.
783                         rCellStore.push_back_empty();
784                 }
785 
786                 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs;
787                 if (rCell.mpAttr)
788                     rAttrStore.push_back(*rCell.mpAttr);
789                 else
790                     rAttrStore.push_back_empty();
791             }
792 
793             if (pArray->IsUpdateRefs())
794             {
795                 // At this point each broadcaster instance is managed by 2
796                 // containers. We will release those in the original storage
797                 // below before transferring them to the document.
798                 const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
799                 sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters;
800                 if (pBroadcaster)
801                     // A const pointer would be implicitly converted to a bool type.
802                     rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster));
803                 else
804                     rBCStore.push_back_empty();
805             }
806 
807             // The same with cell note instances ...
808             sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes;
809             if (rCell.mpNote)
810                 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
811             else
812                 rNoteStore.push_back_empty();
813 
814             // Add cell anchored images
815             aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects);
816 
817             if (rCell.mpPattern)
818                 aSortedCols.at(j)->setPattern(nRow, rCell.mpPattern);
819         }
820 
821         if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
822         {
823             // Hidden and filtered flags are first converted to segments.
824             aRowFlags.setRowHidden(nRow, rRow.mbHidden);
825             aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
826         }
827 
828         if (pProgress)
829             pProgress->SetStateOnPercent(i);
830     }
831 
832     rSortedCols.swap(aSortedCols);
833     rRowFlags.swap(aRowFlags);
834 }
835 
836 void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
837 {
838     if (nTop < rRange.aStart.Row())
839         rRange.aStart.SetRow(nTop);
840 
841     if (rRange.aEnd.Row() < nBottom)
842         rRange.aEnd.SetRow(nBottom);
843 }
844 
845 class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
846 {
847     std::vector<ScFormulaCell*>& mrCells;
848     ScColumn* mpCol;
849 
850 public:
851     explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
852         mrCells(rCells), mpCol(nullptr) {}
853 
854     virtual void startColumn( ScColumn* pCol ) override
855     {
856         mpCol = pCol;
857     }
858 
859     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
860     {
861         assert(mpCol);
862 
863         if (!bVal)
864             return;
865 
866         mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
867     }
868 };
869 
870 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
871 {
872     ScColumn* mpCol;
873 
874     std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
875     sc::StartListeningContext maStartCxt;
876     sc::EndListeningContext maEndCxt;
877 
878 public:
879     explicit ListenerStartAction( ScDocument& rDoc ) :
880         mpCol(nullptr),
881         mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)),
882         maStartCxt(rDoc, mpPosSet),
883         maEndCxt(rDoc, mpPosSet) {}
884 
885     virtual void startColumn( ScColumn* pCol ) override
886     {
887         mpCol = pCol;
888     }
889 
890     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
891     {
892         assert(mpCol);
893 
894         if (!bVal)
895             return;
896 
897         mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
898     }
899 };
900 
901 }
902 
903 void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray* pArray,
904         SCCOL nDataCol1, SCCOL nDataCol2,
905         const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
906 {
907     const SCROW nRow1 = pArray->GetStart();
908     const SCROW nLastRow = pArray->GetLast();
909     const SCCOL nChunkCols = std::max<SCCOL>( 1, kSortCellsChunk / (nLastRow - nRow1 + 1));
910     // Before data area.
911     for (SCCOL nCol = rDataAreaExtras.mnStartCol; nCol < nDataCol1; nCol += nChunkCols)
912     {
913         const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, nDataCol1 - 1);
914         initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
915                 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
916         SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
917     }
918     // Behind data area.
919     for (SCCOL nCol = nDataCol2 + 1; nCol <= rDataAreaExtras.mnEndCol; nCol += nChunkCols)
920     {
921         const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, rDataAreaExtras.mnEndCol);
922         initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
923                 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
924         SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
925     }
926 }
927 
928 void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray* pArray,
929         SCROW nDataRow1, SCROW nDataRow2, const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
930 {
931     const SCCOL nCol1 = static_cast<SCCOL>(pArray->GetStart());
932     const SCCOL nLastCol = static_cast<SCCOL>(pArray->GetLast());
933     const SCROW nChunkRows = std::max<SCROW>( 1, kSortCellsChunk / (nLastCol - nCol1 + 1));
934     // Above data area.
935     for (SCROW nRow = rDataAreaExtras.mnStartRow; nRow < nDataRow1; nRow += nChunkRows)
936     {
937         const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, nDataRow1 - 1);
938         SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
939     }
940     // Below data area.
941     for (SCROW nRow = nDataRow2 + 1; nRow <= rDataAreaExtras.mnEndRow; nRow += nChunkRows)
942     {
943         const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, rDataAreaExtras.mnEndRow);
944         SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
945     }
946 }
947 
948 void ScTable::SortReorderByColumn(
949     const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
950 {
951     SCCOLROW nStart = pArray->GetStart();
952     SCCOLROW nLast = pArray->GetLast();
953 
954     std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
955     size_t nCount = aIndices.size();
956 
957     // Cut formula grouping at row and reference boundaries before the reordering.
958     ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
959     for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
960         aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
961 
962     // Collect all listeners of cell broadcasters of sorted range.
963     std::vector<SvtListener*> aCellListeners;
964 
965     if (!pArray->IsUpdateRefs())
966     {
967         // Collect listeners of cell broadcasters.
968         for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
969             aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
970 
971         // Remove any duplicate listener entries.  We must ensure that we
972         // notify each unique listener only once.
973         std::sort(aCellListeners.begin(), aCellListeners.end());
974         aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
975 
976         // Notify the cells' listeners to stop listening.
977         /* TODO: for performance this could be enhanced to stop and later
978          * restart only listening to within the reordered range and keep
979          * listening to everything outside untouched. */
980         sc::RefStopListeningHint aHint;
981         for (auto const & l : aCellListeners)
982             l->Notify(aHint);
983     }
984 
985     // table to keep track of column index to position in the index table.
986     std::vector<SCCOLROW> aPosTable(nCount);
987     for (size_t i = 0; i < nCount; ++i)
988         aPosTable[aIndices[i]-nStart] = i;
989 
990     SCCOLROW nDest = nStart;
991     for (size_t i = 0; i < nCount; ++i, ++nDest)
992     {
993         SCCOLROW nSrc = aIndices[i];
994         if (nDest != nSrc)
995         {
996             aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
997 
998             // Update the position of the index that was originally equal to nDest.
999             size_t nPos = aPosTable[nDest-nStart];
1000             aIndices[nPos] = nSrc;
1001             aPosTable[nSrc-nStart] = nPos;
1002         }
1003 
1004         if (pProgress)
1005             pProgress->SetStateOnPercent(i);
1006     }
1007 
1008     // Reset formula cell positions which became out-of-sync after column reordering.
1009     bool bUpdateRefs = pArray->IsUpdateRefs();
1010     for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1011         aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
1012 
1013     if (pArray->IsUpdateRefs())
1014     {
1015         // Set up column reorder map (for later broadcasting of reference updates).
1016         sc::ColRowReorderMapType aColMap;
1017         const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1018         for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1019         {
1020             SCCOL nNew = i + nStart;
1021             SCCOL nOld = rOldIndices[i];
1022             aColMap.emplace(nOld, nNew);
1023         }
1024 
1025         // Collect all listeners within sorted range ahead of time.
1026         std::vector<SvtListener*> aListeners;
1027 
1028         for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1029             aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1030 
1031         // Get all area listeners that listen on one column within the range
1032         // and end their listening.
1033         ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
1034         std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1035                 aMoveRange, sc::AreaOverlapType::OneColumnInside);
1036         {
1037             for (auto& rAreaListener : aAreaListeners)
1038             {
1039                 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1040                 aListeners.push_back( rAreaListener.mpListener);
1041             }
1042         }
1043 
1044         // Remove any duplicate listener entries and notify all listeners
1045         // afterward.  We must ensure that we notify each unique listener only
1046         // once.
1047         std::sort(aListeners.begin(), aListeners.end());
1048         aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1049         ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
1050         std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1051 
1052         // Re-start area listeners on the reordered columns.
1053         {
1054             for (auto& rAreaListener : aAreaListeners)
1055             {
1056                 ScRange aNewRange = rAreaListener.maArea;
1057                 sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
1058                 if (itCol != aColMap.end())
1059                 {
1060                     aNewRange.aStart.SetCol( itCol->second);
1061                     aNewRange.aEnd.SetCol( itCol->second);
1062                 }
1063                 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1064             }
1065         }
1066     }
1067     else    // !(pArray->IsUpdateRefs())
1068     {
1069         // Notify the cells' listeners to (re-)start listening.
1070         sc::RefStartListeningHint aHint;
1071         for (auto const & l : aCellListeners)
1072             l->Notify(aHint);
1073     }
1074 
1075     // Re-join formulas at row boundaries now that all the references have
1076     // been adjusted for column reordering.
1077     for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1078     {
1079         sc::CellStoreType& rCells = aCol[nCol].maCells;
1080         sc::CellStoreType::position_type aPos = rCells.position(nRow1);
1081         sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1082         if (nRow2 < rDocument.MaxRow())
1083         {
1084             aPos = rCells.position(aPos.first, nRow2+1);
1085             sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1086         }
1087     }
1088 }
1089 
1090 void ScTable::SortReorderByRow( ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2,
1091         ScProgress* pProgress, bool bOnlyDataAreaExtras )
1092 {
1093     assert(!pArray->IsUpdateRefs());
1094 
1095     if (nCol2 < nCol1)
1096         return;
1097 
1098     // bOnlyDataAreaExtras:
1099     // Data area extras by definition do not have any cell content so no
1100     // formula cells either, so that handling doesn't need to be executed.
1101     // However, there may be listeners of formulas listening to broadcasters of
1102     // empty cells.
1103 
1104     SCROW nRow1 = pArray->GetStart();
1105     SCROW nRow2 = pArray->GetLast();
1106 
1107     // Collect all listeners of cell broadcasters of sorted range.
1108     std::vector<SvtListener*> aCellListeners;
1109 
1110     // When the update ref mode is disabled, we need to detach all formula
1111     // cells in the sorted range before reordering, and re-start them
1112     // afterward.
1113     if (!bOnlyDataAreaExtras)
1114     {
1115         sc::EndListeningContext aCxt(rDocument);
1116         DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1117     }
1118 
1119     // Collect listeners of cell broadcasters.
1120     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1121         aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
1122 
1123     // Remove any duplicate listener entries.  We must ensure that we notify
1124     // each unique listener only once.
1125     std::sort(aCellListeners.begin(), aCellListeners.end());
1126     aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
1127 
1128     // Notify the cells' listeners to stop listening.
1129     /* TODO: for performance this could be enhanced to stop and later
1130      * restart only listening to within the reordered range and keep
1131      * listening to everything outside untouched. */
1132     {
1133         sc::RefStopListeningHint aHint;
1134         for (auto const & l : aCellListeners)
1135             l->Notify(aHint);
1136     }
1137 
1138     // Split formula groups at the sort range boundaries (if applicable).
1139     if (!bOnlyDataAreaExtras)
1140     {
1141         std::vector<SCROW> aRowBounds;
1142         aRowBounds.reserve(2);
1143         aRowBounds.push_back(nRow1);
1144         aRowBounds.push_back(nRow2+1);
1145         for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1146             SplitFormulaGroups(nCol, aRowBounds);
1147     }
1148 
1149     // Cells in the data rows only reference values in the document. Make
1150     // a copy before updating the document.
1151     std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1152     SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1153     fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2,
1154             pProgress, this, bOnlyDataAreaExtras);
1155 
1156     for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1157     {
1158         SCCOL nThisCol = i + nCol1;
1159 
1160         if (!bOnlyDataAreaExtras)
1161         {
1162             {
1163                 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1164                 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1165                 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1166             }
1167 
1168             {
1169                 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1170                 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1171                 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1172             }
1173         }
1174 
1175         {
1176             sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1177             sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1178 
1179             // Do the same as broadcaster storage transfer (to prevent double deletion).
1180             rDest.release_range(nRow1, nRow2);
1181             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1182             aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1183         }
1184 
1185         // Update draw object positions
1186         aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1187 
1188         {
1189             // Get all row spans where the pattern is not NULL.
1190             std::vector<PatternSpan> aSpans =
1191                 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1192                     aSortedCols[i]->maPatterns);
1193 
1194             for (const auto& rSpan : aSpans)
1195             {
1196                 assert(rSpan.mpPattern); // should never be NULL.
1197                 rDocument.GetPool()->Put(*rSpan.mpPattern);
1198             }
1199 
1200             for (const auto& rSpan : aSpans)
1201             {
1202                 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1203                 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1204             }
1205         }
1206 
1207         aCol[nThisCol].CellStorageModified();
1208     }
1209 
1210     if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
1211     {
1212         aRowFlags.maRowsHidden.build_tree();
1213         aRowFlags.maRowsFiltered.build_tree();
1214 
1215         // Remove all flags in the range first.
1216         SetRowHidden(nRow1, nRow2, false);
1217         SetRowFiltered(nRow1, nRow2, false);
1218 
1219         std::vector<sc::RowSpan> aSpans =
1220             sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1221 
1222         for (const auto& rSpan : aSpans)
1223             SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1224 
1225         aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1226 
1227         for (const auto& rSpan : aSpans)
1228             SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1229     }
1230 
1231     // Notify the cells' listeners to (re-)start listening.
1232     {
1233         sc::RefStartListeningHint aHint;
1234         for (auto const & l : aCellListeners)
1235             l->Notify(aHint);
1236     }
1237 
1238     if (!bOnlyDataAreaExtras)
1239     {
1240         // Re-group columns in the sorted range too.
1241         for (SCCOL i = nCol1; i <= nCol2; ++i)
1242             aCol[i].RegroupFormulaCells();
1243 
1244         {
1245             sc::StartListeningContext aCxt(rDocument);
1246             AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1247         }
1248     }
1249 }
1250 
1251 void ScTable::SortReorderByRowRefUpdate(
1252     ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1253 {
1254     assert(pArray->IsUpdateRefs());
1255 
1256     if (nCol2 < nCol1)
1257         return;
1258 
1259     SCROW nRow1 = pArray->GetStart();
1260     SCROW nRow2 = pArray->GetLast();
1261 
1262     ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1263     sc::ColumnSpanSet aGrpListenerRanges;
1264 
1265     {
1266         // Get the range of formula group listeners within sorted range (if any).
1267         sc::QueryRange aQuery;
1268 
1269         ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM();
1270         std::vector<sc::AreaListener> aGrpListeners =
1271             pBASM->GetAllListeners(
1272                 aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group);
1273 
1274         {
1275             for (const auto& rGrpListener : aGrpListeners)
1276             {
1277                 assert(rGrpListener.mbGroupListening);
1278                 SvtListener* pGrpLis = rGrpListener.mpListener;
1279                 pGrpLis->Query(aQuery);
1280                 rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis);
1281             }
1282         }
1283 
1284         ScRangeList aTmp;
1285         aQuery.swapRanges(aTmp);
1286 
1287         // If the range is within the sorted range, we need to expand its rows
1288         // to the top and bottom of the sorted range, since the formula cells
1289         // could be anywhere in the sorted range after reordering.
1290         for (size_t i = 0, n = aTmp.size(); i < n; ++i)
1291         {
1292             ScRange aRange = aTmp[i];
1293             if (!aMoveRange.Intersects(aRange))
1294             {
1295                 // Doesn't overlap with the sorted range at all.
1296                 aGrpListenerRanges.set(GetDoc(), aRange, true);
1297                 continue;
1298             }
1299 
1300             if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
1301             {
1302                 // Its column range is within the column range of the sorted range.
1303                 expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1304                 aGrpListenerRanges.set(GetDoc(), aRange, true);
1305                 continue;
1306             }
1307 
1308             // It intersects with the sorted range, but its column range is
1309             // not within the column range of the sorted range.  Split it into
1310             // 2 ranges.
1311             ScRange aR1 = aRange;
1312             ScRange aR2 = aRange;
1313             if (aRange.aStart.Col() < aMoveRange.aStart.Col())
1314             {
1315                 // Left half is outside the sorted range while the right half is inside.
1316                 aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
1317                 aR2.aStart.SetCol(aMoveRange.aStart.Col());
1318                 expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1319             }
1320             else
1321             {
1322                 // Left half is inside the sorted range while the right half is outside.
1323                 aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
1324                 aR2.aStart.SetCol(aMoveRange.aEnd.Col());
1325                 expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1326             }
1327 
1328             aGrpListenerRanges.set(GetDoc(), aR1, true);
1329             aGrpListenerRanges.set(GetDoc(), aR2, true);
1330         }
1331     }
1332 
1333     // Split formula groups at the sort range boundaries (if applicable).
1334     std::vector<SCROW> aRowBounds;
1335     aRowBounds.reserve(2);
1336     aRowBounds.push_back(nRow1);
1337     aRowBounds.push_back(nRow2+1);
1338     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1339         SplitFormulaGroups(nCol, aRowBounds);
1340 
1341     // Cells in the data rows only reference values in the document. Make
1342     // a copy before updating the document.
1343     std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1344     SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1345     std::vector<SvtListener*> aListenersDummy;
1346     fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false);
1347 
1348     for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1349     {
1350         SCCOL nThisCol = i + nCol1;
1351 
1352         {
1353             sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1354             sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1355             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1356         }
1357 
1358         {
1359             sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1360             sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1361             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1362         }
1363 
1364         {
1365             sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters;
1366             sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
1367 
1368             // Release current broadcasters first, to prevent them from getting deleted.
1369             rDest.release_range(nRow1, nRow2);
1370 
1371             // Transfer sorted broadcaster segment to the document.
1372             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1373         }
1374 
1375         {
1376             sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1377             sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1378 
1379             // Do the same as broadcaster storage transfer (to prevent double deletion).
1380             rDest.release_range(nRow1, nRow2);
1381             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1382             aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1383         }
1384 
1385         // Update draw object positions
1386         aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1387 
1388         {
1389             // Get all row spans where the pattern is not NULL.
1390             std::vector<PatternSpan> aSpans =
1391                 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1392                     aSortedCols[i]->maPatterns);
1393 
1394             for (const auto& rSpan : aSpans)
1395             {
1396                 assert(rSpan.mpPattern); // should never be NULL.
1397                 rDocument.GetPool()->Put(*rSpan.mpPattern);
1398             }
1399 
1400             for (const auto& rSpan : aSpans)
1401             {
1402                 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1403                 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1404             }
1405         }
1406 
1407         aCol[nThisCol].CellStorageModified();
1408     }
1409 
1410     if (pArray->IsKeepQuery())
1411     {
1412         aRowFlags.maRowsHidden.build_tree();
1413         aRowFlags.maRowsFiltered.build_tree();
1414 
1415         // Remove all flags in the range first.
1416         SetRowHidden(nRow1, nRow2, false);
1417         SetRowFiltered(nRow1, nRow2, false);
1418 
1419         std::vector<sc::RowSpan> aSpans =
1420             sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1421 
1422         for (const auto& rSpan : aSpans)
1423             SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1424 
1425         aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1426 
1427         for (const auto& rSpan : aSpans)
1428             SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1429     }
1430 
1431     // Set up row reorder map (for later broadcasting of reference updates).
1432     sc::ColRowReorderMapType aRowMap;
1433     const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1434     for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1435     {
1436         SCROW nNew = i + nRow1;
1437         SCROW nOld = rOldIndices[i];
1438         aRowMap.emplace(nOld, nNew);
1439     }
1440 
1441     // Collect all listeners within sorted range ahead of time.
1442     std::vector<SvtListener*> aListeners;
1443 
1444     // Collect listeners of cell broadcasters.
1445     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1446         aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1447 
1448     // Get all area listeners that listen on one row within the range and end
1449     // their listening.
1450     std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1451             aMoveRange, sc::AreaOverlapType::OneRowInside);
1452     {
1453         for (auto& rAreaListener : aAreaListeners)
1454         {
1455             rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1456             aListeners.push_back( rAreaListener.mpListener);
1457         }
1458     }
1459 
1460     {
1461         // Get all formula cells from the former group area listener ranges.
1462 
1463         std::vector<ScFormulaCell*> aFCells;
1464         FormulaCellCollectAction aAction(aFCells);
1465         aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1466 
1467         aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() );
1468     }
1469 
1470     // Remove any duplicate listener entries.  We must ensure that we notify
1471     // each unique listener only once.
1472     std::sort(aListeners.begin(), aListeners.end());
1473     aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1474 
1475     // Collect positions of all shared formula cells outside the sorted range,
1476     // and make them unshared before notifying them.
1477     sc::RefQueryFormulaGroup aFormulaGroupPos;
1478     aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1479 
1480     std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
1481     const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
1482     for (const auto& [rTab, rCols] : rGroupTabs)
1483     {
1484         for (const auto& [nCol, rCol] : rCols)
1485         {
1486             std::vector<SCROW> aBounds(rCol);
1487             rDocument.UnshareFormulaCells(rTab, nCol, aBounds);
1488         }
1489     }
1490 
1491     // Notify the listeners to update their references.
1492     ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
1493     std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1494 
1495     // Re-group formulas in affected columns.
1496     for (const auto& [rTab, rCols] : rGroupTabs)
1497     {
1498         for (const auto& rEntry : rCols)
1499             rDocument.RegroupFormulaCells(rTab, rEntry.first);
1500     }
1501 
1502     // Re-start area listeners on the reordered rows.
1503     for (const auto& rAreaListener : aAreaListeners)
1504     {
1505         ScRange aNewRange = rAreaListener.maArea;
1506         sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
1507         if (itRow != aRowMap.end())
1508         {
1509             aNewRange.aStart.SetRow( itRow->second);
1510             aNewRange.aEnd.SetRow( itRow->second);
1511         }
1512         rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1513     }
1514 
1515     // Re-group columns in the sorted range too.
1516     for (SCCOL i = nCol1; i <= nCol2; ++i)
1517         aCol[i].RegroupFormulaCells();
1518 
1519     {
1520         // Re-start area listeners on the old group listener ranges.
1521         ListenerStartAction aAction(rDocument);
1522         aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1523     }
1524 }
1525 
1526 short ScTable::CompareCell(
1527     sal_uInt16 nSort,
1528     ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1529     ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1530 {
1531     short nRes = 0;
1532 
1533     CellType eType1 = rCell1.meType, eType2 = rCell2.meType;
1534 
1535     if (!rCell1.isEmpty())
1536     {
1537         if (!rCell2.isEmpty())
1538         {
1539             bool bErr1 = false;
1540             bool bStr1 = ( eType1 != CELLTYPE_VALUE );
1541             if (eType1 == CELLTYPE_FORMULA)
1542             {
1543                 if (rCell1.mpFormula->GetErrCode() != FormulaError::NONE)
1544                 {
1545                     bErr1 = true;
1546                     bStr1 = false;
1547                 }
1548                 else if (rCell1.mpFormula->IsValue())
1549                 {
1550                     bStr1 = false;
1551                 }
1552             }
1553 
1554             bool bErr2 = false;
1555             bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1556             if (eType2 == CELLTYPE_FORMULA)
1557             {
1558                 if (rCell2.mpFormula->GetErrCode() != FormulaError::NONE)
1559                 {
1560                     bErr2 = true;
1561                     bStr2 = false;
1562                 }
1563                 else if (rCell2.mpFormula->IsValue())
1564                 {
1565                     bStr2 = false;
1566                 }
1567             }
1568 
1569             if ( bStr1 && bStr2 )           // only compare strings as strings!
1570             {
1571                 OUString aStr1;
1572                 OUString aStr2;
1573                 if (eType1 == CELLTYPE_STRING)
1574                     aStr1 = rCell1.mpString->getString();
1575                 else
1576                     GetString(nCell1Col, nCell1Row, aStr1);
1577                 if (eType2 == CELLTYPE_STRING)
1578                     aStr2 = rCell2.mpString->getString();
1579                 else
1580                     GetString(nCell2Col, nCell2Row, aStr2);
1581 
1582                 bool bUserDef     = aSortParam.bUserDef;        // custom sort order
1583                 bool bNaturalSort = aSortParam.bNaturalSort;    // natural sort
1584                 bool bCaseSens    = aSortParam.bCaseSens;       // case sensitivity
1585 
1586                 ScUserList* pList = ScGlobal::GetUserList();
1587                 if (bUserDef && pList && pList->size() > aSortParam.nUserIndex )
1588                 {
1589                     const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
1590 
1591                     if ( bNaturalSort )
1592                         nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator );
1593                     else
1594                     {
1595                         if ( bCaseSens )
1596                             nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) );
1597                         else
1598                             nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) );
1599                     }
1600 
1601                 }
1602                 if (!bUserDef)
1603                 {
1604                     if ( bNaturalSort )
1605                         nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator );
1606                     else
1607                         nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
1608                 }
1609             }
1610             else if ( bStr1 )               // String <-> Number or Error
1611             {
1612                 if (bErr2)
1613                     nRes = -1;              // String in front of Error
1614                 else
1615                     nRes = 1;               // Number in front of String
1616             }
1617             else if ( bStr2 )               // Number or Error <-> String
1618             {
1619                 if (bErr1)
1620                     nRes = 1;               // String in front of Error
1621                 else
1622                     nRes = -1;              // Number in front of String
1623             }
1624             else if (bErr1 && bErr2)
1625             {
1626                 // nothing, two Errors are equal
1627             }
1628             else if (bErr1)                 // Error <-> Number
1629             {
1630                 nRes = 1;                   // Number in front of Error
1631             }
1632             else if (bErr2)                 // Number <-> Error
1633             {
1634                 nRes = -1;                  // Number in front of Error
1635             }
1636             else                            // Mixed numbers
1637             {
1638                 double nVal1 = rCell1.getValue();
1639                 double nVal2 = rCell2.getValue();
1640                 if (nVal1 < nVal2)
1641                     nRes = -1;
1642                 else if (nVal1 > nVal2)
1643                     nRes = 1;
1644             }
1645             if ( !aSortParam.maKeyState[nSort].bAscending )
1646                 nRes = -nRes;
1647         }
1648         else
1649             nRes = -1;
1650     }
1651     else
1652     {
1653         if (!rCell2.isEmpty())
1654             nRes = 1;
1655         else
1656             nRes = 0;                   // both empty
1657     }
1658     return nRes;
1659 }
1660 
1661 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1662 {
1663     short nRes;
1664     sal_uInt16 nSort = 0;
1665     do
1666     {
1667         ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 );
1668         ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 );
1669         if ( aSortParam.bByRow )
1670             nRes = CompareCell( nSort,
1671                 rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg,
1672                 rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg );
1673         else
1674             nRes = CompareCell( nSort,
1675                 rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField,
1676                 rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField );
1677     } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
1678     if( nRes == 0 )
1679     {
1680         ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 );
1681         ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 );
1682         if( rInfo1.nOrg < rInfo2.nOrg )
1683             nRes = -1;
1684         else if( rInfo1.nOrg > rInfo2.nOrg )
1685             nRes = 1;
1686     }
1687     return nRes;
1688 }
1689 
1690 void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi )
1691 {
1692     if ((nHi - nLo) == 1)
1693     {
1694         if (Compare(pArray, nLo, nHi) > 0)
1695             pArray->Swap( nLo, nHi );
1696     }
1697     else
1698     {
1699         SCCOLROW ni = nLo;
1700         SCCOLROW nj = nHi;
1701         do
1702         {
1703             while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1704                 ni++;
1705             while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1706                 nj--;
1707             if (ni <= nj)
1708             {
1709                 if (ni != nj)
1710                     pArray->Swap( ni, nj );
1711                 ni++;
1712                 nj--;
1713             }
1714         } while (ni < nj);
1715         if ((nj - nLo) < (nHi - ni))
1716         {
1717             if (nLo < nj)
1718                 QuickSort(pArray, nLo, nj);
1719             if (ni < nHi)
1720                 QuickSort(pArray, ni, nHi);
1721         }
1722         else
1723         {
1724             if (ni < nHi)
1725                 QuickSort(pArray, ni, nHi);
1726             if (nLo < nj)
1727                 QuickSort(pArray, nLo, nj);
1728         }
1729     }
1730 }
1731 
1732 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1733 {
1734     short nRes;
1735     sal_uInt16 nSort = 0;
1736     const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
1737     if (aSortParam.bByRow)
1738     {
1739         do
1740         {
1741             SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
1742             ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
1743             ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
1744             nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
1745         } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1746     }
1747     else
1748     {
1749         do
1750         {
1751             SCROW nRow = aSortParam.maKeyState[nSort].nField;
1752             ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow);
1753             ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow);
1754             nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
1755                     nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
1756         } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1757     }
1758     return nRes;
1759 }
1760 
1761 bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const   // over aSortParam
1762 {
1763     for (SCCOLROW i=nStart; i<nEnd; i++)
1764     {
1765         if (Compare( i, i+1 ) > 0)
1766             return false;
1767     }
1768     return true;
1769 }
1770 
1771 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1772 {
1773     SCROW nRow;
1774     int nMax = nRow2 - nRow1;
1775     for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
1776     {
1777         nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
1778         pArray->Swap(i, nRow1 + nRow);
1779     }
1780 }
1781 
1782 void ScTable::Sort(
1783     const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1784     ScProgress* pProgress, sc::ReorderParam* pUndo )
1785 {
1786     InitSortCollator( rSortParam );
1787     bGlobalKeepQuery = bKeepQuery;
1788 
1789     if (pUndo)
1790     {
1791         // Copy over the basic sort parameters.
1792         pUndo->maDataAreaExtras = rSortParam.aDataAreaExtras;
1793         pUndo->mbByRow = rSortParam.bByRow;
1794         pUndo->mbHiddenFiltered = bKeepQuery;
1795         pUndo->mbUpdateRefs = bUpdateRefs;
1796         pUndo->mbHasHeaders = rSortParam.bHasHeader;
1797     }
1798 
1799     // It is assumed that the data area has already been trimmed as necessary.
1800 
1801     aSortParam = rSortParam;    // must be assigned before calling IsSorted()
1802     if (rSortParam.bByRow)
1803     {
1804         const SCROW nLastRow = rSortParam.nRow2;
1805         const SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
1806         if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
1807         {
1808             if(pProgress)
1809                 pProgress->SetState( 0, nLastRow-nRow1 );
1810 
1811             std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1812                         aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
1813 
1814             if ( nLastRow - nRow1 > 255 )
1815                 DecoladeRow(pArray.get(), nRow1, nLastRow);
1816 
1817             QuickSort(pArray.get(), nRow1, nLastRow);
1818             if (pArray->IsUpdateRefs())
1819                 SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1820             else
1821             {
1822                 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress, false);
1823                 if (rSortParam.aDataAreaExtras.anyExtrasWanted())
1824                     SortReorderAreaExtrasByRow( pArray.get(), aSortParam.nCol1, aSortParam.nCol2,
1825                             rSortParam.aDataAreaExtras, pProgress);
1826             }
1827 
1828             if (pUndo)
1829             {
1830                 // Stored is the first data row without header row.
1831                 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1832                 pUndo->maDataAreaExtras.mnStartRow = nRow1;
1833                 pUndo->maOrderIndices = pArray->GetOrderIndices();
1834             }
1835         }
1836     }
1837     else
1838     {
1839         const SCCOL nLastCol = rSortParam.nCol2;
1840         const SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1841         if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1842         {
1843             if(pProgress)
1844                 pProgress->SetState( 0, nLastCol-nCol1 );
1845 
1846             std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1847                         aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
1848 
1849             QuickSort(pArray.get(), nCol1, nLastCol);
1850             SortReorderByColumn(pArray.get(), rSortParam.nRow1, rSortParam.nRow2,
1851                     rSortParam.aDataAreaExtras.mbCellFormats, pProgress);
1852             if (rSortParam.aDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1853                 SortReorderAreaExtrasByColumn( pArray.get(),
1854                         rSortParam.nRow1, rSortParam.nRow2, rSortParam.aDataAreaExtras, pProgress);
1855 
1856             if (pUndo)
1857             {
1858                 // Stored is the first data column without header column.
1859                 pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
1860                 pUndo->maDataAreaExtras.mnStartCol = nCol1;
1861                 pUndo->maOrderIndices = pArray->GetOrderIndices();
1862             }
1863         }
1864     }
1865     DestroySortCollator();
1866 }
1867 
1868 void ScTable::Reorder( const sc::ReorderParam& rParam )
1869 {
1870     if (rParam.maOrderIndices.empty())
1871         return;
1872 
1873     std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1874     if (!pArray)
1875         return;
1876 
1877     if (rParam.mbByRow)
1878     {
1879         // Re-play sorting from the known sort indices.
1880         pArray->ReorderByRow(rParam.maOrderIndices);
1881         if (pArray->IsUpdateRefs())
1882             SortReorderByRowRefUpdate(
1883                 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
1884         else
1885         {
1886             SortReorderByRow( pArray.get(),
1887                     rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr, false);
1888             if (rParam.maDataAreaExtras.anyExtrasWanted())
1889                 SortReorderAreaExtrasByRow( pArray.get(),
1890                         rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(),
1891                         rParam.maDataAreaExtras, nullptr);
1892         }
1893     }
1894     else
1895     {
1896         // Ordering by column is much simpler.  Just set the order indices and we are done.
1897         pArray->SetOrderIndices(rParam.maOrderIndices);
1898         SortReorderByColumn(
1899             pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1900             rParam.maDataAreaExtras.mbCellFormats, nullptr);
1901         if (rParam.maDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1902             SortReorderAreaExtrasByColumn( pArray.get(),
1903                     rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1904                     rParam.maDataAreaExtras, nullptr);
1905     }
1906 }
1907 
1908 namespace {
1909 
1910 class SubTotalRowFinder
1911 {
1912     const ScTable& mrTab;
1913     const ScSubTotalParam& mrParam;
1914 
1915 public:
1916     SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
1917         mrTab(rTab), mrParam(rParam) {}
1918 
1919     bool operator() (size_t nRow, const ScFormulaCell* pCell)
1920     {
1921         if (!pCell->IsSubTotal())
1922             return false;
1923 
1924         SCCOL nStartCol = mrParam.nCol1;
1925         SCCOL nEndCol = mrParam.nCol2;
1926 
1927         for (SCCOL nCol : mrTab.GetColumnsRange(0, nStartCol - 1))
1928         {
1929             if (mrTab.HasData(nCol, nRow))
1930                 return true;
1931         }
1932         for (SCCOL nCol : mrTab.GetColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol()))
1933         {
1934             if (mrTab.HasData(nCol, nRow))
1935                 return true;
1936         }
1937         return false;
1938     }
1939 };
1940 
1941 }
1942 
1943 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
1944 {
1945     SCCOL nStartCol = rParam.nCol1;
1946     SCROW nStartRow = rParam.nRow1 + 1;     // Header
1947     SCCOL nEndCol   = ClampToAllocatedColumns(rParam.nCol2);
1948     SCROW nEndRow    = rParam.nRow2;
1949 
1950     for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1951     {
1952         const sc::CellStoreType& rCells = aCol[nCol].maCells;
1953         SubTotalRowFinder aFunc(*this, rParam);
1954         std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
1955             sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
1956         if (aPos.first != rCells.end())
1957             return true;
1958     }
1959     return false;
1960 }
1961 
1962 namespace {
1963 
1964 struct RemoveSubTotalsHandler
1965 {
1966     std::set<SCROW> aRemoved;
1967 
1968     void operator() (size_t nRow, const ScFormulaCell* p)
1969     {
1970         if (p->IsSubTotal())
1971             aRemoved.insert(nRow);
1972     }
1973 };
1974 
1975 }
1976 
1977 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
1978 {
1979     SCCOL nStartCol = rParam.nCol1;
1980     SCROW nStartRow = rParam.nRow1 + 1;     // Header
1981     SCCOL nEndCol   = ClampToAllocatedColumns(rParam.nCol2);
1982     SCROW nEndRow    = rParam.nRow2;        // will change
1983 
1984     RemoveSubTotalsHandler aFunc;
1985     for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1986     {
1987         const sc::CellStoreType& rCells = aCol[nCol].maCells;
1988         sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
1989     }
1990 
1991     auto& aRows = aFunc.aRemoved;
1992 
1993     std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) {
1994             RemoveRowBreak(nRow+1, false, true);
1995             rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1);
1996         });
1997 
1998     rParam.nRow2 -= aRows.size();
1999 }
2000 
2001 //  Delete hard number formats (for result formulas)
2002 
2003 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
2004 {
2005     const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
2006     if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
2007             == SfxItemState::SET )
2008     {
2009         auto pNewPattern = std::make_unique<ScPatternAttr>( *pPattern );
2010         SfxItemSet& rSet = pNewPattern->GetItemSet();
2011         rSet.ClearItem( ATTR_VALUE_FORMAT );
2012         rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
2013         pTab->SetPattern( nCol, nRow, std::move(pNewPattern) );
2014     }
2015 }
2016 
2017 namespace {
2018 
2019 struct RowEntry
2020 {
2021     sal_uInt16  nGroupNo;
2022     SCROW   nSubStartRow;
2023     SCROW   nDestRow;
2024     SCROW   nFuncStart;
2025     SCROW   nFuncEnd;
2026 };
2027 
2028 }
2029 
2030 static TranslateId lcl_GetSubTotalStrId(int id)
2031 {
2032     switch ( id )
2033     {
2034         case SUBTOTAL_FUNC_AVE:     return STR_FUN_TEXT_AVG;
2035         case SUBTOTAL_FUNC_CNT:
2036         case SUBTOTAL_FUNC_CNT2:    return STR_FUN_TEXT_COUNT;
2037         case SUBTOTAL_FUNC_MAX:     return STR_FUN_TEXT_MAX;
2038         case SUBTOTAL_FUNC_MIN:     return STR_FUN_TEXT_MIN;
2039         case SUBTOTAL_FUNC_PROD:    return STR_FUN_TEXT_PRODUCT;
2040         case SUBTOTAL_FUNC_STD:
2041         case SUBTOTAL_FUNC_STDP:    return STR_FUN_TEXT_STDDEV;
2042         case SUBTOTAL_FUNC_SUM:     return STR_FUN_TEXT_SUM;
2043         case SUBTOTAL_FUNC_VAR:
2044         case SUBTOTAL_FUNC_VARP:    return STR_FUN_TEXT_VAR;
2045         default:
2046         {
2047              return STR_EMPTYDATA;
2048             // added to avoid warnings
2049         }
2050     }
2051 }
2052 
2053 //      new intermediate results
2054 //      rParam.nRow2 is changed!
2055 
2056 bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
2057 {
2058     SCCOL nStartCol = rParam.nCol1;
2059     SCROW nStartRow = rParam.nRow1 + 1;     // Header
2060     SCCOL nEndCol   = rParam.nCol2;
2061     SCROW nEndRow    = rParam.nRow2;        // will change
2062     sal_uInt16 i;
2063 
2064     //  Remove empty rows at the end
2065     //  so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
2066     //  If sorted, all empty rows are at the end.
2067     SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
2068     nEndRow -= nEmpty;
2069 
2070     sal_uInt16 nLevelCount = 0;             // Number of levels
2071     bool bDoThis = true;
2072     for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
2073         if (rParam.bGroupActive[i])
2074             nLevelCount = i+1;
2075         else
2076             bDoThis = false;
2077 
2078     if (nLevelCount==0)                 // do nothing
2079         return true;
2080 
2081     SCCOL*          nGroupCol = rParam.nField;  // columns which will be used when grouping
2082 
2083     //  With (blank) as a separate category, subtotal rows from
2084     //  the other columns must always be tested
2085     //  (previously only when a column occurred more than once)
2086     bool bTestPrevSub = ( nLevelCount > 1 );
2087 
2088     OUString  aSubString;
2089 
2090     bool bIgnoreCase = !rParam.bCaseSens;
2091 
2092     OUString aCompString[MAXSUBTOTAL];
2093 
2094                                 //TODO: sort?
2095 
2096     ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find(
2097                                 ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para ));
2098 
2099     bool bSpaceLeft = true;                                         // Success when inserting?
2100 
2101     // For performance reasons collect formula entries so their
2102     // references don't have to be tested for updates each time a new row is
2103     // inserted
2104     RowEntry aRowEntry;
2105     ::std::vector< RowEntry > aRowVector;
2106 
2107     for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++)
2108     {
2109         aRowEntry.nGroupNo = nLevelCount - nLevel - 1;
2110 
2111         // how many results per level
2112         SCCOL nResCount         = rParam.nSubTotals[aRowEntry.nGroupNo];
2113         // result functions
2114         ScSubTotalFunc* pResFunc = rParam.pFunctions[aRowEntry.nGroupNo].get();
2115 
2116         if (nResCount > 0)                                      // otherwise only sort
2117         {
2118             for (i=0; i<=aRowEntry.nGroupNo; i++)
2119             {
2120                 GetString( nGroupCol[i], nStartRow, aSubString );
2121                 if ( bIgnoreCase )
2122                     aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2123                 else
2124                     aCompString[i] = aSubString;
2125             }                                                   // aSubString stays on the last
2126 
2127             bool bBlockVis = false;             // group visible?
2128             aRowEntry.nSubStartRow = nStartRow;
2129             for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
2130             {
2131                 bool bChanged;
2132                 if (nRow>nEndRow)
2133                     bChanged = true;
2134                 else
2135                 {
2136                     bChanged = false;
2137                     OUString aString;
2138                     for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
2139                     {
2140                         GetString( nGroupCol[i], nRow, aString );
2141                         if (bIgnoreCase)
2142                             aString = ScGlobal::getCharClass().uppercase(aString);
2143                         //  when sorting, blanks are separate group
2144                         //  otherwise blank cells are allowed below
2145                         bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
2146                                         aString != aCompString[i] );
2147                     }
2148                     if ( bChanged && bTestPrevSub )
2149                     {
2150                         // No group change on rows that will contain subtotal formulas
2151                         bChanged = std::none_of(aRowVector.begin(), aRowVector.end(),
2152                             [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; });
2153                     }
2154                 }
2155                 if ( bChanged )
2156                 {
2157                     aRowEntry.nDestRow   = nRow;
2158                     aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
2159                     aRowEntry.nFuncEnd   = nRow-1;
2160 
2161                     bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab,
2162                             aRowEntry.nDestRow, 1 );
2163                     DBShowRow( aRowEntry.nDestRow, bBlockVis );
2164                     if ( rParam.bPagebreak && nRow < rDocument.MaxRow() &&
2165                             aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2166                         SetRowBreak(aRowEntry.nSubStartRow, false, true);
2167 
2168                     if (bSpaceLeft)
2169                     {
2170                         for ( auto& rRowEntry : aRowVector)
2171                         {
2172                             if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow )
2173                                 ++rRowEntry.nSubStartRow;
2174                             if ( aRowEntry.nDestRow <= rRowEntry.nDestRow )
2175                                 ++rRowEntry.nDestRow;
2176                             if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart )
2177                                 ++rRowEntry.nFuncStart;
2178                             if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd )
2179                                 ++rRowEntry.nFuncEnd;
2180                         }
2181                         // collect formula positions
2182                         aRowVector.push_back( aRowEntry );
2183 
2184                         OUString aOutString = aSubString;
2185                         if (aOutString.isEmpty())
2186                             aOutString = ScResId( STR_EMPTYDATA );
2187                         aOutString += " ";
2188                         TranslateId pStrId = STR_TABLE_ERGEBNIS;
2189                         if ( nResCount == 1 )
2190                             pStrId = lcl_GetSubTotalStrId(pResFunc[0]);
2191                         aOutString += ScResId(pStrId);
2192                         SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
2193                         ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle );
2194 
2195                         ++nRow;
2196                         ++nEndRow;
2197                         aRowEntry.nSubStartRow = nRow;
2198                         for (i=0; i<=aRowEntry.nGroupNo; i++)
2199                         {
2200                             GetString( nGroupCol[i], nRow, aSubString );
2201                             if ( bIgnoreCase )
2202                                 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2203                             else
2204                                 aCompString[i] = aSubString;
2205                         }
2206                     }
2207                 }
2208                 bBlockVis = !RowFiltered(nRow);
2209             }
2210         }
2211     }
2212 
2213     if (!aRowVector.empty())
2214     {
2215         // generate global total
2216         SCROW nGlobalStartRow = aRowVector[0].nSubStartRow;
2217         SCROW nGlobalStartFunc = aRowVector[0].nFuncStart;
2218         SCROW nGlobalEndRow = 0;
2219         SCROW nGlobalEndFunc = 0;
2220         for (const auto& rRowEntry : aRowVector)
2221         {
2222             nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow;
2223             nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow;
2224         }
2225 
2226         for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++)
2227         {
2228             const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1;
2229             const ScSubTotalFunc* pResFunc = rParam.pFunctions[nGroupNo].get();
2230             if (!pResFunc)
2231             {
2232                 // No subtotal function given for this group => no formula or
2233                 // label and do not insert a row.
2234                 continue;
2235             }
2236 
2237             // increment end row
2238             nGlobalEndRow++;
2239 
2240             // add row entry for formula
2241             aRowEntry.nGroupNo = nGroupNo;
2242             aRowEntry.nSubStartRow = nGlobalStartRow;
2243             aRowEntry.nFuncStart = nGlobalStartFunc;
2244             aRowEntry.nDestRow = nGlobalEndRow;
2245             aRowEntry.nFuncEnd = nGlobalEndFunc;
2246 
2247             // increment row
2248             nGlobalEndFunc++;
2249 
2250             bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1);
2251 
2252             if (bSpaceLeft)
2253             {
2254                 aRowVector.push_back(aRowEntry);
2255                 nEndRow++;
2256                 DBShowRow(aRowEntry.nDestRow, true);
2257 
2258                 // insert label
2259                 OUString label = ScResId(STR_TABLE_GRAND) + " " + ScResId(lcl_GetSubTotalStrId(pResFunc[0]));
2260                 SetString(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, label);
2261                 ApplyStyle(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle);
2262             }
2263         }
2264     }
2265 
2266     // now insert the formulas
2267     ScComplexRefData aRef;
2268     aRef.InitFlags();
2269     aRef.Ref1.SetAbsTab(nTab);
2270     aRef.Ref2.SetAbsTab(nTab);
2271     for (const auto& rRowEntry : aRowVector)
2272     {
2273         SCCOL nResCount         = rParam.nSubTotals[rRowEntry.nGroupNo];
2274         SCCOL* nResCols         = rParam.pSubTotals[rRowEntry.nGroupNo].get();
2275         ScSubTotalFunc* pResFunc = rParam.pFunctions[rRowEntry.nGroupNo].get();
2276         for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
2277         {
2278             aRef.Ref1.SetAbsCol(nResCols[nResult]);
2279             aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart);
2280             aRef.Ref2.SetAbsCol(nResCols[nResult]);
2281             aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd);
2282 
2283             ScTokenArray aArr(rDocument);
2284             aArr.AddOpCode( ocSubTotal );
2285             aArr.AddOpCode( ocOpen );
2286             aArr.AddDouble( static_cast<double>(pResFunc[nResult]) );
2287             aArr.AddOpCode( ocSep );
2288             aArr.AddDoubleReference( aRef );
2289             aArr.AddOpCode( ocClose );
2290             aArr.AddOpCode( ocStop );
2291             ScFormulaCell* pCell = new ScFormulaCell(
2292                 rDocument, ScAddress(nResCols[nResult], rRowEntry.nDestRow, nTab), aArr);
2293             if ( rParam.bIncludePattern )
2294                 pCell->SetNeedNumberFormat(true);
2295 
2296             SetFormulaCell(nResCols[nResult], rRowEntry.nDestRow, pCell);
2297             if ( nResCols[nResult] != nGroupCol[rRowEntry.nGroupNo] )
2298             {
2299                 ApplyStyle( nResCols[nResult], rRowEntry.nDestRow, pStyle );
2300 
2301                 lcl_RemoveNumberFormat( this, nResCols[nResult], rRowEntry.nDestRow );
2302             }
2303         }
2304 
2305     }
2306 
2307     //TODO: according to setting, shift intermediate-sum rows up?
2308 
2309     //TODO: create Outlines directly?
2310 
2311     if (bSpaceLeft)
2312         DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2313 
2314     rParam.nRow2 = nEndRow;                 // new end
2315     return bSpaceLeft;
2316 }
2317 
2318 namespace {
2319 
2320 class QueryEvaluator
2321 {
2322     ScDocument& mrDoc;
2323     svl::SharedStringPool& mrStrPool;
2324     const ScTable& mrTab;
2325     const ScQueryParam& mrParam;
2326     bool mpTestEqualCondition;
2327     utl::TransliterationWrapper* mpTransliteration;
2328     CollatorWrapper* mpCollator;
2329     const bool mbMatchWholeCell;
2330     const bool mbCaseSensitive;
2331 
2332     static bool isPartialTextMatchOp(const ScQueryEntry& rEntry)
2333     {
2334         switch (rEntry.eOp)
2335         {
2336             // these operators can only be used with textural comparisons.
2337             case SC_CONTAINS:
2338             case SC_DOES_NOT_CONTAIN:
2339             case SC_BEGINS_WITH:
2340             case SC_ENDS_WITH:
2341             case SC_DOES_NOT_BEGIN_WITH:
2342             case SC_DOES_NOT_END_WITH:
2343                 return true;
2344             default:
2345                 ;
2346         }
2347         return false;
2348     }
2349 
2350     static bool isTextMatchOp(const ScQueryEntry& rEntry)
2351     {
2352         if (isPartialTextMatchOp(rEntry))
2353             return true;
2354 
2355         switch (rEntry.eOp)
2356         {
2357             // these operators can be used for either textural or value comparison.
2358             case SC_EQUAL:
2359             case SC_NOT_EQUAL:
2360                 return true;
2361             default:
2362                 ;
2363         }
2364         return false;
2365     }
2366 
2367     bool isRealWildOrRegExp(const ScQueryEntry& rEntry) const
2368     {
2369         if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
2370             return false;
2371 
2372         return isTextMatchOp(rEntry);
2373     }
2374 
2375     bool isTestWildOrRegExp(const ScQueryEntry& rEntry) const
2376     {
2377         if (!mpTestEqualCondition)
2378             return false;
2379 
2380         if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
2381             return false;
2382 
2383         return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
2384     }
2385 
2386     void setupTransliteratorIfNeeded()
2387     {
2388         if (!mpTransliteration)
2389             mpTransliteration = &ScGlobal::GetTransliteration(mrParam.bCaseSens);
2390     }
2391 
2392     void setupCollatorIfNeeded()
2393     {
2394         if (!mpCollator)
2395             mpCollator = &ScGlobal::GetCollator(mrParam.bCaseSens);
2396     }
2397 
2398 public:
2399     QueryEvaluator(ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
2400                    bool pTestEqualCondition) :
2401         mrDoc(rDoc),
2402         mrStrPool(rDoc.GetSharedStringPool()),
2403         mrTab(rTab),
2404         mrParam(rParam),
2405         mpTestEqualCondition(pTestEqualCondition),
2406         mpTransliteration(nullptr),
2407         mpCollator(nullptr),
2408         mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell()),
2409         mbCaseSensitive( rParam.bCaseSens )
2410     {
2411     }
2412 
2413     bool isQueryByValue(
2414         const ScQueryEntry::Item& rItem, SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
2415     {
2416         if (rItem.meType == ScQueryEntry::ByString)
2417             return false;
2418 
2419         if (!rCell.isEmpty())
2420         {
2421             if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2422                 // Error values are compared as string.
2423                 return false;
2424 
2425             return rCell.hasNumeric();
2426         }
2427 
2428         return mrTab.HasValueData(nCol, nRow);
2429     }
2430 
2431     bool isQueryByString(
2432         const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2433         SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell)
2434     {
2435         if (isTextMatchOp(rEntry))
2436             return true;
2437 
2438         if (rItem.meType != ScQueryEntry::ByString)
2439             return false;
2440 
2441         if (!rCell.isEmpty())
2442             return rCell.hasString();
2443 
2444         return mrTab.HasStringData(nCol, nRow);
2445     }
2446 
2447     sal_uInt32 getNumFmt( SCCOL nCol, SCROW nRow, const ScInterpreterContext* pContext )
2448     {
2449         sal_uInt32 nNumFmt = (pContext ?
2450                 mrTab.GetNumberFormat(*pContext, ScAddress(nCol, nRow, mrTab.GetTab())) :
2451                 mrTab.GetNumberFormat(nCol, nRow));
2452         if (nNumFmt && (nNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
2453             // Any General of any locale is irrelevant for rounding.
2454             nNumFmt = 0;
2455         return nNumFmt;
2456     }
2457 
2458     std::pair<bool,bool> compareByValue(
2459         const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2460         const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2461         const ScInterpreterContext* pContext)
2462     {
2463         bool bOk = false;
2464         bool bTestEqual = false;
2465         double nCellVal;
2466         double fQueryVal = rItem.mfVal;
2467         // Defer all number format detection to as late as possible as it's a
2468         // bottle neck, even if that complicates the code. Also do not
2469         // unnecessarily call ScDocument::RoundValueAsShown() for the same
2470         // reason.
2471         sal_uInt32 nNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
2472 
2473         if (!rCell.isEmpty())
2474         {
2475             switch (rCell.meType)
2476             {
2477                 case CELLTYPE_VALUE :
2478                     nCellVal = rCell.mfValue;
2479                 break;
2480                 case CELLTYPE_FORMULA :
2481                     nCellVal = rCell.mpFormula->GetValue();
2482                 break;
2483                 default:
2484                     nCellVal = 0.0;
2485             }
2486             if (rItem.mbRoundForFilter && nCellVal != 0.0)
2487             {
2488                 nNumFmt = getNumFmt( nCol, nRow, pContext);
2489                 if (nNumFmt)
2490                 {
2491                     switch (rCell.meType)
2492                     {
2493                         case CELLTYPE_VALUE :
2494                         case CELLTYPE_FORMULA :
2495                             nCellVal = mrDoc.RoundValueAsShown(nCellVal, nNumFmt, pContext);
2496                         break;
2497                         default:
2498                             assert(!"can't be");
2499                     }
2500                 }
2501             }
2502         }
2503         else
2504             nCellVal = mrTab.GetValue(nCol, nRow);
2505 
2506         /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
2507          * date+time format was queried rEntry.bQueryByDate is not set. In
2508          * case other queries wanted to use this mechanism they should do
2509          * the same, in other words only if rEntry.nVal is an integer value
2510          * rEntry.bQueryByDate should be true and the time fraction be
2511          * stripped here. */
2512 
2513         if (rItem.meType == ScQueryEntry::ByDate)
2514         {
2515             if (nNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND)
2516                 nNumFmt = getNumFmt( nCol, nRow, pContext);
2517             if (nNumFmt)
2518             {
2519                 SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : mrDoc.GetFormatTable();
2520                 const SvNumberformat* pEntry = pFormatter->GetEntry(nNumFmt);
2521                 if (pEntry)
2522                 {
2523                     SvNumFormatType nNumFmtType = pEntry->GetType();
2524                     /* NOTE: Omitting the check for absence of
2525                      * css::util::NumberFormat::TIME would include also date+time formatted
2526                      * values of the same day. That may be desired in some
2527                      * cases, querying all time values of a day, but confusing
2528                      * in other cases. A user can always setup a standard
2529                      * filter query for x >= date AND x < date+1 */
2530                     if ((nNumFmtType & SvNumFormatType::DATE) && !(nNumFmtType & SvNumFormatType::TIME))
2531                     {
2532                         // The format is of date type.  Strip off the time
2533                         // element.
2534                         nCellVal = ::rtl::math::approxFloor(nCellVal);
2535                     }
2536                 }
2537             }
2538         }
2539         else if (rItem.mbRoundForFilter && fQueryVal != 0.0)
2540         {
2541             /* TODO: shouldn't rItem.mfVal (which fQueryVal is) already had
2542              * been stored as rounded in all cases if needed so this extra
2543              * rounding is superfluous? Or rather, if not, then rounding it
2544              * here may produce different roundings for different cell number
2545              * formats, which is odd. This all looks suspicious and the
2546              * intention of tdf#142910 commit
2547              * f6b143a57d9bd8f5d7b29febcb4e01ee1eb2ff1d isn't quite clear. */
2548             if (nNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND)
2549                 nNumFmt = getNumFmt( nCol, nRow, pContext);
2550             if (nNumFmt)
2551                 fQueryVal = mrDoc.RoundValueAsShown(fQueryVal, nNumFmt, pContext);
2552         }
2553 
2554         switch (rEntry.eOp)
2555         {
2556             case SC_EQUAL :
2557                 bOk = ::rtl::math::approxEqual(nCellVal, fQueryVal);
2558                 break;
2559             case SC_LESS :
2560                 bOk = (nCellVal < fQueryVal) && !::rtl::math::approxEqual(nCellVal, fQueryVal);
2561                 break;
2562             case SC_GREATER :
2563                 bOk = (nCellVal > fQueryVal) && !::rtl::math::approxEqual(nCellVal, fQueryVal);
2564                 break;
2565             case SC_LESS_EQUAL :
2566                 bOk = (nCellVal < fQueryVal) || ::rtl::math::approxEqual(nCellVal, fQueryVal);
2567                 if ( bOk && mpTestEqualCondition )
2568                     bTestEqual = ::rtl::math::approxEqual(nCellVal, fQueryVal);
2569                 break;
2570             case SC_GREATER_EQUAL :
2571                 bOk = (nCellVal > fQueryVal) || ::rtl::math::approxEqual( nCellVal, fQueryVal);
2572                 if ( bOk && mpTestEqualCondition )
2573                     bTestEqual = ::rtl::math::approxEqual(nCellVal, fQueryVal);
2574                 break;
2575             case SC_NOT_EQUAL :
2576                 bOk = !::rtl::math::approxEqual(nCellVal, fQueryVal);
2577                 break;
2578             default:
2579             {
2580                 // added to avoid warnings
2581             }
2582         }
2583 
2584         return std::pair<bool,bool>(bOk, bTestEqual);
2585     }
2586 
2587     std::pair<bool,bool> compareByString(
2588         const ScRefCellValue& rCell, SCROW nRow, const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2589         const ScInterpreterContext* pContext)
2590     {
2591         if (!rCell.isEmpty())
2592         {
2593             if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2594             {
2595                 // Error cell is evaluated as string (for now).
2596                 const OUString aCellStr = ScGlobal::GetErrorString(rCell.mpFormula->GetErrCode());
2597                 return compareByStringComparator(rEntry, rItem, nullptr, &aCellStr);
2598             }
2599             else if (rCell.meType == CELLTYPE_STRING)
2600             {
2601                 return compareByStringComparator(rEntry, rItem, rCell.mpString, nullptr);
2602             }
2603             else
2604             {
2605                 sal_uInt32 nFormat = pContext ? mrTab.GetNumberFormat( *pContext, ScAddress(static_cast<SCCOL>(rEntry.nField), nRow, mrTab.GetTab()) ) :
2606                     mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
2607                 OUString aStr;
2608                 SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : mrDoc.GetFormatTable();
2609                 ScCellFormat::GetInputString(rCell, nFormat, aStr, *pFormatter, mrDoc, rEntry.bDoQuery);
2610                 return compareByStringComparator(rEntry, rItem, nullptr, &aStr);
2611             }
2612         }
2613         else
2614         {
2615             OUString aStr;
2616             mrTab.GetInputString(static_cast<SCCOL>(rEntry.nField), nRow, aStr);
2617             return compareByStringComparator(rEntry, rItem, nullptr, &aStr);
2618         }
2619     }
2620 
2621     // Called from compareByString() method, where different sources of strings are checked.
2622     // The value is placed inside one parameter: [pValueSource1] or [pValueSource2] but never in both.
2623     std::pair<bool,bool> compareByStringComparator(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2624         const svl::SharedString* pValueSource1, const OUString * pValueSource2)
2625     {
2626         bool bOk = false;
2627         bool bTestEqual = false;
2628         bool bMatchWholeCell = mbMatchWholeCell;
2629         if (isPartialTextMatchOp(rEntry))
2630             // may have to do partial textural comparison.
2631             bMatchWholeCell = false;
2632 
2633         const bool bRealWildOrRegExp = isRealWildOrRegExp(rEntry);
2634         const bool bTestWildOrRegExp = isTestWildOrRegExp(rEntry);
2635 
2636         // [pValueSource1] or [pValueSource2] but never both of them or none of them
2637         assert((pValueSource1 != nullptr) != (pValueSource2 != nullptr));
2638 
2639         if ( bRealWildOrRegExp || bTestWildOrRegExp )
2640         {
2641             const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2642 
2643             sal_Int32 nStart = 0;
2644             sal_Int32 nEnd   = rValue.getLength();
2645 
2646             // from 614 on, nEnd is behind the found text
2647             bool bMatch = false;
2648             if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
2649             {
2650                 nEnd = 0;
2651                 nStart = rValue.getLength();
2652                 bMatch = rEntry.GetSearchTextPtr( mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell )
2653                     ->SearchBackward(rValue, &nStart, &nEnd);
2654             }
2655             else
2656             {
2657                 bMatch = rEntry.GetSearchTextPtr( mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell )
2658                     ->SearchForward(rValue, &nStart, &nEnd);
2659             }
2660             if ( bMatch && bMatchWholeCell
2661                     && (nStart != 0 || nEnd != rValue.getLength()) )
2662                 bMatch = false;    // RegExp must match entire cell string
2663             if ( bRealWildOrRegExp )
2664             {
2665                 switch (rEntry.eOp)
2666                 {
2667                     case SC_EQUAL:
2668                     case SC_CONTAINS:
2669                         bOk = bMatch;
2670                         break;
2671                     case SC_NOT_EQUAL:
2672                     case SC_DOES_NOT_CONTAIN:
2673                         bOk = !bMatch;
2674                         break;
2675                     case SC_BEGINS_WITH:
2676                         bOk = ( bMatch && (nStart == 0) );
2677                         break;
2678                     case SC_DOES_NOT_BEGIN_WITH:
2679                         bOk = !( bMatch && (nStart == 0) );
2680                         break;
2681                     case SC_ENDS_WITH:
2682                         bOk = ( bMatch && (nEnd == rValue.getLength()) );
2683                         break;
2684                     case SC_DOES_NOT_END_WITH:
2685                         bOk = !( bMatch && (nEnd == rValue.getLength()) );
2686                         break;
2687                     default:
2688                         {
2689                             // added to avoid warnings
2690                         }
2691                 }
2692             }
2693             else
2694                 bTestEqual = bMatch;
2695         }
2696         if ( !bRealWildOrRegExp )
2697         {
2698             // Simple string matching i.e. no regexp match.
2699             if (isTextMatchOp(rEntry))
2700             {
2701                 if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
2702                 {
2703                     // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
2704                     // the query value is assigned directly, and the string is empty. In that case,
2705                     // don't find any string (isEqual would find empty string results in formula cells).
2706                     bOk = false;
2707                     if ( rEntry.eOp == SC_NOT_EQUAL )
2708                         bOk = !bOk;
2709                 }
2710                 else if ( bMatchWholeCell )
2711                 {
2712                     if (pValueSource1)
2713                     {
2714                         // Fast string equality check by comparing string identifiers.
2715                         if (mrParam.bCaseSens)
2716                         {
2717                             bOk = pValueSource1->getData() == rItem.maString.getData();
2718                         }
2719                         else
2720                         {
2721                             bOk = pValueSource1->getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2722                         }
2723                     }
2724                     else // if (pValueSource2)
2725                     {
2726                         if (mrParam.bCaseSens)
2727                         {
2728                             bOk = (*pValueSource2 == rItem.maString.getString());
2729                         }
2730                         else
2731                         {
2732                             // fallback
2733                             const svl::SharedString rSource2(mrStrPool.intern(*pValueSource2));
2734                             // Fast string equality check by comparing string identifiers.
2735                             bOk = rSource2.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2736                         }
2737                     }
2738 
2739                     if ( rEntry.eOp == SC_NOT_EQUAL )
2740                         bOk = !bOk;
2741                 }
2742                 else
2743                 {
2744                     // Where do we find a match (if at all)
2745                     sal_Int32 nStrPos;
2746 
2747                     if (!mbCaseSensitive)
2748                     { // Common case for vlookup etc.
2749                         const svl::SharedString rSource(pValueSource1? *pValueSource1 : mrStrPool.intern(*pValueSource2));
2750 
2751                         const rtl_uString *pQuer = rItem.maString.getDataIgnoreCase();
2752                         const rtl_uString *pCellStr = rSource.getDataIgnoreCase();
2753 
2754                         assert(pQuer != nullptr);
2755                         assert(pCellStr != nullptr);
2756 
2757                         const sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH ||
2758                                             rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2759                             (pCellStr->length - pQuer->length) : 0;
2760 
2761                         if (nIndex < 0)
2762                             nStrPos = -1;
2763                         else if (rEntry.eOp == SC_EQUAL ||
2764                                  rEntry.eOp == SC_NOT_EQUAL)
2765                         {
2766                             nStrPos = pCellStr == pQuer ? 0 : -1;
2767                         }
2768                         else
2769                         { // OUString::indexOf
2770                             nStrPos = rtl_ustr_indexOfStr_WithLength(
2771                                 pCellStr->buffer + nIndex, pCellStr->length - nIndex,
2772                                 pQuer->buffer, pQuer->length );
2773 
2774                             if (nStrPos >= 0)
2775                                 nStrPos += nIndex;
2776                         }
2777                     }
2778                     else
2779                     {
2780                         const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2781                         const OUString aQueryStr = rItem.maString.getString();
2782                         const LanguageType nLang = ScGlobal::oSysLocale->GetLanguageTag().getLanguageType();
2783                         setupTransliteratorIfNeeded();
2784                         const OUString aCell( mpTransliteration->transliterate(
2785                                             rValue, nLang, 0, rValue.getLength(),
2786                                             nullptr ) );
2787 
2788                         const OUString aQuer( mpTransliteration->transliterate(
2789                                             aQueryStr, nLang, 0, aQueryStr.getLength(),
2790                                             nullptr ) );
2791 
2792                         const sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2793                             (aCell.getLength() - aQuer.getLength()) : 0;
2794                         nStrPos = ((nIndex < 0) ? -1 : aCell.indexOf( aQuer, nIndex ));
2795                     }
2796                     switch (rEntry.eOp)
2797                     {
2798                     case SC_EQUAL:
2799                         bOk = ( nStrPos == 0 );
2800                         break;
2801                     case SC_CONTAINS:
2802                         bOk = ( nStrPos != -1 );
2803                         break;
2804                     case SC_NOT_EQUAL:
2805                         bOk = ( nStrPos != 0 );
2806                         break;
2807                     case SC_DOES_NOT_CONTAIN:
2808                         bOk = ( nStrPos == -1 );
2809                         break;
2810                     case SC_BEGINS_WITH:
2811                         bOk = ( nStrPos == 0 );
2812                         break;
2813                     case SC_DOES_NOT_BEGIN_WITH:
2814                         bOk = ( nStrPos != 0 );
2815                         break;
2816                     case SC_ENDS_WITH:
2817                         bOk = ( nStrPos >= 0 );
2818                         break;
2819                     case SC_DOES_NOT_END_WITH:
2820                         bOk = ( nStrPos < 0 );
2821                         break;
2822                     default:
2823                         {
2824                             // added to avoid warnings
2825                         }
2826                     }
2827                 }
2828             }
2829             else
2830             {   // use collator here because data was probably sorted
2831                 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2832                 setupCollatorIfNeeded();
2833                 sal_Int32 nCompare = mpCollator->compareString(
2834                     rValue, rItem.maString.getString());
2835                 switch (rEntry.eOp)
2836                 {
2837                     case SC_LESS :
2838                         bOk = (nCompare < 0);
2839                         break;
2840                     case SC_GREATER :
2841                         bOk = (nCompare > 0);
2842                         break;
2843                     case SC_LESS_EQUAL :
2844                         bOk = (nCompare <= 0);
2845                         if ( bOk && mpTestEqualCondition && !bTestEqual )
2846                             bTestEqual = (nCompare == 0);
2847                         break;
2848                     case SC_GREATER_EQUAL :
2849                         bOk = (nCompare >= 0);
2850                         if ( bOk && mpTestEqualCondition && !bTestEqual )
2851                             bTestEqual = (nCompare == 0);
2852                         break;
2853                     default:
2854                     {
2855                         // added to avoid warnings
2856                     }
2857                 }
2858             }
2859         }
2860 
2861         return std::pair<bool,bool>(bOk, bTestEqual);
2862     }
2863 
2864     std::pair<bool, bool> compareByTextColor(SCCOL nCol, SCROW nRow, SCTAB nTab,
2865                                              const ScQueryEntry::Item& rItem)
2866     {
2867         ScAddress aPos(nCol, nRow, nTab);
2868         Color color;
2869         bool bHasConditionalColor = false;
2870         // Text color can be set via conditional formatting - check that first
2871         const ScPatternAttr* pPattern = mrDoc.GetPattern(nCol, nRow, nTab);
2872         if (pPattern)
2873         {
2874             if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
2875             {
2876                 const SfxItemSet* pCondSet
2877                     = mrDoc.GetCondResult(nCol, nRow, nTab);
2878                 const SvxColorItem* pColor = &pPattern->GetItem(ATTR_FONT_COLOR, pCondSet);
2879                 color = pColor->GetValue();
2880                 bHasConditionalColor = true;
2881             }
2882         }
2883 
2884         if (!bHasConditionalColor)
2885         {
2886             const SvxColorItem* pColor = mrDoc.GetAttr(aPos, ATTR_FONT_COLOR);
2887             color = pColor->GetValue();
2888         }
2889 
2890         bool bMatch = rItem.maColor == color;
2891         return std::pair<bool, bool>(bMatch, false);
2892     }
2893 
2894     std::pair<bool, bool> compareByBackgroundColor(SCCOL nCol, SCROW nRow, SCTAB nTab,
2895                                                    const ScQueryEntry::Item& rItem)
2896     {
2897         ScAddress aPos(nCol, nRow, nTab);
2898         Color color;
2899 
2900         // Background color can be set via conditional formatting - check that first
2901         bool bHasConditionalColor = false;
2902         const ScPatternAttr* pPattern = mrDoc.GetPattern(nCol, nRow, nTab);
2903         if (pPattern)
2904         {
2905             if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
2906             {
2907                 const SfxItemSet* pCondSet
2908                     = mrDoc.GetCondResult(nCol, nRow, nTab);
2909                 const SvxBrushItem* pBackgroundColor = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
2910                 color = pBackgroundColor->GetColor();
2911                 bHasConditionalColor = true;
2912             }
2913         }
2914 
2915         ScConditionalFormat* pCondFormat = mrDoc.GetCondFormat(nCol, nRow, nTab);
2916         if (pCondFormat)
2917         {
2918             for (size_t i = 0; i < pCondFormat->size(); i++)
2919             {
2920                 auto aEntry = pCondFormat->GetEntry(i);
2921                 if (aEntry->GetType() == ScFormatEntry::Type::Colorscale)
2922                 {
2923                     const ScColorScaleFormat* pColFormat
2924                         = static_cast<const ScColorScaleFormat*>(aEntry);
2925                     color = *(pColFormat->GetColor(aPos));
2926                     bHasConditionalColor = true;
2927                 }
2928             }
2929         }
2930 
2931         if (!bHasConditionalColor)
2932         {
2933             const SvxBrushItem* pBrush = mrDoc.GetAttr(aPos, ATTR_BACKGROUND);
2934             color = pBrush->GetColor();
2935         }
2936 
2937         bool bMatch = rItem.maColor == color;
2938         return std::pair<bool, bool>(bMatch, false);
2939     }
2940 
2941     // To be called only if both isQueryByValue() and isQueryByString()
2942     // returned false and range lookup is wanted! In range lookup comparison
2943     // numbers are less than strings. Nothing else is compared.
2944     std::pair<bool,bool> compareByRangeLookup(
2945         const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2946         const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
2947     {
2948         bool bTestEqual = false;
2949 
2950         if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS && rEntry.eOp != SC_LESS_EQUAL)
2951             return std::pair<bool,bool>(false, bTestEqual);
2952 
2953         if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER && rEntry.eOp != SC_GREATER_EQUAL)
2954             return std::pair<bool,bool>(false, bTestEqual);
2955 
2956         if (!rCell.isEmpty())
2957         {
2958             if (rItem.meType == ScQueryEntry::ByString)
2959             {
2960                 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2961                     // Error values are compared as string.
2962                     return std::pair<bool,bool>(false, bTestEqual);
2963 
2964                 return std::pair<bool,bool>(rCell.hasNumeric(), bTestEqual);
2965             }
2966 
2967             return std::pair<bool,bool>(!rCell.hasNumeric(), bTestEqual);
2968         }
2969 
2970         if (rItem.meType == ScQueryEntry::ByString)
2971             return std::pair<bool,bool>(mrTab.HasValueData(nCol, nRow), bTestEqual);
2972 
2973         return std::pair<bool,bool>(!mrTab.HasValueData(nCol, nRow), bTestEqual);
2974     }
2975 };
2976 
2977 }
2978 
2979 bool ScTable::ValidQuery(
2980     SCROW nRow, const ScQueryParam& rParam, const ScRefCellValue* pCell, bool* pbTestEqualCondition,
2981     const ScInterpreterContext* pContext, sc::TableColumnBlockPositionSet* pBlockPos)
2982 {
2983     if (!rParam.GetEntry(0).bDoQuery)
2984         return true;
2985 
2986     //---------------------------------------------------------------
2987 
2988     const SCSIZE nFixedBools = 32;
2989     bool aBool[nFixedBools];
2990     bool aTest[nFixedBools];
2991     SCSIZE nEntryCount = rParam.GetEntryCount();
2992     bool* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new bool[nEntryCount] );
2993     bool* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new bool[nEntryCount] );
2994 
2995     tools::Long    nPos = -1;
2996     QueryEvaluator aEval(rDocument, *this, rParam, pbTestEqualCondition != nullptr);
2997     ScQueryParam::const_iterator it, itBeg = rParam.begin(), itEnd = rParam.end();
2998     for (it = itBeg; it != itEnd && (*it)->bDoQuery; ++it)
2999     {
3000         const ScQueryEntry& rEntry = **it;
3001         SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
3002 
3003         // We can only handle one single direct query passed as a known pCell,
3004         // subsequent queries have to obtain the cell.
3005         ScRefCellValue aCell;
3006         if(pCell && it == itBeg)
3007             aCell = *pCell;
3008         else if( pBlockPos )
3009         {   // hinted mdds access
3010             ScColumn* column = FetchColumn(nCol);
3011             aCell = column->GetCellValue(*pBlockPos->getBlockPosition( nCol ), nRow);
3012         }
3013         else
3014             aCell = GetCellValue(nCol, nRow);
3015 
3016         std::pair<bool,bool> aRes(false, false);
3017 
3018         const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
3019         if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
3020         {
3021             bool hasData;
3022             if( pBlockPos )
3023             {
3024                 ScColumn* column = FetchColumn(rEntry.nField);
3025                 hasData = column->HasDataAt(*pBlockPos->getBlockPosition(rEntry.nField), nRow);
3026             }
3027             else
3028                 hasData = aCol[rEntry.nField].HasDataAt(nRow);
3029             if (rEntry.IsQueryByEmpty())
3030                 aRes.first = !hasData;
3031             else
3032             {
3033                 assert(rEntry.IsQueryByNonEmpty());
3034                 aRes.first = hasData;
3035             }
3036         }
3037         else
3038         {
3039             for (const auto& rItem : rItems)
3040             {
3041                 if (rItem.meType == ScQueryEntry::ByTextColor)
3042                 {
3043                     std::pair<bool, bool> aThisRes
3044                         = aEval.compareByTextColor(nCol, nRow, nTab, rItem);
3045                     aRes.first |= aThisRes.first;
3046                     aRes.second |= aThisRes.second;
3047                 }
3048                 else if (rItem.meType == ScQueryEntry::ByBackgroundColor)
3049                 {
3050                     std::pair<bool,bool> aThisRes =
3051                         aEval.compareByBackgroundColor(nCol, nRow, nTab, rItem);
3052                     aRes.first |= aThisRes.first;
3053                     aRes.second |= aThisRes.second;
3054                 }
3055                 else if (aEval.isQueryByValue(rItem, nCol, nRow, aCell))
3056                 {
3057                     std::pair<bool,bool> aThisRes =
3058                         aEval.compareByValue(aCell, nCol, nRow, rEntry, rItem, pContext);
3059                     aRes.first |= aThisRes.first;
3060                     aRes.second |= aThisRes.second;
3061                 }
3062                 else if (aEval.isQueryByString(rEntry, rItem, nCol, nRow, aCell))
3063                 {
3064                     std::pair<bool,bool> aThisRes =
3065                         aEval.compareByString(aCell, nRow, rEntry, rItem, pContext);
3066                     aRes.first |= aThisRes.first;
3067                     aRes.second |= aThisRes.second;
3068                 }
3069                 else if (rParam.mbRangeLookup)
3070                 {
3071                     std::pair<bool,bool> aThisRes =
3072                         aEval.compareByRangeLookup(aCell, nCol, nRow, rEntry, rItem);
3073                     aRes.first |= aThisRes.first;
3074                     aRes.second |= aThisRes.second;
3075                 }
3076 
3077                 if (aRes.first && aRes.second)
3078                     break;
3079             }
3080         }
3081 
3082         if (nPos == -1)
3083         {
3084             nPos++;
3085             pPasst[nPos] = aRes.first;
3086             pTest[nPos] = aRes.second;
3087         }
3088         else
3089         {
3090             if (rEntry.eConnect == SC_AND)
3091             {
3092                 pPasst[nPos] = pPasst[nPos] && aRes.first;
3093                 pTest[nPos] = pTest[nPos] && aRes.second;
3094             }
3095             else
3096             {
3097                 nPos++;
3098                 pPasst[nPos] = aRes.first;
3099                 pTest[nPos] = aRes.second;
3100             }
3101         }
3102     }
3103 
3104     for ( tools::Long j=1; j <= nPos; j++ )
3105     {
3106         pPasst[0] = pPasst[0] || pPasst[j];
3107         pTest[0] = pTest[0] || pTest[j];
3108     }
3109 
3110     bool bRet = pPasst[0];
3111     if ( pPasst != &aBool[0] )
3112         delete [] pPasst;
3113     if ( pbTestEqualCondition )
3114         *pbTestEqualCondition = pTest[0];
3115     if ( pTest != &aTest[0] )
3116         delete [] pTest;
3117 
3118     return bRet;
3119 }
3120 
3121 void ScTable::TopTenQuery( ScQueryParam& rParam )
3122 {
3123     bool bSortCollatorInitialized = false;
3124     SCSIZE nEntryCount = rParam.GetEntryCount();
3125     SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
3126     SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
3127     for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
3128     {
3129         ScQueryEntry& rEntry = rParam.GetEntry(i);
3130         ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
3131 
3132         for (ScQueryEntry::Item& rItem : rItems)
3133         {
3134             switch (rEntry.eOp)
3135             {
3136                 case SC_TOPVAL:
3137                 case SC_BOTVAL:
3138                 case SC_TOPPERC:
3139                 case SC_BOTPERC:
3140                 {
3141                     ScSortParam aLocalSortParam(rParam, static_cast<SCCOL>(rEntry.nField));
3142                     aSortParam = aLocalSortParam;       // used in CreateSortInfoArray, Compare
3143                     if (!bSortCollatorInitialized)
3144                     {
3145                         bSortCollatorInitialized = true;
3146                         InitSortCollator(aLocalSortParam);
3147                     }
3148                     std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
3149                     DecoladeRow(pArray.get(), nRow1, rParam.nRow2);
3150                     QuickSort(pArray.get(), nRow1, rParam.nRow2);
3151                     std::unique_ptr<ScSortInfo[]> const& ppInfo = pArray->GetFirstArray();
3152                     SCSIZE nValidCount = nCount;
3153                     // Don't count note or blank cells, they are sorted to the end
3154                     while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.isEmpty())
3155                         nValidCount--;
3156                     // Don't count Strings, they are between Value and blank
3157                     while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.hasString())
3158                         nValidCount--;
3159                     if (nValidCount > 0)
3160                     {
3161                         if (rItem.meType == ScQueryEntry::ByString)
3162                         {   // by string ain't going to work
3163                             rItem.meType = ScQueryEntry::ByValue;
3164                             rItem.mfVal = 10;   // 10 and 10% respectively
3165                         }
3166                         SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
3167                         SCSIZE nOffset = 0;
3168                         switch (rEntry.eOp)
3169                         {
3170                         case SC_TOPVAL:
3171                         {
3172                             rEntry.eOp = SC_GREATER_EQUAL;
3173                             if (nVal > nValidCount)
3174                                 nVal = nValidCount;
3175                             nOffset = nValidCount - nVal;   // 1 <= nVal <= nValidCount
3176                         }
3177                         break;
3178                         case SC_BOTVAL:
3179                         {
3180                             rEntry.eOp = SC_LESS_EQUAL;
3181                             if (nVal > nValidCount)
3182                                 nVal = nValidCount;
3183                             nOffset = nVal - 1;     // 1 <= nVal <= nValidCount
3184                         }
3185                         break;
3186                         case SC_TOPPERC:
3187                         {
3188                             rEntry.eOp = SC_GREATER_EQUAL;
3189                             if (nVal > 100)
3190                                 nVal = 100;
3191                             nOffset = nValidCount - (nValidCount * nVal / 100);
3192                             if (nOffset >= nValidCount)
3193                                 nOffset = nValidCount - 1;
3194                         }
3195                         break;
3196                         case SC_BOTPERC:
3197                         {
3198                             rEntry.eOp = SC_LESS_EQUAL;
3199                             if (nVal > 100)
3200                                 nVal = 100;
3201                             nOffset = (nValidCount * nVal / 100);
3202                             if (nOffset >= nValidCount)
3203                                 nOffset = nValidCount - 1;
3204                         }
3205                         break;
3206                         default:
3207                         {
3208                             // added to avoid warnings
3209                         }
3210                         }
3211                         ScRefCellValue aCell = ppInfo[nOffset].maCell;
3212                         if (aCell.hasNumeric())
3213                             rItem.mfVal = aCell.getValue();
3214                         else
3215                         {
3216                             OSL_FAIL("TopTenQuery: pCell no ValueData");
3217                             rEntry.eOp = SC_GREATER_EQUAL;
3218                             rItem.mfVal = 0;
3219                         }
3220                     }
3221                     else
3222                     {
3223                         rEntry.eOp = SC_GREATER_EQUAL;
3224                         rItem.meType = ScQueryEntry::ByValue;
3225                         rItem.mfVal = 0;
3226                     }
3227                 }
3228                 break;
3229                 default:
3230                 {
3231                     // added to avoid warnings
3232                 }
3233             }
3234         }
3235     }
3236     if ( bSortCollatorInitialized )
3237         DestroySortCollator();
3238 }
3239 
3240 namespace {
3241 
3242 bool CanOptimizeQueryStringToNumber( const SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat )
3243 {
3244     // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
3245     // The problem with this optimization is that the autofilter dialog apparently converts
3246     // the value to text and then converts that back to a number for filtering.
3247     // If that leads to any change of value (such as when time is rounded to seconds),
3248     // even matching values will be filtered out. Therefore query by value only for formats
3249     // where no such change should occur.
3250     if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex))
3251     {
3252         switch(pEntry->GetType())
3253         {
3254         case SvNumFormatType::NUMBER:
3255         case SvNumFormatType::FRACTION:
3256         case SvNumFormatType::SCIENTIFIC:
3257             return true;
3258         case SvNumFormatType::DATE:
3259         case SvNumFormatType::DATETIME:
3260             bDateFormat = true;
3261             break;
3262         default:
3263             break;
3264         }
3265     }
3266     return false;
3267 }
3268 
3269 class PrepareQueryItem
3270 {
3271     const ScDocument& mrDoc;
3272     const bool mbRoundForFilter;
3273 public:
3274     explicit PrepareQueryItem(const ScDocument& rDoc, bool bRoundForFilter) :
3275         mrDoc(rDoc), mbRoundForFilter(bRoundForFilter) {}
3276 
3277     void operator() (ScQueryEntry::Item& rItem)
3278     {
3279         rItem.mbRoundForFilter = mbRoundForFilter;
3280 
3281         if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
3282             return;
3283 
3284         sal_uInt32 nIndex = 0;
3285         bool bNumber = mrDoc.GetFormatTable()->
3286             IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
3287 
3288         // Advanced Filter creates only ByString queries that need to be
3289         // converted to ByValue if appropriate. rItem.mfVal now holds the value
3290         // if bNumber==true.
3291 
3292         if (rItem.meType == ScQueryEntry::ByString)
3293         {
3294             bool bDateFormat = false;
3295             if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat ))
3296                 rItem.meType = ScQueryEntry::ByValue;
3297             if (!bDateFormat)
3298                 return;
3299         }
3300 
3301         // Double-check if the query by date is really appropriate.
3302 
3303         if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
3304         {
3305             const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
3306             if (pEntry)
3307             {
3308                 SvNumFormatType nNumFmtType = pEntry->GetType();
3309                 if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME))
3310                     rItem.meType = ScQueryEntry::ByValue;    // not a date only
3311                 else
3312                     rItem.meType = ScQueryEntry::ByDate;    // date only
3313             }
3314             else
3315                 rItem.meType = ScQueryEntry::ByValue;    // what the ... not a date
3316         }
3317         else
3318             rItem.meType = ScQueryEntry::ByValue;    // not a date
3319     }
3320 };
3321 
3322 void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, bool bRoundForFilter )
3323 {
3324     bool bTopTen = false;
3325     SCSIZE nEntryCount = rParam.GetEntryCount();
3326 
3327     for ( SCSIZE i = 0; i < nEntryCount; ++i )
3328     {
3329         ScQueryEntry& rEntry = rParam.GetEntry(i);
3330         if (!rEntry.bDoQuery)
3331             continue;
3332 
3333         ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
3334         std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc, bRoundForFilter));
3335 
3336         if ( !bTopTen )
3337         {
3338             switch ( rEntry.eOp )
3339             {
3340                 case SC_TOPVAL:
3341                 case SC_BOTVAL:
3342                 case SC_TOPPERC:
3343                 case SC_BOTPERC:
3344                 {
3345                     bTopTen = true;
3346                 }
3347                 break;
3348                 default:
3349                 {
3350                 }
3351             }
3352         }
3353     }
3354 
3355     if ( bTopTen )
3356     {
3357         pTab->TopTenQuery( rParam );
3358     }
3359 }
3360 
3361 }
3362 
3363 void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
3364 {
3365     lcl_PrepareQuery(&rDocument, this, rQueryParam, false);
3366 }
3367 
3368 SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
3369 {
3370     ScQueryParam    aParam( rParamOrg );
3371     typedef std::unordered_set<OUString> StrSetType;
3372     StrSetType aStrSet;
3373 
3374     bool    bStarted = false;
3375     bool    bOldResult = true;
3376     SCROW   nOldStart = 0;
3377     SCROW   nOldEnd = 0;
3378 
3379     SCSIZE nCount   = 0;
3380     SCROW nOutRow   = 0;
3381     SCROW nHeader   = aParam.bHasHeader ? 1 : 0;
3382 
3383     lcl_PrepareQuery(&rDocument, this, aParam, true);
3384 
3385     if (!aParam.bInplace)
3386     {
3387         nOutRow = aParam.nDestRow + nHeader;
3388         if (nHeader > 0)
3389             CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
3390                             aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
3391     }
3392 
3393     sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access
3394 
3395     SCROW nRealRow2 = aParam.nRow2;
3396     for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
3397     {
3398         bool bResult;                                   // Filter result
3399         bool bValid = ValidQuery(j, aParam, nullptr, nullptr, nullptr, &blockPos);
3400         if (!bValid && bKeepSub)                        // Keep subtotals
3401         {
3402             for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
3403             {
3404                 ScRefCellValue aCell = GetCellValue(nCol, j);
3405                 if (aCell.meType != CELLTYPE_FORMULA)
3406                     continue;
3407 
3408                 if (!aCell.mpFormula->IsSubTotal())
3409                     continue;
3410 
3411                 if (RefVisible(aCell.mpFormula))
3412                     bValid = true;
3413             }
3414         }
3415         if (bValid)
3416         {
3417             if (aParam.bDuplicate)
3418                 bResult = true;
3419             else
3420             {
3421                 OUStringBuffer aStr;
3422                 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
3423                 {
3424                     OUString aCellStr;
3425                     GetString(k, j, aCellStr);
3426                     aStr.append(aCellStr + u"\x0001");
3427                 }
3428 
3429                 bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted.
3430             }
3431         }
3432         else
3433             bResult = false;
3434 
3435         if (aParam.bInplace)
3436         {
3437             if (bResult == bOldResult && bStarted)
3438                 nOldEnd = j;
3439             else
3440             {
3441                 if (bStarted)
3442                     DBShowRows(nOldStart,nOldEnd, bOldResult);
3443                 nOldStart = nOldEnd = j;
3444                 bOldResult = bResult;
3445             }
3446             bStarted = true;
3447         }
3448         else
3449         {
3450             if (bResult)
3451             {
3452                 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
3453                 if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints
3454                     blockPos.invalidate();
3455                 ++nOutRow;
3456             }
3457         }
3458         if (bResult)
3459             ++nCount;
3460     }
3461 
3462     if (aParam.bInplace && bStarted)
3463         DBShowRows(nOldStart,nOldEnd, bOldResult);
3464 
3465     if (aParam.bInplace)
3466         SetDrawPageSize();
3467 
3468     return nCount;
3469 }
3470 
3471 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3472 {
3473     bool    bValid = true;
3474     std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]);
3475     OUString  aCellStr;
3476     SCCOL   nCol = nCol1;
3477     OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3478     SCTAB   nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3479     SCROW   nDBRow1 = rQueryParam.nRow1;
3480     SCCOL   nDBCol2 = rQueryParam.nCol2;
3481     // First row must be column headers
3482     while (bValid && (nCol <= nCol2))
3483     {
3484         OUString aQueryStr;
3485         GetUpperCellString(nCol, nRow1, aQueryStr);
3486         bool bFound = false;
3487         SCCOL i = rQueryParam.nCol1;
3488         while (!bFound && (i <= nDBCol2))
3489         {
3490             if ( nTab == nDBTab )
3491                 GetUpperCellString(i, nDBRow1, aCellStr);
3492             else
3493                 rDocument.GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
3494             bFound = (aCellStr == aQueryStr);
3495             if (!bFound) i++;
3496         }
3497         if (bFound)
3498             pFields[nCol - nCol1] = i;
3499         else
3500             bValid = false;
3501         nCol++;
3502     }
3503     if (bValid)
3504     {
3505         sal_uLong nVisible = 0;
3506         for ( nCol=nCol1; nCol<=nCol2; nCol++ )
3507             nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
3508 
3509         if ( nVisible > SCSIZE_MAX / sizeof(void*) )
3510         {
3511             OSL_FAIL("too many filter criteria");
3512             nVisible = 0;
3513         }
3514 
3515         SCSIZE nNewEntries = nVisible;
3516         rQueryParam.Resize( nNewEntries );
3517 
3518         SCSIZE nIndex = 0;
3519         SCROW nRow = nRow1 + 1;
3520         svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
3521         while (nRow <= nRow2)
3522         {
3523             nCol = nCol1;
3524             while (nCol <= nCol2)
3525             {
3526                 GetInputString( nCol, nRow, aCellStr );
3527                 if (!aCellStr.isEmpty())
3528                 {
3529                     if (nIndex < nNewEntries)
3530                     {
3531                         rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
3532                         rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
3533                         nIndex++;
3534                         if (nIndex < nNewEntries)
3535                             rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
3536                     }
3537                     else
3538                         bValid = false;
3539                 }
3540                 nCol++;
3541             }
3542             nRow++;
3543             if (nIndex < nNewEntries)
3544                 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
3545         }
3546     }
3547     return bValid;
3548 }
3549 
3550 bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3551 {
3552     // A valid StarQuery must be at least 4 columns wide. To be precise it
3553     // should be exactly 4 columns ...
3554     // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
3555     // column Excel style query range immediately left to itself would result
3556     // in a circular reference when the field name or operator or value (first
3557     // to third query range column) is obtained (#i58354#). Furthermore, if the
3558     // range wasn't sufficiently specified data changes wouldn't flag formula
3559     // cells for recalculation.
3560     if (nCol2 - nCol1 < 3)
3561         return false;
3562 
3563     bool bValid;
3564     OUString aCellStr;
3565     SCSIZE nIndex = 0;
3566     SCROW nRow = nRow1;
3567     OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3568     SCTAB   nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3569     SCROW   nDBRow1 = rQueryParam.nRow1;
3570     SCCOL   nDBCol2 = rQueryParam.nCol2;
3571 
3572     SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
3573     rQueryParam.Resize( nNewEntries );
3574     svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
3575 
3576     do
3577     {
3578         ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
3579 
3580         bValid = false;
3581         // First column AND/OR
3582         if (nIndex > 0)
3583         {
3584             GetUpperCellString(nCol1, nRow, aCellStr);
3585             if ( aCellStr == ScResId(STR_TABLE_AND) )
3586             {
3587                 rEntry.eConnect = SC_AND;
3588                 bValid = true;
3589             }
3590             else if ( aCellStr == ScResId(STR_TABLE_OR) )
3591             {
3592                 rEntry.eConnect = SC_OR;
3593                 bValid = true;
3594             }
3595         }
3596         // Second column field name
3597         if ((nIndex < 1) || bValid)
3598         {
3599             bool bFound = false;
3600             GetUpperCellString(nCol1 + 1, nRow, aCellStr);
3601             for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
3602             {
3603                 OUString aFieldStr;
3604                 if ( nTab == nDBTab )
3605                     GetUpperCellString(i, nDBRow1, aFieldStr);
3606                 else
3607                     rDocument.GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
3608                 bFound = (aCellStr == aFieldStr);
3609                 if (bFound)
3610                 {
3611                     rEntry.nField = i;
3612                     bValid = true;
3613                 }
3614                 else
3615                     bValid = false;
3616             }
3617         }
3618         // Third column operator =<>...
3619         if (bValid)
3620         {
3621             GetUpperCellString(nCol1 + 2, nRow, aCellStr);
3622             if (aCellStr.startsWith("<"))
3623             {
3624                 if (aCellStr[1] == '>')
3625                     rEntry.eOp = SC_NOT_EQUAL;
3626                 else if (aCellStr[1] == '=')
3627                     rEntry.eOp = SC_LESS_EQUAL;
3628                 else
3629                     rEntry.eOp = SC_LESS;
3630             }
3631             else if (aCellStr.startsWith(">"))
3632             {
3633                 if (aCellStr[1] == '=')
3634                     rEntry.eOp = SC_GREATER_EQUAL;
3635                 else
3636                     rEntry.eOp = SC_GREATER;
3637             }
3638             else if (aCellStr.startsWith("="))
3639                 rEntry.eOp = SC_EQUAL;
3640 
3641         }
3642         // Fourth column values
3643         if (bValid)
3644         {
3645             OUString aStr;
3646             GetString(nCol1 + 3, nRow, aStr);
3647             rEntry.GetQueryItem().maString = rPool.intern(aStr);
3648             rEntry.bDoQuery = true;
3649         }
3650         nIndex++;
3651         nRow++;
3652     }
3653     while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
3654     return bValid;
3655 }
3656 
3657 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3658 {
3659     SCSIZE i, nCount;
3660     PutInOrder(nCol1, nCol2);
3661     PutInOrder(nRow1, nRow2);
3662 
3663     nCount = rQueryParam.GetEntryCount();
3664     for (i=0; i < nCount; i++)
3665         rQueryParam.GetEntry(i).Clear();
3666 
3667     // Standard query table
3668     bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3669     // Excel Query table
3670     if (!bValid)
3671         bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3672 
3673     SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
3674     nCount = rQueryParam.GetEntryCount();
3675     if (bValid)
3676     {
3677         //  query type must be set
3678         for (i=0; i < nCount; i++)
3679         {
3680             ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem();
3681             sal_uInt32 nIndex = 0;
3682             bool bNumber = pFormatter->IsNumberFormat(
3683                 rItem.maString.getString(), nIndex, rItem.mfVal);
3684             bool bDateFormat = false;
3685             rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat )
3686                 ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString);
3687         }
3688     }
3689     else
3690     {
3691         for (i=0; i < nCount; i++)
3692             rQueryParam.GetEntry(i).Clear();
3693     }
3694     return bValid;
3695 }
3696 
3697 bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
3698 {
3699     if (nStartRow == nEndRow)
3700         // Assume only data.
3701         /* XXX NOTE: previous behavior still checked this one row and could
3702          * evaluate it has header row, but that doesn't make much sense. */
3703         return false;
3704 
3705     if (nStartCol == nEndCol)
3706     {
3707         CellType eFirstCellType = GetCellType(nStartCol, nStartRow);
3708         CellType eSecondCellType = GetCellType(nStartCol, nStartRow+1);
3709         return ((eFirstCellType == CELLTYPE_STRING || eFirstCellType == CELLTYPE_EDIT) &&
3710                 (eSecondCellType != CELLTYPE_STRING && eSecondCellType != CELLTYPE_EDIT));
3711     }
3712 
3713     for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3714     {
3715         CellType eType = GetCellType( nCol, nStartRow );
3716         // Any non-text cell in first row => not headers.
3717         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3718             return false;
3719     }
3720 
3721     // First row all text cells, any non-text cell in second row => headers.
3722     SCROW nTestRow = nStartRow + 1;
3723     for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3724     {
3725         CellType eType = GetCellType( nCol, nTestRow );
3726         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3727             return true;
3728     }
3729 
3730     // Also second row all text cells => first row not headers.
3731     return false;
3732 }
3733 
3734 bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
3735 {
3736     if (nStartCol == nEndCol)
3737         // Assume only data.
3738         /* XXX NOTE: previous behavior still checked this one column and could
3739          * evaluate it has header column, but that doesn't make much sense. */
3740         return false;
3741 
3742     if (nStartRow == nEndRow)
3743     {
3744         CellType eFirstCellType = GetCellType(nStartCol, nStartRow);
3745         CellType eSecondCellType = GetCellType(nStartCol+1, nStartRow);
3746         return ((eFirstCellType == CELLTYPE_STRING || eFirstCellType == CELLTYPE_EDIT) &&
3747                 (eSecondCellType != CELLTYPE_STRING && eSecondCellType != CELLTYPE_EDIT));
3748     }
3749 
3750     for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3751     {
3752         CellType eType = GetCellType( nStartCol, nRow );
3753         // Any non-text cell in first column => not headers.
3754         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3755             return false;
3756     }
3757 
3758     // First column all text cells, any non-text cell in second column => headers.
3759     SCCOL nTestCol = nStartCol + 1;
3760     for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3761     {
3762         CellType eType = GetCellType( nRow, nTestCol );
3763         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3764             return true;
3765     }
3766 
3767     // Also second column all text cells => first column not headers.
3768     return false;
3769 }
3770 
3771 void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering )
3772 {
3773     if (nCol >= aCol.size())
3774         return;
3775 
3776     sc::ColumnBlockConstPosition aBlockPos;
3777     aCol[nCol].InitBlockPosition(aBlockPos);
3778     aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, bFiltering);
3779 }
3780 
3781 void ScTable::GetFilteredFilterEntries(
3782     SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering )
3783 {
3784     if (nCol >= aCol.size())
3785         return;
3786 
3787     sc::ColumnBlockConstPosition aBlockPos;
3788     aCol[nCol].InitBlockPosition(aBlockPos);
3789 
3790     // remove the entry for this column from the query parameter
3791     ScQueryParam aParam( rParam );
3792     aParam.RemoveEntryByField(nCol);
3793 
3794     lcl_PrepareQuery(&rDocument, this, aParam, true);
3795     for ( SCROW j = nRow1; j <= nRow2; ++j )
3796     {
3797         if (ValidQuery(j, aParam))
3798         {
3799             aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering);
3800         }
3801     }
3802 }
3803 
3804 bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings)
3805 {
3806     return aCol[nCol].GetDataEntries( nRow, rStrings);
3807 }
3808 
3809 sal_uLong ScTable::GetCellCount() const
3810 {
3811     sal_uLong nCellCount = 0;
3812 
3813     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3814         nCellCount += aCol[nCol].GetCellCount();
3815 
3816     return nCellCount;
3817 }
3818 
3819 sal_uLong ScTable::GetWeightedCount() const
3820 {
3821     sal_uLong nCellCount = 0;
3822 
3823     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3824         nCellCount += aCol[nCol].GetWeightedCount();
3825 
3826     return nCellCount;
3827 }
3828 
3829 sal_uLong ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
3830 {
3831     sal_uLong nCellCount = 0;
3832 
3833     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3834         nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow);
3835 
3836     return nCellCount;
3837 }
3838 
3839 sal_uLong ScTable::GetCodeCount() const
3840 {
3841     sal_uLong nCodeCount = 0;
3842 
3843     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3844         if ( aCol[nCol].GetCellCount() )
3845             nCodeCount += aCol[nCol].GetCodeCount();
3846 
3847     return nCodeCount;
3848 }
3849 
3850 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
3851         SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
3852 {
3853     if ( IsColValid( nCol ) )
3854         return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
3855     else
3856         return 0;
3857 }
3858 
3859 sal_Int32 ScTable::GetMaxNumberStringLen(
3860     sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
3861 {
3862     if ( IsColValid( nCol ) )
3863         return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
3864     else
3865         return 0;
3866 }
3867 
3868 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
3869 {
3870     ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab );
3871     ScRange aMarkArea( ScAddress::UNINITIALIZED );
3872     if (rMark.IsMultiMarked())
3873         rMark.GetMultiMarkArea( aMarkArea );
3874     else if (rMark.IsMarked())
3875         rMark.GetMarkArea( aMarkArea );
3876     else
3877     {
3878         assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
3879         aMarkArea.aStart.SetCol(0);
3880         aMarkArea.aEnd.SetCol(rDocument.MaxCol());
3881     }
3882     const SCCOL nStartCol = aMarkArea.aStart.Col();
3883     const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col());
3884     for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol)
3885     {
3886         if (mpColFlags && ColHidden(nCol))
3887             continue;
3888 
3889         aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3890     }
3891 }
3892 
3893 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3894