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