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