avoid repeated checks with same result on fast lookup path
[LibreOffice.git] / sc / source / core / data / table3.cxx
blob877aeede34d8693b8102d6cd8ca9502ae7d06df4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/processfactory.hxx>
21 #include <comphelper/random.hxx>
22 #include <editeng/brushitem.hxx>
23 #include <editeng/colritem.hxx>
24 #include <unotools/textsearch.hxx>
25 #include <svl/numformat.hxx>
26 #include <svl/zforlist.hxx>
27 #include <svl/zformat.hxx>
28 #include <unotools/charclass.hxx>
29 #include <unotools/collatorwrapper.hxx>
30 #include <stdlib.h>
31 #include <unotools/transliterationwrapper.hxx>
32 #include <com/sun/star/i18n/KParseTokens.hpp>
33 #include <com/sun/star/i18n/KParseType.hpp>
34 #include <sal/log.hxx>
35 #include <osl/diagnose.h>
37 #include <refdata.hxx>
38 #include <table.hxx>
39 #include <scitems.hxx>
40 #include <formulacell.hxx>
41 #include <document.hxx>
42 #include <globstr.hrc>
43 #include <scresid.hxx>
44 #include <global.hxx>
45 #include <stlpool.hxx>
46 #include <patattr.hxx>
47 #include <subtotal.hxx>
48 #include <docoptio.hxx>
49 #include <markdata.hxx>
50 #include <rangelst.hxx>
51 #include <userlist.hxx>
52 #include <progress.hxx>
53 #include <cellform.hxx>
54 #include <queryparam.hxx>
55 #include <queryentry.hxx>
56 #include <subtotalparam.hxx>
57 #include <docpool.hxx>
58 #include <cellvalue.hxx>
59 #include <tokenarray.hxx>
60 #include <mtvcellfunc.hxx>
61 #include <columnspanset.hxx>
62 #include <fstalgorithm.hxx>
63 #include <listenercontext.hxx>
64 #include <sharedformula.hxx>
65 #include <stlsheet.hxx>
66 #include <refhint.hxx>
67 #include <listenerquery.hxx>
68 #include <bcaslot.hxx>
69 #include <reordermap.hxx>
70 #include <drwlayer.hxx>
71 #include <conditio.hxx>
72 #include <colorscale.hxx>
74 #include <svl/sharedstringpool.hxx>
76 #include <memory>
77 #include <set>
78 #include <unordered_set>
79 #include <vector>
80 #include <mdds/flat_segment_tree.hpp>
82 using namespace ::com::sun::star;
84 namespace naturalsort {
86 using namespace ::com::sun::star::i18n;
88 /** Splits a given string into three parts: the prefix, number string, and
89 the suffix.
91 @param sWhole
92 Original string to be split into pieces
94 @param sPrefix
95 Prefix string that consists of the part before the first number token.
96 If no number was found, sPrefix is unchanged.
98 @param sSuffix
99 String after the last number token. This may still contain number strings.
100 If no number was found, sSuffix is unchanged.
102 @param fNum
103 Number converted from the middle number string
104 If no number was found, fNum is unchanged.
106 @return Returns TRUE if a numeral element is found in a given string, or
107 FALSE if no numeral element is found.
109 static bool SplitString( const OUString &sWhole,
110 OUString &sPrefix, OUString &sSuffix, double &fNum )
112 // Get prefix element, search for any digit and stop.
113 sal_Int32 nPos = 0;
114 while (nPos < sWhole.getLength())
116 const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos);
117 if (nType & KCharacterType::DIGIT)
118 break;
119 sWhole.iterateCodePoints( &nPos );
122 // Return FALSE if no numeral element is found
123 if ( nPos == sWhole.getLength() )
124 return false;
126 // Get numeral element
127 const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep();
128 ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken(
129 KParseType::ANY_NUMBER, sWhole, nPos,
130 KParseTokens::ANY_NUMBER, "", KParseTokens::ANY_NUMBER, sUser );
132 if ( aPRNum.EndPos == nPos )
134 SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
135 nPos << " : " << sWhole);
136 return false;
139 sPrefix = sWhole.copy( 0, nPos );
140 fNum = aPRNum.Value;
141 sSuffix = sWhole.copy( aPRNum.EndPos );
143 return true;
146 /** Naturally compares two given strings.
148 This is the main function that should be called externally. It returns
149 either 1, 0, or -1 depending on the comparison result of given two strings.
151 @param sInput1
152 Input string 1
154 @param sInput2
155 Input string 2
157 @param bCaseSens
158 Boolean value for case sensitivity
160 @param pData
161 Pointer to user defined sort list
163 @param pCW
164 Pointer to collator wrapper for normal string comparison
166 @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
167 sInput2 is greater.
169 static short Compare( const OUString &sInput1, const OUString &sInput2,
170 const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
172 OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
176 double nNum1, nNum2;
177 bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
178 bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
180 short nPreRes; // Prefix comparison result
181 if ( pData )
183 if ( bCaseSens )
185 if ( !bNumFound1 || !bNumFound2 )
186 return static_cast<short>(pData->Compare( sStr1, sStr2 ));
187 else
188 nPreRes = pData->Compare( sPre1, sPre2 );
190 else
192 if ( !bNumFound1 || !bNumFound2 )
193 return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
194 else
195 nPreRes = pData->ICompare( sPre1, sPre2 );
198 else
200 if ( !bNumFound1 || !bNumFound2 )
201 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
202 else
203 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
206 // Prefix strings differ. Return immediately.
207 if ( nPreRes != 0 ) return nPreRes;
209 if ( nNum1 != nNum2 )
211 if ( nNum1 < nNum2 ) return -1;
212 return (nNum1 > nNum2) ? 1 : 0;
215 // The prefix and the first numerical elements are equal, but the suffix
216 // strings may still differ. Stay in the loop.
218 sStr1 = sSuf1;
219 sStr2 = sSuf2;
221 } while (true);
223 return 0;
228 namespace {
230 struct ScSortInfo final
232 ScRefCellValue maCell;
233 SCCOLROW nOrg;
238 class ScSortInfoArray
240 public:
242 struct Cell
244 ScRefCellValue maCell;
245 const sc::CellTextAttr* mpAttr;
246 const ScPostIt* mpNote;
247 std::vector<SdrObject*> maDrawObjects;
248 const ScPatternAttr* mpPattern;
250 Cell() : mpAttr(nullptr), mpNote(nullptr), mpPattern(nullptr) {}
253 struct Row
255 std::vector<Cell> maCells;
257 bool mbHidden:1;
258 bool mbFiltered:1;
260 explicit Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
263 typedef std::vector<Row> RowsType;
265 private:
266 std::unique_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
268 std::vector<std::unique_ptr<ScSortInfo[]>> mvppInfo;
269 SCCOLROW nStart;
270 SCCOLROW mnLastIndex; /// index of last non-empty cell position.
272 std::vector<SCCOLROW> maOrderIndices;
273 bool mbKeepQuery;
274 bool mbUpdateRefs;
276 public:
277 ScSortInfoArray(const ScSortInfoArray&) = delete;
278 const ScSortInfoArray& operator=(const ScSortInfoArray&) = delete;
280 ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
281 mvppInfo(nSorts),
282 nStart( nInd1 ),
283 mnLastIndex(nInd2),
284 mbKeepQuery(false),
285 mbUpdateRefs(false)
287 SCSIZE nCount( nInd2 - nInd1 + 1 );
288 if (nSorts)
290 for ( sal_uInt16 nSort = 0; nSort < nSorts; nSort++ )
292 mvppInfo[nSort].reset(new ScSortInfo[nCount]);
296 for (size_t i = 0; i < nCount; ++i)
297 maOrderIndices.push_back(i+nStart);
300 void SetKeepQuery( bool b ) { mbKeepQuery = b; }
302 bool IsKeepQuery() const { return mbKeepQuery; }
304 void SetUpdateRefs( bool b ) { mbUpdateRefs = b; }
306 bool IsUpdateRefs() const { return mbUpdateRefs; }
309 * Call this only during normal sorting, not from reordering.
311 std::unique_ptr<ScSortInfo[]> const & GetFirstArray() const
313 return mvppInfo[0];
317 * Call this only during normal sorting, not from reordering.
319 ScSortInfo & Get( sal_uInt16 nSort, SCCOLROW nInd )
321 return mvppInfo[nSort][ nInd - nStart ];
325 * Call this only during normal sorting, not from reordering.
327 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
329 if (nInd1 == nInd2) // avoid self-move-assign
330 return;
331 SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
332 SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
333 for ( sal_uInt16 nSort = 0; nSort < static_cast<sal_uInt16>(mvppInfo.size()); nSort++ )
335 auto & ppInfo = mvppInfo[nSort];
336 std::swap(ppInfo[n1], ppInfo[n2]);
339 std::swap(maOrderIndices[n1], maOrderIndices[n2]);
341 if (mpRows)
343 // Swap rows in data table.
344 RowsType& rRows = *mpRows;
345 std::swap(rRows[n1], rRows[n2]);
349 void SetOrderIndices( std::vector<SCCOLROW>&& rIndices )
351 maOrderIndices = std::move(rIndices);
355 * @param rIndices indices are actual row positions on the sheet, not an
356 * offset from the top row.
358 void ReorderByRow( const std::vector<SCCOLROW>& rIndices )
360 if (!mpRows)
361 return;
363 RowsType& rRows = *mpRows;
365 std::vector<SCCOLROW> aOrderIndices2;
366 aOrderIndices2.reserve(rIndices.size());
368 RowsType aRows2;
369 aRows2.reserve(rRows.size());
371 for (const auto& rIndex : rIndices)
373 size_t nPos = rIndex - nStart; // switch to an offset to top row.
374 aRows2.push_back(rRows[nPos]);
375 aOrderIndices2.push_back(maOrderIndices[nPos]);
378 rRows.swap(aRows2);
379 maOrderIndices.swap(aOrderIndices2);
382 sal_uInt16 GetUsedSorts() const { return mvppInfo.size(); }
384 SCCOLROW GetStart() const { return nStart; }
385 SCCOLROW GetLast() const { return mnLastIndex; }
387 const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; }
389 RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
391 mpRows.reset(new RowsType);
392 mpRows->resize(nRowSize, Row(nColSize));
393 return *mpRows;
396 RowsType* GetDataRows()
398 return mpRows.get();
402 // Assume that we can handle 512MB, which with a ~100 bytes
403 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
404 // overhead in one chunk.
405 constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell);
407 namespace {
409 void initDataRows(
410 ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols,
411 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
412 bool bHiddenFiltered, bool bPattern, bool bCellNotes, bool bCellDrawObjects, bool bOnlyDataAreaExtras )
414 // Fill row-wise data table.
415 ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
417 const std::vector<SCCOLROW>& rOrderIndices = rArray.GetOrderIndices();
418 assert(!bOnlyDataAreaExtras || (rOrderIndices.size() == static_cast<size_t>(nRow2 - nRow1 + 1)
419 && nRow1 == rArray.GetStart()));
421 ScDrawLayer* pDrawLayer = (bCellDrawObjects ? rTab.GetDoc().GetDrawLayer() : nullptr);
422 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
424 ScColumn& rCol = rCols[nCol];
426 // Skip reordering of cell formats if the whole span is on the same pattern entry.
427 bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
429 sc::ColumnBlockConstPosition aBlockPos;
430 rCol.InitBlockPosition(aBlockPos);
431 std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects;
432 if (pDrawLayer)
433 aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2);
435 for (SCROW nR = nRow1; nR <= nRow2; ++nR)
437 const SCROW nRow = (bOnlyDataAreaExtras ? rOrderIndices[nR - rArray.GetStart()] : nR);
438 ScSortInfoArray::Row& rRow = rRows[nR-nRow1];
439 ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
440 if (!bOnlyDataAreaExtras)
442 rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
443 rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
445 if (bCellNotes)
446 rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
447 if (pDrawLayer)
448 rCell.maDrawObjects = aRowDrawObjects[nRow];
450 if (!bUniformPattern && bPattern)
451 rCell.mpPattern = rCol.GetPattern(nRow);
455 if (!bOnlyDataAreaExtras && bHiddenFiltered)
457 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
459 ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
460 rRow.mbHidden = rTab.RowHidden(nRow);
461 rRow.mbFiltered = rTab.RowFiltered(nRow);
468 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
470 std::unique_ptr<ScSortInfoArray> pArray;
472 if (rParam.mbByRow)
474 // Create a sort info array with just the data table.
475 SCROW nRow1 = rParam.maSortRange.aStart.Row();
476 SCROW nRow2 = rParam.maSortRange.aEnd.Row();
477 SCCOL nCol1 = rParam.maSortRange.aStart.Col();
478 SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
480 pArray.reset(new ScSortInfoArray(0, nRow1, nRow2));
481 pArray->SetKeepQuery(rParam.mbHiddenFiltered);
482 pArray->SetUpdateRefs(rParam.mbUpdateRefs);
484 initDataRows( *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2, rParam.mbHiddenFiltered,
485 rParam.maDataAreaExtras.mbCellFormats, true, true, false);
487 else
489 SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
490 SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
492 pArray.reset(new ScSortInfoArray(0, nCol1, nCol2));
493 pArray->SetKeepQuery(rParam.mbHiddenFiltered);
494 pArray->SetUpdateRefs(rParam.mbUpdateRefs);
497 return pArray;
500 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray(
501 const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
502 bool bKeepQuery, bool bUpdateRefs )
504 sal_uInt16 nUsedSorts = 1;
505 while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
506 nUsedSorts++;
507 std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ));
508 pArray->SetKeepQuery(bKeepQuery);
509 pArray->SetUpdateRefs(bUpdateRefs);
511 if ( rSortParam.bByRow )
513 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
515 SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
516 ScColumn* pCol = &aCol[nCol];
517 sc::ColumnBlockConstPosition aBlockPos;
518 pCol->InitBlockPosition(aBlockPos);
519 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
521 ScSortInfo & rInfo = pArray->Get( nSort, nRow );
522 rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow);
523 rInfo.nOrg = nRow;
527 initDataRows( *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2, bKeepQuery,
528 rSortParam.aDataAreaExtras.mbCellFormats, true, true, false);
530 else
532 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
534 SCROW nRow = rSortParam.maKeyState[nSort].nField;
535 for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
536 nCol <= static_cast<SCCOL>(nInd2); nCol++ )
538 ScSortInfo & rInfo = pArray->Get( nSort, nCol );
539 rInfo.maCell = GetCellValue(nCol, nRow);
540 rInfo.nOrg = nCol;
544 return pArray;
547 namespace {
549 struct SortedColumn
551 typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
553 sc::CellStoreType maCells;
554 sc::CellTextAttrStoreType maCellTextAttrs;
555 sc::BroadcasterStoreType maBroadcasters;
556 sc::CellNoteStoreType maCellNotes;
557 std::vector<std::vector<SdrObject*>> maCellDrawObjects;
559 PatRangeType maPatterns;
560 PatRangeType::const_iterator miPatternPos;
562 SortedColumn(const SortedColumn&) = delete;
563 const SortedColumn operator=(const SortedColumn&) = delete;
565 explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) :
566 maCells(nTopEmptyRows),
567 maCellTextAttrs(nTopEmptyRows),
568 maBroadcasters(nTopEmptyRows),
569 maCellNotes(nTopEmptyRows),
570 maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr),
571 miPatternPos(maPatterns.begin()) {}
573 void setPattern( SCROW nRow, const ScPatternAttr* pPat )
575 miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
579 struct SortedRowFlags
581 typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
583 FlagsType maRowsHidden;
584 FlagsType maRowsFiltered;
585 FlagsType::const_iterator miPosHidden;
586 FlagsType::const_iterator miPosFiltered;
588 SortedRowFlags(const ScSheetLimits& rSheetLimits) :
589 maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false),
590 maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false),
591 miPosHidden(maRowsHidden.begin()),
592 miPosFiltered(maRowsFiltered.begin()) {}
594 void setRowHidden( SCROW nRow, bool b )
596 miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
599 void setRowFiltered( SCROW nRow, bool b )
601 miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
604 void swap( SortedRowFlags& r )
606 maRowsHidden.swap(r.maRowsHidden);
607 maRowsFiltered.swap(r.maRowsFiltered);
609 // Just reset the position hints.
610 miPosHidden = maRowsHidden.begin();
611 miPosFiltered = maRowsFiltered.begin();
615 struct PatternSpan
617 SCROW mnRow1;
618 SCROW mnRow2;
619 const ScPatternAttr* mpPattern;
621 PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
622 mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
627 bool ScTable::IsSortCollatorGlobal() const
629 return pSortCollator == &ScGlobal::GetCollator() ||
630 pSortCollator == &ScGlobal::GetCaseCollator();
633 void ScTable::InitSortCollator( const ScSortParam& rPar )
635 if ( !rPar.aCollatorLocale.Language.isEmpty() )
637 if ( !pSortCollator || IsSortCollatorGlobal() )
638 pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
639 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
640 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
642 else
643 { // SYSTEM
644 DestroySortCollator();
645 pSortCollator = &ScGlobal::GetCollator(rPar.bCaseSens);
649 void ScTable::DestroySortCollator()
651 if ( pSortCollator )
653 if ( !IsSortCollatorGlobal() )
654 delete pSortCollator;
655 pSortCollator = nullptr;
659 namespace {
661 template<typename Hint, typename ReorderMap, typename Index>
662 class ReorderNotifier
664 Hint maHint;
665 public:
666 ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) :
667 maHint(rMap, nTab, nPos1, nPos2) {}
669 void operator() ( SvtListener* p )
671 p->Notify(maHint);
675 class FormulaGroupPosCollector
677 sc::RefQueryFormulaGroup& mrQuery;
679 public:
680 explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
682 void operator() ( const SvtListener* p )
684 p->Query(mrQuery);
688 void fillSortedColumnArray(
689 std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
690 SortedRowFlags& rRowFlags,
691 std::vector<SvtListener*>& rCellListeners,
692 ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable,
693 bool bOnlyDataAreaExtras )
695 assert(!bOnlyDataAreaExtras || !pArray->IsUpdateRefs());
697 SCROW nRow1 = pArray->GetStart();
698 ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
699 std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices();
701 size_t nColCount = nCol2 - nCol1 + 1;
702 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
703 SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
704 aSortedCols.reserve(nColCount);
705 for (size_t i = 0; i < nColCount; ++i)
707 // In the sorted column container, element positions and row
708 // positions must match, else formula cells may mis-behave during
709 // grouping.
710 aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
713 for (size_t i = 0; i < pRows->size(); ++i)
715 const SCROW nRow = nRow1 + i;
717 ScSortInfoArray::Row& rRow = (*pRows)[i];
718 for (size_t j = 0; j < rRow.maCells.size(); ++j)
720 ScSortInfoArray::Cell& rCell = rRow.maCells[j];
722 // If bOnlyDataAreaExtras,
723 // sc::CellStoreType aSortedCols.at(j)->maCells
724 // and
725 // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs
726 // are by definition all empty mdds::multi_type_vector, so nothing
727 // needs to be done to push *all* empty.
729 if (!bOnlyDataAreaExtras)
731 sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells;
732 switch (rCell.maCell.meType)
734 case CELLTYPE_STRING:
735 assert(rCell.mpAttr);
736 rCellStore.push_back(*rCell.maCell.mpString);
737 break;
738 case CELLTYPE_VALUE:
739 assert(rCell.mpAttr);
740 rCellStore.push_back(rCell.maCell.mfValue);
741 break;
742 case CELLTYPE_EDIT:
743 assert(rCell.mpAttr);
744 rCellStore.push_back(rCell.maCell.mpEditText->Clone().release());
745 break;
746 case CELLTYPE_FORMULA:
748 assert(rCell.mpAttr);
749 ScAddress aOldPos = rCell.maCell.mpFormula->aPos;
751 const ScAddress aCellPos(nCol1 + j, nRow, nTab);
752 ScFormulaCell* pNew = rCell.maCell.mpFormula->Clone( aCellPos );
753 if (pArray->IsUpdateRefs())
755 pNew->CopyAllBroadcasters(*rCell.maCell.mpFormula);
756 pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
758 else
760 pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
763 if (!rCellListeners.empty())
765 // Original source cells will be deleted during
766 // sc::CellStoreType::transfer(), SvtListener is a base
767 // class, so we need to replace it.
768 auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.mpFormula));
769 if (it != rCellListeners.end())
770 *it = pNew;
773 rCellStore.push_back(pNew);
775 break;
776 default:
777 //assert(!rCell.mpAttr);
778 // This assert doesn't hold, for example
779 // CopyCellsFromClipHandler may omit copying cells during
780 // PasteSpecial for which CopyTextAttrsFromClipHandler
781 // still copies a CellTextAttr. So if that really is not
782 // expected then fix it there.
783 rCellStore.push_back_empty();
786 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs;
787 if (rCell.mpAttr)
788 rAttrStore.push_back(*rCell.mpAttr);
789 else
790 rAttrStore.push_back_empty();
793 if (pArray->IsUpdateRefs())
795 // At this point each broadcaster instance is managed by 2
796 // containers. We will release those in the original storage
797 // below before transferring them to the document.
798 const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
799 sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters;
800 if (pBroadcaster)
801 // A const pointer would be implicitly converted to a bool type.
802 rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster));
803 else
804 rBCStore.push_back_empty();
807 // The same with cell note instances ...
808 sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes;
809 if (rCell.mpNote)
810 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
811 else
812 rNoteStore.push_back_empty();
814 // Add cell anchored images
815 aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects);
817 if (rCell.mpPattern)
818 aSortedCols.at(j)->setPattern(nRow, rCell.mpPattern);
821 if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
823 // Hidden and filtered flags are first converted to segments.
824 aRowFlags.setRowHidden(nRow, rRow.mbHidden);
825 aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
828 if (pProgress)
829 pProgress->SetStateOnPercent(i);
832 rSortedCols.swap(aSortedCols);
833 rRowFlags.swap(aRowFlags);
836 void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
838 if (nTop < rRange.aStart.Row())
839 rRange.aStart.SetRow(nTop);
841 if (rRange.aEnd.Row() < nBottom)
842 rRange.aEnd.SetRow(nBottom);
845 class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
847 std::vector<ScFormulaCell*>& mrCells;
848 ScColumn* mpCol;
850 public:
851 explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
852 mrCells(rCells), mpCol(nullptr) {}
854 virtual void startColumn( ScColumn* pCol ) override
856 mpCol = pCol;
859 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
861 assert(mpCol);
863 if (!bVal)
864 return;
866 mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
870 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
872 ScColumn* mpCol;
874 std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
875 sc::StartListeningContext maStartCxt;
876 sc::EndListeningContext maEndCxt;
878 public:
879 explicit ListenerStartAction( ScDocument& rDoc ) :
880 mpCol(nullptr),
881 mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)),
882 maStartCxt(rDoc, mpPosSet),
883 maEndCxt(rDoc, mpPosSet) {}
885 virtual void startColumn( ScColumn* pCol ) override
887 mpCol = pCol;
890 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
892 assert(mpCol);
894 if (!bVal)
895 return;
897 mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
903 void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray* pArray,
904 SCCOL nDataCol1, SCCOL nDataCol2,
905 const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
907 const SCROW nRow1 = pArray->GetStart();
908 const SCROW nLastRow = pArray->GetLast();
909 const SCCOL nChunkCols = std::max<SCCOL>( 1, kSortCellsChunk / (nLastRow - nRow1 + 1));
910 // Before data area.
911 for (SCCOL nCol = rDataAreaExtras.mnStartCol; nCol < nDataCol1; nCol += nChunkCols)
913 const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, nDataCol1 - 1);
914 initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
915 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
916 SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
918 // Behind data area.
919 for (SCCOL nCol = nDataCol2 + 1; nCol <= rDataAreaExtras.mnEndCol; nCol += nChunkCols)
921 const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, rDataAreaExtras.mnEndCol);
922 initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
923 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
924 SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
928 void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray* pArray,
929 SCROW nDataRow1, SCROW nDataRow2, const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
931 const SCCOL nCol1 = static_cast<SCCOL>(pArray->GetStart());
932 const SCCOL nLastCol = static_cast<SCCOL>(pArray->GetLast());
933 const SCROW nChunkRows = std::max<SCROW>( 1, kSortCellsChunk / (nLastCol - nCol1 + 1));
934 // Above data area.
935 for (SCROW nRow = rDataAreaExtras.mnStartRow; nRow < nDataRow1; nRow += nChunkRows)
937 const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, nDataRow1 - 1);
938 SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
940 // Below data area.
941 for (SCROW nRow = nDataRow2 + 1; nRow <= rDataAreaExtras.mnEndRow; nRow += nChunkRows)
943 const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, rDataAreaExtras.mnEndRow);
944 SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
948 void ScTable::SortReorderByColumn(
949 const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
951 SCCOLROW nStart = pArray->GetStart();
952 SCCOLROW nLast = pArray->GetLast();
954 std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
955 size_t nCount = aIndices.size();
957 // Cut formula grouping at row and reference boundaries before the reordering.
958 ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
959 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
960 aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
962 // Collect all listeners of cell broadcasters of sorted range.
963 std::vector<SvtListener*> aCellListeners;
965 if (!pArray->IsUpdateRefs())
967 // Collect listeners of cell broadcasters.
968 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
969 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
971 // Remove any duplicate listener entries. We must ensure that we
972 // notify each unique listener only once.
973 std::sort(aCellListeners.begin(), aCellListeners.end());
974 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
976 // Notify the cells' listeners to stop listening.
977 /* TODO: for performance this could be enhanced to stop and later
978 * restart only listening to within the reordered range and keep
979 * listening to everything outside untouched. */
980 sc::RefStopListeningHint aHint;
981 for (auto const & l : aCellListeners)
982 l->Notify(aHint);
985 // table to keep track of column index to position in the index table.
986 std::vector<SCCOLROW> aPosTable(nCount);
987 for (size_t i = 0; i < nCount; ++i)
988 aPosTable[aIndices[i]-nStart] = i;
990 SCCOLROW nDest = nStart;
991 for (size_t i = 0; i < nCount; ++i, ++nDest)
993 SCCOLROW nSrc = aIndices[i];
994 if (nDest != nSrc)
996 aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
998 // Update the position of the index that was originally equal to nDest.
999 size_t nPos = aPosTable[nDest-nStart];
1000 aIndices[nPos] = nSrc;
1001 aPosTable[nSrc-nStart] = nPos;
1004 if (pProgress)
1005 pProgress->SetStateOnPercent(i);
1008 // Reset formula cell positions which became out-of-sync after column reordering.
1009 bool bUpdateRefs = pArray->IsUpdateRefs();
1010 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1011 aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
1013 if (pArray->IsUpdateRefs())
1015 // Set up column reorder map (for later broadcasting of reference updates).
1016 sc::ColRowReorderMapType aColMap;
1017 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1018 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1020 SCCOL nNew = i + nStart;
1021 SCCOL nOld = rOldIndices[i];
1022 aColMap.emplace(nOld, nNew);
1025 // Collect all listeners within sorted range ahead of time.
1026 std::vector<SvtListener*> aListeners;
1028 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1029 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1031 // Get all area listeners that listen on one column within the range
1032 // and end their listening.
1033 ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
1034 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1035 aMoveRange, sc::AreaOverlapType::OneColumnInside);
1037 for (auto& rAreaListener : aAreaListeners)
1039 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1040 aListeners.push_back( rAreaListener.mpListener);
1044 // Remove any duplicate listener entries and notify all listeners
1045 // afterward. We must ensure that we notify each unique listener only
1046 // once.
1047 std::sort(aListeners.begin(), aListeners.end());
1048 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1049 ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
1050 std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1052 // Re-start area listeners on the reordered columns.
1054 for (auto& rAreaListener : aAreaListeners)
1056 ScRange aNewRange = rAreaListener.maArea;
1057 sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
1058 if (itCol != aColMap.end())
1060 aNewRange.aStart.SetCol( itCol->second);
1061 aNewRange.aEnd.SetCol( itCol->second);
1063 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1067 else // !(pArray->IsUpdateRefs())
1069 // Notify the cells' listeners to (re-)start listening.
1070 sc::RefStartListeningHint aHint;
1071 for (auto const & l : aCellListeners)
1072 l->Notify(aHint);
1075 // Re-join formulas at row boundaries now that all the references have
1076 // been adjusted for column reordering.
1077 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1079 sc::CellStoreType& rCells = aCol[nCol].maCells;
1080 sc::CellStoreType::position_type aPos = rCells.position(nRow1);
1081 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1082 if (nRow2 < rDocument.MaxRow())
1084 aPos = rCells.position(aPos.first, nRow2+1);
1085 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1090 void ScTable::SortReorderByRow( ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2,
1091 ScProgress* pProgress, bool bOnlyDataAreaExtras )
1093 assert(!pArray->IsUpdateRefs());
1095 if (nCol2 < nCol1)
1096 return;
1098 // bOnlyDataAreaExtras:
1099 // Data area extras by definition do not have any cell content so no
1100 // formula cells either, so that handling doesn't need to be executed.
1101 // However, there may be listeners of formulas listening to broadcasters of
1102 // empty cells.
1104 SCROW nRow1 = pArray->GetStart();
1105 SCROW nRow2 = pArray->GetLast();
1107 // Collect all listeners of cell broadcasters of sorted range.
1108 std::vector<SvtListener*> aCellListeners;
1110 // When the update ref mode is disabled, we need to detach all formula
1111 // cells in the sorted range before reordering, and re-start them
1112 // afterward.
1113 if (!bOnlyDataAreaExtras)
1115 sc::EndListeningContext aCxt(rDocument);
1116 DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1119 // Collect listeners of cell broadcasters.
1120 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1121 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
1123 // Remove any duplicate listener entries. We must ensure that we notify
1124 // each unique listener only once.
1125 std::sort(aCellListeners.begin(), aCellListeners.end());
1126 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
1128 // Notify the cells' listeners to stop listening.
1129 /* TODO: for performance this could be enhanced to stop and later
1130 * restart only listening to within the reordered range and keep
1131 * listening to everything outside untouched. */
1133 sc::RefStopListeningHint aHint;
1134 for (auto const & l : aCellListeners)
1135 l->Notify(aHint);
1138 // Split formula groups at the sort range boundaries (if applicable).
1139 if (!bOnlyDataAreaExtras)
1141 std::vector<SCROW> aRowBounds
1143 nRow1,
1144 nRow2+1
1146 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1147 SplitFormulaGroups(nCol, aRowBounds);
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);
1157 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1159 SCCOL nThisCol = i + nCol1;
1161 if (!bOnlyDataAreaExtras)
1164 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1165 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1166 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1170 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1171 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1172 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1177 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1178 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
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);
1186 // Update draw object positions
1187 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
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);
1195 for (const auto& rSpan : aSpans)
1197 assert(rSpan.mpPattern); // should never be NULL.
1198 rDocument.GetPool()->Put(*rSpan.mpPattern);
1201 for (const auto& rSpan : aSpans)
1203 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1204 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1208 aCol[nThisCol].CellStorageModified();
1211 if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
1213 aRowFlags.maRowsHidden.build_tree();
1214 aRowFlags.maRowsFiltered.build_tree();
1216 // Remove all flags in the range first.
1217 SetRowHidden(nRow1, nRow2, false);
1218 SetRowFiltered(nRow1, nRow2, false);
1220 std::vector<sc::RowSpan> aSpans =
1221 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1223 for (const auto& rSpan : aSpans)
1224 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1226 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1228 for (const auto& rSpan : aSpans)
1229 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1232 // Notify the cells' listeners to (re-)start listening.
1234 sc::RefStartListeningHint aHint;
1235 for (auto const & l : aCellListeners)
1236 l->Notify(aHint);
1239 if (!bOnlyDataAreaExtras)
1241 // Re-group columns in the sorted range too.
1242 for (SCCOL i = nCol1; i <= nCol2; ++i)
1243 aCol[i].RegroupFormulaCells();
1246 sc::StartListeningContext aCxt(rDocument);
1247 AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1252 void ScTable::SortReorderByRowRefUpdate(
1253 ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1255 assert(pArray->IsUpdateRefs());
1257 if (nCol2 < nCol1)
1258 return;
1260 SCROW nRow1 = pArray->GetStart();
1261 SCROW nRow2 = pArray->GetLast();
1263 ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1264 sc::ColumnSpanSet aGrpListenerRanges;
1267 // Get the range of formula group listeners within sorted range (if any).
1268 sc::QueryRange aQuery;
1270 ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM();
1271 std::vector<sc::AreaListener> aGrpListeners =
1272 pBASM->GetAllListeners(
1273 aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group);
1276 for (const auto& rGrpListener : aGrpListeners)
1278 assert(rGrpListener.mbGroupListening);
1279 SvtListener* pGrpLis = rGrpListener.mpListener;
1280 pGrpLis->Query(aQuery);
1281 rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis);
1285 ScRangeList aTmp;
1286 aQuery.swapRanges(aTmp);
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)
1293 ScRange aRange = aTmp[i];
1294 if (!aMoveRange.Intersects(aRange))
1296 // Doesn't overlap with the sorted range at all.
1297 aGrpListenerRanges.set(GetDoc(), aRange, true);
1298 continue;
1301 if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
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;
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())
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());
1321 else
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());
1329 aGrpListenerRanges.set(GetDoc(), aR1, true);
1330 aGrpListenerRanges.set(GetDoc(), aR2, true);
1334 // Split formula groups at the sort range boundaries (if applicable).
1335 std::vector<SCROW> aRowBounds
1337 nRow1,
1338 nRow2+1
1340 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1341 SplitFormulaGroups(nCol, aRowBounds);
1343 // Cells in the data rows only reference values in the document. Make
1344 // a copy before updating the document.
1345 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1346 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1347 std::vector<SvtListener*> aListenersDummy;
1348 fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false);
1350 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1352 SCCOL nThisCol = i + nCol1;
1355 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1356 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1357 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1361 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1362 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1363 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1367 sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters;
1368 sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
1370 // Release current broadcasters first, to prevent them from getting deleted.
1371 rDest.release_range(nRow1, nRow2);
1373 // Transfer sorted broadcaster segment to the document.
1374 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1378 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1379 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1381 // Do the same as broadcaster storage transfer (to prevent double deletion).
1382 rDest.release_range(nRow1, nRow2);
1383 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1384 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1387 // Update draw object positions
1388 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1391 // Get all row spans where the pattern is not NULL.
1392 std::vector<PatternSpan> aSpans =
1393 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1394 aSortedCols[i]->maPatterns);
1396 for (const auto& rSpan : aSpans)
1398 assert(rSpan.mpPattern); // should never be NULL.
1399 rDocument.GetPool()->Put(*rSpan.mpPattern);
1402 for (const auto& rSpan : aSpans)
1404 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1405 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1409 aCol[nThisCol].CellStorageModified();
1412 if (pArray->IsKeepQuery())
1414 aRowFlags.maRowsHidden.build_tree();
1415 aRowFlags.maRowsFiltered.build_tree();
1417 // Remove all flags in the range first.
1418 SetRowHidden(nRow1, nRow2, false);
1419 SetRowFiltered(nRow1, nRow2, false);
1421 std::vector<sc::RowSpan> aSpans =
1422 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1424 for (const auto& rSpan : aSpans)
1425 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1427 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1429 for (const auto& rSpan : aSpans)
1430 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1433 // Set up row reorder map (for later broadcasting of reference updates).
1434 sc::ColRowReorderMapType aRowMap;
1435 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1436 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1438 SCROW nNew = i + nRow1;
1439 SCROW nOld = rOldIndices[i];
1440 aRowMap.emplace(nOld, nNew);
1443 // Collect all listeners within sorted range ahead of time.
1444 std::vector<SvtListener*> aListeners;
1446 // Collect listeners of cell broadcasters.
1447 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1448 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1450 // Get all area listeners that listen on one row within the range and end
1451 // their listening.
1452 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1453 aMoveRange, sc::AreaOverlapType::OneRowInside);
1455 for (auto& rAreaListener : aAreaListeners)
1457 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1458 aListeners.push_back( rAreaListener.mpListener);
1463 // Get all formula cells from the former group area listener ranges.
1465 std::vector<ScFormulaCell*> aFCells;
1466 FormulaCellCollectAction aAction(aFCells);
1467 aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1469 aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() );
1472 // Remove any duplicate listener entries. We must ensure that we notify
1473 // each unique listener only once.
1474 std::sort(aListeners.begin(), aListeners.end());
1475 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1477 // Collect positions of all shared formula cells outside the sorted range,
1478 // and make them unshared before notifying them.
1479 sc::RefQueryFormulaGroup aFormulaGroupPos;
1480 aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1482 std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
1483 const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
1484 for (const auto& [rTab, rCols] : rGroupTabs)
1486 for (const auto& [nCol, rCol] : rCols)
1488 std::vector<SCROW> aBounds(rCol);
1489 rDocument.UnshareFormulaCells(rTab, nCol, aBounds);
1493 // Notify the listeners to update their references.
1494 ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
1495 std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1497 // Re-group formulas in affected columns.
1498 for (const auto& [rTab, rCols] : rGroupTabs)
1500 for (const auto& rEntry : rCols)
1501 rDocument.RegroupFormulaCells(rTab, rEntry.first);
1504 // Re-start area listeners on the reordered rows.
1505 for (const auto& rAreaListener : aAreaListeners)
1507 ScRange aNewRange = rAreaListener.maArea;
1508 sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
1509 if (itRow != aRowMap.end())
1511 aNewRange.aStart.SetRow( itRow->second);
1512 aNewRange.aEnd.SetRow( itRow->second);
1514 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1517 // Re-group columns in the sorted range too.
1518 for (SCCOL i = nCol1; i <= nCol2; ++i)
1519 aCol[i].RegroupFormulaCells();
1522 // Re-start area listeners on the old group listener ranges.
1523 ListenerStartAction aAction(rDocument);
1524 aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1528 short ScTable::CompareCell(
1529 sal_uInt16 nSort,
1530 ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1531 ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1533 short nRes = 0;
1535 CellType eType1 = rCell1.meType, eType2 = rCell2.meType;
1537 if (!rCell1.isEmpty())
1539 if (!rCell2.isEmpty())
1541 bool bErr1 = false;
1542 bool bStr1 = ( eType1 != CELLTYPE_VALUE );
1543 if (eType1 == CELLTYPE_FORMULA)
1545 if (rCell1.mpFormula->GetErrCode() != FormulaError::NONE)
1547 bErr1 = true;
1548 bStr1 = false;
1550 else if (rCell1.mpFormula->IsValue())
1552 bStr1 = false;
1556 bool bErr2 = false;
1557 bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1558 if (eType2 == CELLTYPE_FORMULA)
1560 if (rCell2.mpFormula->GetErrCode() != FormulaError::NONE)
1562 bErr2 = true;
1563 bStr2 = false;
1565 else if (rCell2.mpFormula->IsValue())
1567 bStr2 = false;
1571 if ( bStr1 && bStr2 ) // only compare strings as strings!
1573 OUString aStr1;
1574 OUString aStr2;
1575 if (eType1 == CELLTYPE_STRING)
1576 aStr1 = rCell1.mpString->getString();
1577 else
1578 aStr1 = GetString(nCell1Col, nCell1Row);
1579 if (eType2 == CELLTYPE_STRING)
1580 aStr2 = rCell2.mpString->getString();
1581 else
1582 aStr2 = GetString(nCell2Col, nCell2Row);
1584 bool bUserDef = aSortParam.bUserDef; // custom sort order
1585 bool bNaturalSort = aSortParam.bNaturalSort; // natural sort
1586 bool bCaseSens = aSortParam.bCaseSens; // case sensitivity
1588 ScUserList* pList = ScGlobal::GetUserList();
1589 if (bUserDef && pList && pList->size() > aSortParam.nUserIndex )
1591 const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
1593 if ( bNaturalSort )
1594 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator );
1595 else
1597 if ( bCaseSens )
1598 nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) );
1599 else
1600 nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) );
1604 if (!bUserDef)
1606 if ( bNaturalSort )
1607 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator );
1608 else
1609 nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
1612 else if ( bStr1 ) // String <-> Number or Error
1614 if (bErr2)
1615 nRes = -1; // String in front of Error
1616 else
1617 nRes = 1; // Number in front of String
1619 else if ( bStr2 ) // Number or Error <-> String
1621 if (bErr1)
1622 nRes = 1; // String in front of Error
1623 else
1624 nRes = -1; // Number in front of String
1626 else if (bErr1 && bErr2)
1628 // nothing, two Errors are equal
1630 else if (bErr1) // Error <-> Number
1632 nRes = 1; // Number in front of Error
1634 else if (bErr2) // Number <-> Error
1636 nRes = -1; // Number in front of Error
1638 else // Mixed numbers
1640 double nVal1 = rCell1.getValue();
1641 double nVal2 = rCell2.getValue();
1642 if (nVal1 < nVal2)
1643 nRes = -1;
1644 else if (nVal1 > nVal2)
1645 nRes = 1;
1647 if ( !aSortParam.maKeyState[nSort].bAscending )
1648 nRes = -nRes;
1650 else
1651 nRes = -1;
1653 else
1655 if (!rCell2.isEmpty())
1656 nRes = 1;
1657 else
1658 nRes = 0; // both empty
1660 return nRes;
1663 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1665 short nRes;
1666 sal_uInt16 nSort = 0;
1669 ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 );
1670 ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 );
1671 if ( aSortParam.bByRow )
1672 nRes = CompareCell( nSort,
1673 rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg,
1674 rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg );
1675 else
1676 nRes = CompareCell( nSort,
1677 rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField,
1678 rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField );
1679 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
1680 if( nRes == 0 )
1682 ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 );
1683 ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 );
1684 if( rInfo1.nOrg < rInfo2.nOrg )
1685 nRes = -1;
1686 else if( rInfo1.nOrg > rInfo2.nOrg )
1687 nRes = 1;
1689 return nRes;
1692 void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi )
1694 if ((nHi - nLo) == 1)
1696 if (Compare(pArray, nLo, nHi) > 0)
1697 pArray->Swap( nLo, nHi );
1699 else
1701 SCCOLROW ni = nLo;
1702 SCCOLROW nj = nHi;
1705 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1706 ni++;
1707 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1708 nj--;
1709 if (ni <= nj)
1711 if (ni != nj)
1712 pArray->Swap( ni, nj );
1713 ni++;
1714 nj--;
1716 } while (ni < nj);
1717 if ((nj - nLo) < (nHi - ni))
1719 if (nLo < nj)
1720 QuickSort(pArray, nLo, nj);
1721 if (ni < nHi)
1722 QuickSort(pArray, ni, nHi);
1724 else
1726 if (ni < nHi)
1727 QuickSort(pArray, ni, nHi);
1728 if (nLo < nj)
1729 QuickSort(pArray, nLo, nj);
1734 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1736 short nRes;
1737 sal_uInt16 nSort = 0;
1738 const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
1739 if (aSortParam.bByRow)
1743 SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
1744 ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
1745 ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
1746 nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
1747 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1749 else
1753 SCROW nRow = aSortParam.maKeyState[nSort].nField;
1754 ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow);
1755 ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow);
1756 nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
1757 nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
1758 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1760 return nRes;
1763 bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // over aSortParam
1765 for (SCCOLROW i=nStart; i<nEnd; i++)
1767 if (Compare( i, i+1 ) > 0)
1768 return false;
1770 return true;
1773 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1775 SCROW nRow;
1776 int nMax = nRow2 - nRow1;
1777 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
1779 nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
1780 pArray->Swap(i, nRow1 + nRow);
1784 void ScTable::Sort(
1785 const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1786 ScProgress* pProgress, sc::ReorderParam* pUndo )
1788 InitSortCollator( rSortParam );
1789 bGlobalKeepQuery = bKeepQuery;
1791 if (pUndo)
1793 // Copy over the basic sort parameters.
1794 pUndo->maDataAreaExtras = rSortParam.aDataAreaExtras;
1795 pUndo->mbByRow = rSortParam.bByRow;
1796 pUndo->mbHiddenFiltered = bKeepQuery;
1797 pUndo->mbUpdateRefs = bUpdateRefs;
1798 pUndo->mbHasHeaders = rSortParam.bHasHeader;
1801 // It is assumed that the data area has already been trimmed as necessary.
1803 aSortParam = rSortParam; // must be assigned before calling IsSorted()
1804 if (rSortParam.bByRow)
1806 const SCROW nLastRow = rSortParam.nRow2;
1807 const SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
1808 if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
1810 if(pProgress)
1811 pProgress->SetState( 0, nLastRow-nRow1 );
1813 std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1814 aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
1816 if ( nLastRow - nRow1 > 255 )
1817 DecoladeRow(pArray.get(), nRow1, nLastRow);
1819 QuickSort(pArray.get(), nRow1, nLastRow);
1820 if (pArray->IsUpdateRefs())
1821 SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1822 else
1824 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress, false);
1825 if (rSortParam.aDataAreaExtras.anyExtrasWanted())
1826 SortReorderAreaExtrasByRow( pArray.get(), aSortParam.nCol1, aSortParam.nCol2,
1827 rSortParam.aDataAreaExtras, pProgress);
1830 if (pUndo)
1832 // Stored is the first data row without header row.
1833 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1834 pUndo->maDataAreaExtras.mnStartRow = nRow1;
1835 pUndo->maOrderIndices = pArray->GetOrderIndices();
1839 else
1841 const SCCOL nLastCol = rSortParam.nCol2;
1842 const SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1843 if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1845 if(pProgress)
1846 pProgress->SetState( 0, nLastCol-nCol1 );
1848 std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1849 aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
1851 QuickSort(pArray.get(), nCol1, nLastCol);
1852 SortReorderByColumn(pArray.get(), rSortParam.nRow1, rSortParam.nRow2,
1853 rSortParam.aDataAreaExtras.mbCellFormats, pProgress);
1854 if (rSortParam.aDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1855 SortReorderAreaExtrasByColumn( pArray.get(),
1856 rSortParam.nRow1, rSortParam.nRow2, rSortParam.aDataAreaExtras, pProgress);
1858 if (pUndo)
1860 // Stored is the first data column without header column.
1861 pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
1862 pUndo->maDataAreaExtras.mnStartCol = nCol1;
1863 pUndo->maOrderIndices = pArray->GetOrderIndices();
1867 DestroySortCollator();
1870 void ScTable::Reorder( const sc::ReorderParam& rParam )
1872 if (rParam.maOrderIndices.empty())
1873 return;
1875 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1876 if (!pArray)
1877 return;
1879 if (rParam.mbByRow)
1881 // Re-play sorting from the known sort indices.
1882 pArray->ReorderByRow(rParam.maOrderIndices);
1883 if (pArray->IsUpdateRefs())
1884 SortReorderByRowRefUpdate(
1885 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
1886 else
1888 SortReorderByRow( pArray.get(),
1889 rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr, false);
1890 if (rParam.maDataAreaExtras.anyExtrasWanted())
1891 SortReorderAreaExtrasByRow( pArray.get(),
1892 rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(),
1893 rParam.maDataAreaExtras, nullptr);
1896 else
1898 // Ordering by column is much simpler. Just set the order indices and we are done.
1899 pArray->SetOrderIndices(std::vector(rParam.maOrderIndices));
1900 SortReorderByColumn(
1901 pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1902 rParam.maDataAreaExtras.mbCellFormats, nullptr);
1903 if (rParam.maDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1904 SortReorderAreaExtrasByColumn( pArray.get(),
1905 rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1906 rParam.maDataAreaExtras, nullptr);
1910 namespace {
1912 class SubTotalRowFinder
1914 const ScTable& mrTab;
1915 const ScSubTotalParam& mrParam;
1917 public:
1918 SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
1919 mrTab(rTab), mrParam(rParam) {}
1921 bool operator() (size_t nRow, const ScFormulaCell* pCell)
1923 if (!pCell->IsSubTotal())
1924 return false;
1926 SCCOL nStartCol = mrParam.nCol1;
1927 SCCOL nEndCol = mrParam.nCol2;
1929 for (SCCOL nCol : mrTab.GetColumnsRange(0, nStartCol - 1))
1931 if (mrTab.HasData(nCol, nRow))
1932 return true;
1934 for (SCCOL nCol : mrTab.GetColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol()))
1936 if (mrTab.HasData(nCol, nRow))
1937 return true;
1939 return false;
1945 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
1947 SCCOL nStartCol = rParam.nCol1;
1948 SCROW nStartRow = rParam.nRow1 + 1; // Header
1949 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
1950 SCROW nEndRow = rParam.nRow2;
1952 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1954 const sc::CellStoreType& rCells = aCol[nCol].maCells;
1955 SubTotalRowFinder aFunc(*this, rParam);
1956 std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
1957 sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
1958 if (aPos.first != rCells.end())
1959 return true;
1961 return false;
1964 namespace {
1966 struct RemoveSubTotalsHandler
1968 std::set<SCROW> aRemoved;
1970 void operator() (size_t nRow, const ScFormulaCell* p)
1972 if (p->IsSubTotal())
1973 aRemoved.insert(nRow);
1979 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
1981 SCCOL nStartCol = rParam.nCol1;
1982 SCROW nStartRow = rParam.nRow1 + 1; // Header
1983 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
1984 SCROW nEndRow = rParam.nRow2; // will change
1986 RemoveSubTotalsHandler aFunc;
1987 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1989 const sc::CellStoreType& rCells = aCol[nCol].maCells;
1990 sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
1993 auto& aRows = aFunc.aRemoved;
1995 std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) {
1996 RemoveRowBreak(nRow+1, false, true);
1997 rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1);
2000 rParam.nRow2 -= aRows.size();
2003 // Delete hard number formats (for result formulas)
2005 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
2007 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
2008 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
2009 == SfxItemState::SET )
2011 auto pNewPattern = std::make_unique<ScPatternAttr>( *pPattern );
2012 SfxItemSet& rSet = pNewPattern->GetItemSet();
2013 rSet.ClearItem( ATTR_VALUE_FORMAT );
2014 rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
2015 pTab->SetPattern( nCol, nRow, std::move(pNewPattern) );
2019 namespace {
2021 struct RowEntry
2023 sal_uInt16 nGroupNo;
2024 SCROW nSubStartRow;
2025 SCROW nDestRow;
2026 SCROW nFuncStart;
2027 SCROW nFuncEnd;
2032 static TranslateId lcl_GetSubTotalStrId(int id)
2034 switch ( id )
2036 case SUBTOTAL_FUNC_AVE: return STR_FUN_TEXT_AVG;
2037 case SUBTOTAL_FUNC_CNT:
2038 case SUBTOTAL_FUNC_CNT2: return STR_FUN_TEXT_COUNT;
2039 case SUBTOTAL_FUNC_MAX: return STR_FUN_TEXT_MAX;
2040 case SUBTOTAL_FUNC_MIN: return STR_FUN_TEXT_MIN;
2041 case SUBTOTAL_FUNC_PROD: return STR_FUN_TEXT_PRODUCT;
2042 case SUBTOTAL_FUNC_STD:
2043 case SUBTOTAL_FUNC_STDP: return STR_FUN_TEXT_STDDEV;
2044 case SUBTOTAL_FUNC_SUM: return STR_FUN_TEXT_SUM;
2045 case SUBTOTAL_FUNC_VAR:
2046 case SUBTOTAL_FUNC_VARP: return STR_FUN_TEXT_VAR;
2047 default:
2049 return STR_EMPTYDATA;
2050 // added to avoid warnings
2055 // new intermediate results
2056 // rParam.nRow2 is changed!
2058 bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
2060 SCCOL nStartCol = rParam.nCol1;
2061 SCROW nStartRow = rParam.nRow1 + 1; // Header
2062 SCCOL nEndCol = rParam.nCol2;
2063 SCROW nEndRow = rParam.nRow2; // will change
2064 sal_uInt16 i;
2066 // Remove empty rows at the end
2067 // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
2068 // If sorted, all empty rows are at the end.
2069 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
2070 nEndRow -= nEmpty;
2072 sal_uInt16 nLevelCount = 0; // Number of levels
2073 bool bDoThis = true;
2074 for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
2075 if (rParam.bGroupActive[i])
2076 nLevelCount = i+1;
2077 else
2078 bDoThis = false;
2080 if (nLevelCount==0) // do nothing
2081 return true;
2083 SCCOL* nGroupCol = rParam.nField; // columns which will be used when grouping
2085 // With (blank) as a separate category, subtotal rows from
2086 // the other columns must always be tested
2087 // (previously only when a column occurred more than once)
2088 bool bTestPrevSub = ( nLevelCount > 1 );
2090 OUString aSubString;
2092 bool bIgnoreCase = !rParam.bCaseSens;
2094 OUString aCompString[MAXSUBTOTAL];
2096 //TODO: sort?
2098 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find(
2099 ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para ));
2101 bool bSpaceLeft = true; // Success when inserting?
2103 // For performance reasons collect formula entries so their
2104 // references don't have to be tested for updates each time a new row is
2105 // inserted
2106 RowEntry aRowEntry;
2107 ::std::vector< RowEntry > aRowVector;
2109 for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++)
2111 aRowEntry.nGroupNo = nLevelCount - nLevel - 1;
2113 // how many results per level
2114 SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
2115 // result functions
2116 ScSubTotalFunc* pResFunc = rParam.pFunctions[aRowEntry.nGroupNo].get();
2118 if (nResCount > 0) // otherwise only sort
2120 for (i=0; i<=aRowEntry.nGroupNo; i++)
2122 aSubString = GetString( nGroupCol[i], nStartRow );
2123 if ( bIgnoreCase )
2124 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2125 else
2126 aCompString[i] = aSubString;
2127 } // aSubString stays on the last
2129 bool bBlockVis = false; // group visible?
2130 aRowEntry.nSubStartRow = nStartRow;
2131 for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
2133 bool bChanged;
2134 if (nRow>nEndRow)
2135 bChanged = true;
2136 else
2138 bChanged = false;
2139 OUString aString;
2140 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
2142 aString = GetString( nGroupCol[i], nRow );
2143 if (bIgnoreCase)
2144 aString = ScGlobal::getCharClass().uppercase(aString);
2145 // when sorting, blanks are separate group
2146 // otherwise blank cells are allowed below
2147 bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
2148 aString != aCompString[i] );
2150 if ( bChanged && bTestPrevSub )
2152 // No group change on rows that will contain subtotal formulas
2153 bChanged = std::none_of(aRowVector.begin(), aRowVector.end(),
2154 [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; });
2157 if ( bChanged )
2159 aRowEntry.nDestRow = nRow;
2160 aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
2161 aRowEntry.nFuncEnd = nRow-1;
2163 bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab,
2164 aRowEntry.nDestRow, 1 );
2165 DBShowRow( aRowEntry.nDestRow, bBlockVis );
2166 if ( rParam.bPagebreak && nRow < rDocument.MaxRow() &&
2167 aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2168 SetRowBreak(aRowEntry.nSubStartRow, false, true);
2170 if (bSpaceLeft)
2172 for ( auto& rRowEntry : aRowVector)
2174 if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow )
2175 ++rRowEntry.nSubStartRow;
2176 if ( aRowEntry.nDestRow <= rRowEntry.nDestRow )
2177 ++rRowEntry.nDestRow;
2178 if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart )
2179 ++rRowEntry.nFuncStart;
2180 if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd )
2181 ++rRowEntry.nFuncEnd;
2183 // collect formula positions
2184 aRowVector.push_back( aRowEntry );
2186 OUString aOutString = aSubString;
2187 if (aOutString.isEmpty())
2188 aOutString = ScResId( STR_EMPTYDATA );
2189 aOutString += " ";
2190 TranslateId pStrId = STR_TABLE_ERGEBNIS;
2191 if ( nResCount == 1 )
2192 pStrId = lcl_GetSubTotalStrId(pResFunc[0]);
2193 aOutString += ScResId(pStrId);
2194 SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
2195 ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle );
2197 ++nRow;
2198 ++nEndRow;
2199 aRowEntry.nSubStartRow = nRow;
2200 for (i=0; i<=aRowEntry.nGroupNo; i++)
2202 aSubString = GetString( nGroupCol[i], nRow );
2203 if ( bIgnoreCase )
2204 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2205 else
2206 aCompString[i] = aSubString;
2210 bBlockVis = !RowFiltered(nRow);
2215 if (!aRowVector.empty())
2217 // generate global total
2218 SCROW nGlobalStartRow = aRowVector[0].nSubStartRow;
2219 SCROW nGlobalStartFunc = aRowVector[0].nFuncStart;
2220 SCROW nGlobalEndRow = 0;
2221 SCROW nGlobalEndFunc = 0;
2222 for (const auto& rRowEntry : aRowVector)
2224 nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow;
2225 nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow;
2228 for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++)
2230 const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1;
2231 const ScSubTotalFunc* pResFunc = rParam.pFunctions[nGroupNo].get();
2232 if (!pResFunc)
2234 // No subtotal function given for this group => no formula or
2235 // label and do not insert a row.
2236 continue;
2239 // increment end row
2240 nGlobalEndRow++;
2242 // add row entry for formula
2243 aRowEntry.nGroupNo = nGroupNo;
2244 aRowEntry.nSubStartRow = nGlobalStartRow;
2245 aRowEntry.nFuncStart = nGlobalStartFunc;
2246 aRowEntry.nDestRow = nGlobalEndRow;
2247 aRowEntry.nFuncEnd = nGlobalEndFunc;
2249 // increment row
2250 nGlobalEndFunc++;
2252 bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1);
2254 if (bSpaceLeft)
2256 aRowVector.push_back(aRowEntry);
2257 nEndRow++;
2258 DBShowRow(aRowEntry.nDestRow, true);
2260 // insert label
2261 OUString label = ScResId(STR_TABLE_GRAND) + " " + ScResId(lcl_GetSubTotalStrId(pResFunc[0]));
2262 SetString(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, label);
2263 ApplyStyle(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle);
2268 // now insert the formulas
2269 ScComplexRefData aRef;
2270 aRef.InitFlags();
2271 aRef.Ref1.SetAbsTab(nTab);
2272 aRef.Ref2.SetAbsTab(nTab);
2273 for (const auto& rRowEntry : aRowVector)
2275 SCCOL nResCount = rParam.nSubTotals[rRowEntry.nGroupNo];
2276 SCCOL* nResCols = rParam.pSubTotals[rRowEntry.nGroupNo].get();
2277 ScSubTotalFunc* pResFunc = rParam.pFunctions[rRowEntry.nGroupNo].get();
2278 for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
2280 aRef.Ref1.SetAbsCol(nResCols[nResult]);
2281 aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart);
2282 aRef.Ref2.SetAbsCol(nResCols[nResult]);
2283 aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd);
2285 ScTokenArray aArr(rDocument);
2286 aArr.AddOpCode( ocSubTotal );
2287 aArr.AddOpCode( ocOpen );
2288 aArr.AddDouble( static_cast<double>(pResFunc[nResult]) );
2289 aArr.AddOpCode( ocSep );
2290 aArr.AddDoubleReference( aRef );
2291 aArr.AddOpCode( ocClose );
2292 aArr.AddOpCode( ocStop );
2293 ScFormulaCell* pCell = new ScFormulaCell(
2294 rDocument, ScAddress(nResCols[nResult], rRowEntry.nDestRow, nTab), aArr);
2295 if ( rParam.bIncludePattern )
2296 pCell->SetNeedNumberFormat(true);
2298 SetFormulaCell(nResCols[nResult], rRowEntry.nDestRow, pCell);
2299 if ( nResCols[nResult] != nGroupCol[rRowEntry.nGroupNo] )
2301 ApplyStyle( nResCols[nResult], rRowEntry.nDestRow, pStyle );
2303 lcl_RemoveNumberFormat( this, nResCols[nResult], rRowEntry.nDestRow );
2309 //TODO: according to setting, shift intermediate-sum rows up?
2311 //TODO: create Outlines directly?
2313 if (bSpaceLeft)
2314 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2316 rParam.nRow2 = nEndRow; // new end
2317 return bSpaceLeft;
2320 namespace {
2322 class QueryEvaluator
2324 ScDocument& mrDoc;
2325 svl::SharedStringPool& mrStrPool;
2326 const ScTable& mrTab;
2327 const ScQueryParam& mrParam;
2328 bool mpTestEqualCondition;
2329 utl::TransliterationWrapper* mpTransliteration;
2330 CollatorWrapper* mpCollator;
2331 const bool mbMatchWholeCell;
2332 const bool mbCaseSensitive;
2333 ScTable::ValidQueryCache* mpValidQueryCache;
2335 static bool isPartialTextMatchOp(const ScQueryEntry& rEntry)
2337 switch (rEntry.eOp)
2339 // these operators can only be used with textural comparisons.
2340 case SC_CONTAINS:
2341 case SC_DOES_NOT_CONTAIN:
2342 case SC_BEGINS_WITH:
2343 case SC_ENDS_WITH:
2344 case SC_DOES_NOT_BEGIN_WITH:
2345 case SC_DOES_NOT_END_WITH:
2346 return true;
2347 default:
2350 return false;
2353 static bool isTextMatchOp(const ScQueryEntry& rEntry)
2355 if (isPartialTextMatchOp(rEntry))
2356 return true;
2358 switch (rEntry.eOp)
2360 // these operators can be used for either textural or value comparison.
2361 case SC_EQUAL:
2362 case SC_NOT_EQUAL:
2363 return true;
2364 default:
2367 return false;
2370 void setupTransliteratorIfNeeded()
2372 if (!mpTransliteration)
2373 mpTransliteration = &ScGlobal::GetTransliteration(mrParam.bCaseSens);
2376 void setupCollatorIfNeeded()
2378 if (!mpCollator)
2379 mpCollator = &ScGlobal::GetCollator(mrParam.bCaseSens);
2382 public:
2383 QueryEvaluator(ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
2384 bool pTestEqualCondition, ScTable::ValidQueryCache* pValidQueryCache) :
2385 mrDoc(rDoc),
2386 mrStrPool(rDoc.GetSharedStringPool()),
2387 mrTab(rTab),
2388 mrParam(rParam),
2389 mpTestEqualCondition(pTestEqualCondition),
2390 mpTransliteration(nullptr),
2391 mpCollator(nullptr),
2392 mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell()),
2393 mbCaseSensitive( rParam.bCaseSens ),
2394 mpValidQueryCache( pValidQueryCache )
2398 bool isRealWildOrRegExp(const ScQueryEntry& rEntry) const
2400 if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
2401 return false;
2403 return isTextMatchOp(rEntry);
2406 bool isTestWildOrRegExp(const ScQueryEntry& rEntry) const
2408 if (!mpTestEqualCondition)
2409 return false;
2411 if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
2412 return false;
2414 return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
2417 static bool isQueryByValue(
2418 const ScQueryEntry::Item& rItem, ScRefCellValue& rCell)
2420 if (rItem.meType == ScQueryEntry::ByString)
2421 return false;
2423 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2424 // Error values are compared as string.
2425 return false;
2427 return rCell.hasNumeric();
2430 static bool isQueryByString(
2431 const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2432 const ScRefCellValue& rCell)
2434 if (isTextMatchOp(rEntry))
2435 return true;
2437 if (rItem.meType != ScQueryEntry::ByString)
2438 return false;
2440 return rCell.hasString();
2443 sal_uInt32 getNumFmt( SCCOL nCol, SCROW nRow, const ScInterpreterContext* pContext )
2445 sal_uInt32 nNumFmt = (pContext ?
2446 mrTab.GetNumberFormat(*pContext, ScAddress(nCol, nRow, mrTab.GetTab())) :
2447 mrTab.GetNumberFormat(nCol, nRow));
2448 if (nNumFmt && (nNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
2449 // Any General of any locale is irrelevant for rounding.
2450 nNumFmt = 0;
2451 return nNumFmt;
2454 std::pair<bool,bool> compareByValue(
2455 const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2456 const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2457 const ScInterpreterContext* pContext)
2459 bool bOk = false;
2460 bool bTestEqual = false;
2461 double nCellVal;
2462 double fQueryVal = rItem.mfVal;
2463 // Defer all number format detection to as late as possible as it's a
2464 // bottle neck, even if that complicates the code. Also do not
2465 // unnecessarily call ScDocument::RoundValueAsShown() for the same
2466 // reason.
2467 sal_uInt32 nNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
2469 switch (rCell.meType)
2471 case CELLTYPE_VALUE :
2472 nCellVal = rCell.mfValue;
2473 break;
2474 case CELLTYPE_FORMULA :
2475 nCellVal = rCell.mpFormula->GetValue();
2476 break;
2477 default:
2478 nCellVal = 0.0;
2480 if (rItem.mbRoundForFilter && nCellVal != 0.0)
2482 nNumFmt = getNumFmt( nCol, nRow, pContext);
2483 if (nNumFmt)
2485 switch (rCell.meType)
2487 case CELLTYPE_VALUE :
2488 case CELLTYPE_FORMULA :
2489 nCellVal = mrDoc.RoundValueAsShown(nCellVal, nNumFmt, pContext);
2490 break;
2491 default:
2492 assert(!"can't be");
2497 /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
2498 * date+time format was queried rEntry.bQueryByDate is not set. In
2499 * case other queries wanted to use this mechanism they should do
2500 * the same, in other words only if rEntry.nVal is an integer value
2501 * rEntry.bQueryByDate should be true and the time fraction be
2502 * stripped here. */
2504 if (rItem.meType == ScQueryEntry::ByDate)
2506 if (nNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND)
2507 nNumFmt = getNumFmt( nCol, nRow, pContext);
2508 if (nNumFmt)
2510 SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : mrDoc.GetFormatTable();
2511 const SvNumberformat* pEntry = pFormatter->GetEntry(nNumFmt);
2512 if (pEntry)
2514 SvNumFormatType nNumFmtType = pEntry->GetType();
2515 /* NOTE: Omitting the check for absence of
2516 * css::util::NumberFormat::TIME would include also date+time formatted
2517 * values of the same day. That may be desired in some
2518 * cases, querying all time values of a day, but confusing
2519 * in other cases. A user can always setup a standard
2520 * filter query for x >= date AND x < date+1 */
2521 if ((nNumFmtType & SvNumFormatType::DATE) && !(nNumFmtType & SvNumFormatType::TIME))
2523 // The format is of date type. Strip off the time
2524 // element.
2525 nCellVal = ::rtl::math::approxFloor(nCellVal);
2531 switch (rEntry.eOp)
2533 case SC_EQUAL :
2534 bOk = ::rtl::math::approxEqual(nCellVal, fQueryVal);
2535 break;
2536 case SC_LESS :
2537 bOk = (nCellVal < fQueryVal) && !::rtl::math::approxEqual(nCellVal, fQueryVal);
2538 break;
2539 case SC_GREATER :
2540 bOk = (nCellVal > fQueryVal) && !::rtl::math::approxEqual(nCellVal, fQueryVal);
2541 break;
2542 case SC_LESS_EQUAL :
2543 bOk = (nCellVal < fQueryVal) || ::rtl::math::approxEqual(nCellVal, fQueryVal);
2544 if ( bOk && mpTestEqualCondition )
2545 bTestEqual = ::rtl::math::approxEqual(nCellVal, fQueryVal);
2546 break;
2547 case SC_GREATER_EQUAL :
2548 bOk = (nCellVal > fQueryVal) || ::rtl::math::approxEqual( nCellVal, fQueryVal);
2549 if ( bOk && mpTestEqualCondition )
2550 bTestEqual = ::rtl::math::approxEqual(nCellVal, fQueryVal);
2551 break;
2552 case SC_NOT_EQUAL :
2553 bOk = !::rtl::math::approxEqual(nCellVal, fQueryVal);
2554 break;
2555 default:
2557 // added to avoid warnings
2561 return std::pair<bool,bool>(bOk, bTestEqual);
2564 OUString getCellString(const ScRefCellValue& rCell, SCROW nRow, const ScQueryEntry& rEntry,
2565 const ScInterpreterContext* pContext, const svl::SharedString** sharedString)
2567 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2569 // Error cell is evaluated as string (for now).
2570 if(mpValidQueryCache)
2572 const FormulaError error = rCell.mpFormula->GetErrCode();
2573 auto it = mpValidQueryCache->mCachedSharedErrorStrings.find(error);
2574 if( it == mpValidQueryCache->mCachedSharedErrorStrings.end())
2576 svl::SharedString str = mrStrPool.intern(ScGlobal::GetErrorString(error));
2577 auto pos = mpValidQueryCache->mCachedSharedErrorStrings.insert({error, str});
2578 assert(pos.second); // inserted
2579 it = pos.first;
2581 *sharedString = &it->second;
2582 return OUString();
2584 return ScGlobal::GetErrorString(rCell.mpFormula->GetErrCode());
2586 else if (rCell.meType == CELLTYPE_STRING)
2588 *sharedString = rCell.mpString;
2589 return OUString();
2591 else
2593 sal_uInt32 nFormat = pContext ? mrTab.GetNumberFormat( *pContext, ScAddress(static_cast<SCCOL>(rEntry.nField), nRow, mrTab.GetTab()) ) :
2594 mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
2595 SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : mrDoc.GetFormatTable();
2596 return ScCellFormat::GetInputString(rCell, nFormat, *pFormatter, mrDoc, sharedString, rEntry.bDoQuery);
2600 bool isFastCompareByString(const ScQueryEntry& rEntry) const
2602 // If this is true, then there's a fast path in compareByString() which
2603 // can be selected using the template argument to get fast code
2604 // that will not check the same conditions every time. This makes a difference
2605 // in fast lookups that search for an exact value (case sensitive or not).
2606 bool bMatchWholeCell = mbMatchWholeCell;
2607 if (isPartialTextMatchOp(rEntry))
2608 // may have to do partial textural comparison.
2609 bMatchWholeCell = false;
2611 const bool bRealWildOrRegExp = isRealWildOrRegExp(rEntry);
2612 const bool bTestWildOrRegExp = isTestWildOrRegExp(rEntry);
2614 // SC_EQUAL is part of isTextMatchOp(rEntry)
2615 return rEntry.eOp == SC_EQUAL && !bRealWildOrRegExp && !bTestWildOrRegExp && bMatchWholeCell;
2618 // The value is placed inside one parameter: [pValueSource1] or [pValueSource2] but never in both.
2619 // For the template argument see isFastCompareByString().
2620 template<bool bFast = false>
2621 std::pair<bool,bool> compareByString(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2622 const svl::SharedString* pValueSource1, const OUString * pValueSource2)
2624 bool bOk = false;
2625 bool bTestEqual = false;
2626 bool bMatchWholeCell;
2627 if(bFast)
2628 bMatchWholeCell = true;
2629 else
2631 bMatchWholeCell = mbMatchWholeCell;
2632 if (isPartialTextMatchOp(rEntry))
2633 // may have to do partial textural comparison.
2634 bMatchWholeCell = false;
2637 const bool bRealWildOrRegExp = !bFast && isRealWildOrRegExp(rEntry);
2638 const bool bTestWildOrRegExp = !bFast && isTestWildOrRegExp(rEntry);
2640 assert(!bFast || pValueSource1 != nullptr); // shared string for fast path
2641 // [pValueSource1] or [pValueSource2] but never both of them or none of them
2642 assert((pValueSource1 != nullptr) != (pValueSource2 != nullptr));
2644 if ( !bFast && ( bRealWildOrRegExp || bTestWildOrRegExp ))
2646 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2648 sal_Int32 nStart = 0;
2649 sal_Int32 nEnd = rValue.getLength();
2651 // from 614 on, nEnd is behind the found text
2652 bool bMatch = false;
2653 if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
2655 nEnd = 0;
2656 nStart = rValue.getLength();
2657 bMatch = rEntry.GetSearchTextPtr( mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell )
2658 ->SearchBackward(rValue, &nStart, &nEnd);
2660 else
2662 bMatch = rEntry.GetSearchTextPtr( mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell )
2663 ->SearchForward(rValue, &nStart, &nEnd);
2665 if ( bMatch && bMatchWholeCell
2666 && (nStart != 0 || nEnd != rValue.getLength()) )
2667 bMatch = false; // RegExp must match entire cell string
2668 if ( bRealWildOrRegExp )
2670 switch (rEntry.eOp)
2672 case SC_EQUAL:
2673 case SC_CONTAINS:
2674 bOk = bMatch;
2675 break;
2676 case SC_NOT_EQUAL:
2677 case SC_DOES_NOT_CONTAIN:
2678 bOk = !bMatch;
2679 break;
2680 case SC_BEGINS_WITH:
2681 bOk = ( bMatch && (nStart == 0) );
2682 break;
2683 case SC_DOES_NOT_BEGIN_WITH:
2684 bOk = !( bMatch && (nStart == 0) );
2685 break;
2686 case SC_ENDS_WITH:
2687 bOk = ( bMatch && (nEnd == rValue.getLength()) );
2688 break;
2689 case SC_DOES_NOT_END_WITH:
2690 bOk = !( bMatch && (nEnd == rValue.getLength()) );
2691 break;
2692 default:
2694 // added to avoid warnings
2698 else
2699 bTestEqual = bMatch;
2701 if ( bFast || !bRealWildOrRegExp )
2703 // Simple string matching i.e. no regexp match.
2704 if (bFast || isTextMatchOp(rEntry))
2706 // Check this even with bFast.
2707 if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
2709 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
2710 // the query value is assigned directly, and the string is empty. In that case,
2711 // don't find any string (isEqual would find empty string results in formula cells).
2712 bOk = false;
2713 if ( rEntry.eOp == SC_NOT_EQUAL )
2714 bOk = !bOk;
2716 else if ( bFast || bMatchWholeCell )
2718 if (bFast || pValueSource1)
2720 // Fast string equality check by comparing string identifiers.
2721 // This is the bFast path, all conditions should lead here on bFast == true.
2722 if (mrParam.bCaseSens)
2724 bOk = pValueSource1->getData() == rItem.maString.getData();
2726 else
2728 bOk = pValueSource1->getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2731 else // if (pValueSource2)
2733 if (mrParam.bCaseSens)
2735 bOk = (*pValueSource2 == rItem.maString.getString());
2737 else
2739 // fallback
2740 const svl::SharedString rSource2(mrStrPool.intern(*pValueSource2));
2741 // Fast string equality check by comparing string identifiers.
2742 bOk = rSource2.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2746 if ( !bFast && rEntry.eOp == SC_NOT_EQUAL )
2747 bOk = !bOk;
2749 else
2751 // Where do we find a match (if at all)
2752 sal_Int32 nStrPos;
2754 if (!mbCaseSensitive)
2755 { // Common case for vlookup etc.
2756 const svl::SharedString rSource(pValueSource1? *pValueSource1 : mrStrPool.intern(*pValueSource2));
2758 const rtl_uString *pQuer = rItem.maString.getDataIgnoreCase();
2759 const rtl_uString *pCellStr = rSource.getDataIgnoreCase();
2761 assert(pQuer != nullptr);
2762 assert(pCellStr != nullptr);
2764 const sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH ||
2765 rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2766 (pCellStr->length - pQuer->length) : 0;
2768 if (nIndex < 0)
2769 nStrPos = -1;
2770 else
2771 { // OUString::indexOf
2772 nStrPos = rtl_ustr_indexOfStr_WithLength(
2773 pCellStr->buffer + nIndex, pCellStr->length - nIndex,
2774 pQuer->buffer, pQuer->length );
2776 if (nStrPos >= 0)
2777 nStrPos += nIndex;
2780 else
2782 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2783 const OUString aQueryStr = rItem.maString.getString();
2784 const LanguageType nLang = ScGlobal::oSysLocale->GetLanguageTag().getLanguageType();
2785 setupTransliteratorIfNeeded();
2786 const OUString aCell( mpTransliteration->transliterate(
2787 rValue, nLang, 0, rValue.getLength(),
2788 nullptr ) );
2790 const OUString aQuer( mpTransliteration->transliterate(
2791 aQueryStr, nLang, 0, aQueryStr.getLength(),
2792 nullptr ) );
2794 const sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2795 (aCell.getLength() - aQuer.getLength()) : 0;
2796 nStrPos = ((nIndex < 0) ? -1 : aCell.indexOf( aQuer, nIndex ));
2798 switch (rEntry.eOp)
2800 case SC_EQUAL:
2801 case SC_CONTAINS:
2802 bOk = ( nStrPos != -1 );
2803 break;
2804 case SC_NOT_EQUAL:
2805 case SC_DOES_NOT_CONTAIN:
2806 bOk = ( nStrPos == -1 );
2807 break;
2808 case SC_BEGINS_WITH:
2809 bOk = ( nStrPos == 0 );
2810 break;
2811 case SC_DOES_NOT_BEGIN_WITH:
2812 bOk = ( nStrPos != 0 );
2813 break;
2814 case SC_ENDS_WITH:
2815 bOk = ( nStrPos >= 0 );
2816 break;
2817 case SC_DOES_NOT_END_WITH:
2818 bOk = ( nStrPos < 0 );
2819 break;
2820 default:
2822 // added to avoid warnings
2827 else
2828 { // use collator here because data was probably sorted
2829 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2830 setupCollatorIfNeeded();
2831 sal_Int32 nCompare = mpCollator->compareString(
2832 rValue, rItem.maString.getString());
2833 switch (rEntry.eOp)
2835 case SC_LESS :
2836 bOk = (nCompare < 0);
2837 break;
2838 case SC_GREATER :
2839 bOk = (nCompare > 0);
2840 break;
2841 case SC_LESS_EQUAL :
2842 bOk = (nCompare <= 0);
2843 if ( bOk && mpTestEqualCondition && !bTestEqual )
2844 bTestEqual = (nCompare == 0);
2845 break;
2846 case SC_GREATER_EQUAL :
2847 bOk = (nCompare >= 0);
2848 if ( bOk && mpTestEqualCondition && !bTestEqual )
2849 bTestEqual = (nCompare == 0);
2850 break;
2851 default:
2853 // added to avoid warnings
2859 return std::pair<bool,bool>(bOk, bTestEqual);
2862 std::pair<bool, bool> compareByTextColor(SCCOL nCol, SCROW nRow, SCTAB nTab,
2863 const ScQueryEntry::Item& rItem)
2865 ScAddress aPos(nCol, nRow, nTab);
2866 Color color;
2867 bool bHasConditionalColor = false;
2868 // Text color can be set via conditional formatting - check that first
2869 const ScPatternAttr* pPattern = mrDoc.GetPattern(nCol, nRow, nTab);
2870 if (pPattern)
2872 if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
2874 const SfxItemSet* pCondSet
2875 = mrDoc.GetCondResult(nCol, nRow, nTab);
2876 const SvxColorItem* pColor = &pPattern->GetItem(ATTR_FONT_COLOR, pCondSet);
2877 color = pColor->GetValue();
2878 bHasConditionalColor = true;
2882 if (!bHasConditionalColor)
2884 const SvxColorItem* pColor = mrDoc.GetAttr(aPos, ATTR_FONT_COLOR);
2885 color = pColor->GetValue();
2888 bool bMatch = rItem.maColor == color;
2889 return std::pair<bool, bool>(bMatch, false);
2892 std::pair<bool, bool> compareByBackgroundColor(SCCOL nCol, SCROW nRow, SCTAB nTab,
2893 const ScQueryEntry::Item& rItem)
2895 ScAddress aPos(nCol, nRow, nTab);
2896 Color color;
2898 // Background color can be set via conditional formatting - check that first
2899 bool bHasConditionalColor = false;
2900 const ScPatternAttr* pPattern = mrDoc.GetPattern(nCol, nRow, nTab);
2901 if (pPattern)
2903 if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
2905 const SfxItemSet* pCondSet
2906 = mrDoc.GetCondResult(nCol, nRow, nTab);
2907 const SvxBrushItem* pBackgroundColor = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
2908 color = pBackgroundColor->GetColor();
2909 bHasConditionalColor = true;
2913 ScConditionalFormat* pCondFormat = mrDoc.GetCondFormat(nCol, nRow, nTab);
2914 if (pCondFormat)
2916 for (size_t i = 0; i < pCondFormat->size(); i++)
2918 auto aEntry = pCondFormat->GetEntry(i);
2919 if (aEntry->GetType() == ScFormatEntry::Type::Colorscale)
2921 const ScColorScaleFormat* pColFormat
2922 = static_cast<const ScColorScaleFormat*>(aEntry);
2923 color = *(pColFormat->GetColor(aPos));
2924 bHasConditionalColor = true;
2929 if (!bHasConditionalColor)
2931 const SvxBrushItem* pBrush = mrDoc.GetAttr(aPos, ATTR_BACKGROUND);
2932 color = pBrush->GetColor();
2935 bool bMatch = rItem.maColor == color;
2936 return std::pair<bool, bool>(bMatch, false);
2939 // To be called only if both isQueryByValue() and isQueryByString()
2940 // returned false and range lookup is wanted! In range lookup comparison
2941 // numbers are less than strings. Nothing else is compared.
2942 static std::pair<bool,bool> compareByRangeLookup(
2943 const ScRefCellValue& rCell,
2944 const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
2946 bool bTestEqual = false;
2948 if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS && rEntry.eOp != SC_LESS_EQUAL)
2949 return std::pair<bool,bool>(false, bTestEqual);
2951 if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER && rEntry.eOp != SC_GREATER_EQUAL)
2952 return std::pair<bool,bool>(false, bTestEqual);
2954 if (rItem.meType == ScQueryEntry::ByString)
2956 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2957 // Error values are compared as string.
2958 return std::pair<bool,bool>(false, bTestEqual);
2960 return std::pair<bool,bool>(rCell.hasNumeric(), bTestEqual);
2963 return std::pair<bool,bool>(!rCell.hasNumeric(), bTestEqual);
2967 std::pair<bool,bool> validQueryProcessEntry(SCROW nRow, SCCOL nCol, SCTAB nTab, const ScQueryParam& rParam,
2968 ScRefCellValue& aCell, bool* pbTestEqualCondition, const ScInterpreterContext* pContext, QueryEvaluator& aEval,
2969 const ScQueryEntry& rEntry )
2971 std::pair<bool,bool> aRes(false, false);
2972 const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2973 if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
2975 if (rEntry.IsQueryByEmpty())
2976 aRes.first = aCell.isEmpty();
2977 else
2979 assert(rEntry.IsQueryByNonEmpty());
2980 aRes.first = !aCell.isEmpty();
2982 return aRes;
2984 if( rEntry.eOp == SC_EQUAL && rItems.size() >= 10 )
2986 // If there are many items to query for (autofilter does this), then try to search
2987 // efficiently in those items. So first search all the items of the relevant type,
2988 // If that does not find anything, fall back to the generic code.
2989 double value = 0;
2990 bool valid = true;
2991 // For ScQueryEntry::ByValue check that the cell either is a value or is a formula
2992 // that has a value and is not an error (those are compared as strings). This
2993 // is basically simplified isQueryByValue().
2994 if( aCell.meType == CELLTYPE_VALUE )
2995 value = aCell.mfValue;
2996 else if (aCell.meType == CELLTYPE_FORMULA && aCell.mpFormula->GetErrCode() != FormulaError::NONE
2997 && aCell.mpFormula->IsValue())
2999 value = aCell.mpFormula->GetValue();
3001 else
3002 valid = false;
3003 if(valid)
3005 for (const auto& rItem : rItems)
3007 // For speed don't bother comparing approximately here, usually there either
3008 // will be an exact match or it wouldn't match anyway.
3009 if (rItem.meType == ScQueryEntry::ByValue
3010 && value == rItem.mfVal)
3012 return std::make_pair(true, true);
3017 const svl::SharedString* cellSharedString = nullptr;
3018 OUString cellString;
3019 bool cellStringSet = false;
3020 const bool bFastCompareByString = aEval.isFastCompareByString(rEntry);
3021 if(rEntry.eOp == SC_EQUAL && rItems.size() >= 10 && bFastCompareByString)
3023 // The same as above but for strings. Try to optimize the case when
3024 // it's a svl::SharedString comparison. That happens when SC_EQUAL is used
3025 // and simple matching is used, see compareByString()
3026 if(!cellStringSet)
3028 cellString = aEval.getCellString(aCell, nRow, rEntry, pContext, &cellSharedString);
3029 cellStringSet = true;
3031 // For ScQueryEntry::ByString check that the cell is represented by a shared string,
3032 // which means it's either a string cell or a formula error. This is not as
3033 // generous as isQueryByString() but it should be enough and better be safe.
3034 if(cellSharedString != nullptr)
3036 if (rParam.bCaseSens)
3038 for (const auto& rItem : rItems)
3040 if (rItem.meType == ScQueryEntry::ByString
3041 && cellSharedString->getData() == rItem.maString.getData())
3043 return std::make_pair(true, true);
3047 else
3049 for (const auto& rItem : rItems)
3051 if (rItem.meType == ScQueryEntry::ByString
3052 && cellSharedString->getDataIgnoreCase() == rItem.maString.getDataIgnoreCase())
3054 return std::make_pair(true, true);
3060 // Generic handling.
3061 for (const auto& rItem : rItems)
3063 if (rItem.meType == ScQueryEntry::ByTextColor)
3065 std::pair<bool, bool> aThisRes
3066 = aEval.compareByTextColor(nCol, nRow, nTab, rItem);
3067 aRes.first |= aThisRes.first;
3068 aRes.second |= aThisRes.second;
3070 else if (rItem.meType == ScQueryEntry::ByBackgroundColor)
3072 std::pair<bool,bool> aThisRes =
3073 aEval.compareByBackgroundColor(nCol, nRow, nTab, rItem);
3074 aRes.first |= aThisRes.first;
3075 aRes.second |= aThisRes.second;
3077 else if (QueryEvaluator::isQueryByValue(rItem, aCell))
3079 std::pair<bool,bool> aThisRes =
3080 aEval.compareByValue(aCell, nCol, nRow, rEntry, rItem, pContext);
3081 aRes.first |= aThisRes.first;
3082 aRes.second |= aThisRes.second;
3084 else if (QueryEvaluator::isQueryByString(rEntry, rItem, aCell))
3086 if(!cellStringSet)
3088 cellString = aEval.getCellString(aCell, nRow, rEntry, pContext, &cellSharedString);
3089 cellStringSet = true;
3091 std::pair<bool,bool> aThisRes;
3092 if( cellSharedString && bFastCompareByString ) // fast
3093 aThisRes = aEval.compareByString<true>(rEntry, rItem, cellSharedString, nullptr);
3094 else if( cellSharedString )
3095 aThisRes = aEval.compareByString(rEntry, rItem, cellSharedString, nullptr);
3096 else
3097 aThisRes = aEval.compareByString(rEntry, rItem, nullptr, &cellString);
3098 aRes.first |= aThisRes.first;
3099 aRes.second |= aThisRes.second;
3101 else if (rParam.mbRangeLookup)
3103 std::pair<bool,bool> aThisRes =
3104 QueryEvaluator::compareByRangeLookup(aCell, rEntry, rItem);
3105 aRes.first |= aThisRes.first;
3106 aRes.second |= aThisRes.second;
3109 if (aRes.first && (aRes.second || pbTestEqualCondition == nullptr))
3110 break;
3112 return aRes;
3117 bool ScTable::ValidQuery(
3118 SCROW nRow, const ScQueryParam& rParam, const ScRefCellValue* pCell, bool* pbTestEqualCondition,
3119 const ScInterpreterContext* pContext, sc::TableColumnBlockPositionSet* pBlockPos,
3120 ValidQueryCache* pValidQueryCache)
3122 if (!rParam.GetEntry(0).bDoQuery)
3123 return true;
3125 //---------------------------------------------------------------
3127 const SCSIZE nFixedBools = 32;
3128 bool aBool[nFixedBools];
3129 bool aTest[nFixedBools];
3130 SCSIZE nEntryCount = rParam.GetEntryCount();
3131 bool* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new bool[nEntryCount] );
3132 bool* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new bool[nEntryCount] );
3134 tools::Long nPos = -1;
3135 QueryEvaluator aEval(rDocument, *this, rParam, pbTestEqualCondition != nullptr, pValidQueryCache);
3136 ScQueryParam::const_iterator it, itBeg = rParam.begin(), itEnd = rParam.end();
3137 for (it = itBeg; it != itEnd && (*it)->bDoQuery; ++it)
3139 const ScQueryEntry& rEntry = **it;
3141 // Short-circuit the test at the end of the loop - if this is SC_AND
3142 // and the previous value is false, this value will not be needed.
3143 // Disable this if pbTestEqualCondition is present as that one may get set
3144 // even if the result is false (that also means pTest doesn't need to be
3145 // handled here).
3146 if (rEntry.eConnect == SC_AND && pbTestEqualCondition == nullptr
3147 && nPos != -1 && !pPasst[nPos])
3149 continue;
3152 SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
3154 // We can only handle one single direct query passed as a known pCell,
3155 // subsequent queries have to obtain the cell.
3156 ScRefCellValue aCell;
3157 if(pCell && it == itBeg)
3158 aCell = *pCell;
3159 else if( pBlockPos )
3160 { // hinted mdds access
3161 ScColumn* column = FetchColumn(nCol);
3162 aCell = column->GetCellValue(*pBlockPos->getBlockPosition( nCol ), nRow);
3164 else
3165 aCell = GetCellValue(nCol, nRow);
3167 std::pair<bool,bool> aRes = validQueryProcessEntry(nRow, nCol, nTab, rParam, aCell,
3168 pbTestEqualCondition, pContext, aEval, rEntry);
3170 if (nPos == -1)
3172 nPos++;
3173 pPasst[nPos] = aRes.first;
3174 pTest[nPos] = aRes.second;
3176 else
3178 if (rEntry.eConnect == SC_AND)
3180 pPasst[nPos] = pPasst[nPos] && aRes.first;
3181 pTest[nPos] = pTest[nPos] && aRes.second;
3183 else
3185 nPos++;
3186 pPasst[nPos] = aRes.first;
3187 pTest[nPos] = aRes.second;
3192 for ( tools::Long j=1; j <= nPos; j++ )
3194 pPasst[0] = pPasst[0] || pPasst[j];
3195 pTest[0] = pTest[0] || pTest[j];
3198 bool bRet = pPasst[0];
3199 if ( pPasst != &aBool[0] )
3200 delete [] pPasst;
3201 if ( pbTestEqualCondition )
3202 *pbTestEqualCondition = pTest[0];
3203 if ( pTest != &aTest[0] )
3204 delete [] pTest;
3206 return bRet;
3209 void ScTable::TopTenQuery( ScQueryParam& rParam )
3211 bool bSortCollatorInitialized = false;
3212 SCSIZE nEntryCount = rParam.GetEntryCount();
3213 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
3214 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
3215 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
3217 ScQueryEntry& rEntry = rParam.GetEntry(i);
3218 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
3220 for (ScQueryEntry::Item& rItem : rItems)
3222 switch (rEntry.eOp)
3224 case SC_TOPVAL:
3225 case SC_BOTVAL:
3226 case SC_TOPPERC:
3227 case SC_BOTPERC:
3229 ScSortParam aLocalSortParam(rParam, static_cast<SCCOL>(rEntry.nField));
3230 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
3231 if (!bSortCollatorInitialized)
3233 bSortCollatorInitialized = true;
3234 InitSortCollator(aLocalSortParam);
3236 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
3237 DecoladeRow(pArray.get(), nRow1, rParam.nRow2);
3238 QuickSort(pArray.get(), nRow1, rParam.nRow2);
3239 std::unique_ptr<ScSortInfo[]> const& ppInfo = pArray->GetFirstArray();
3240 SCSIZE nValidCount = nCount;
3241 // Don't count note or blank cells, they are sorted to the end
3242 while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.isEmpty())
3243 nValidCount--;
3244 // Don't count Strings, they are between Value and blank
3245 while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.hasString())
3246 nValidCount--;
3247 if (nValidCount > 0)
3249 if (rItem.meType == ScQueryEntry::ByString)
3250 { // by string ain't going to work
3251 rItem.meType = ScQueryEntry::ByValue;
3252 rItem.mfVal = 10; // 10 and 10% respectively
3254 SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
3255 SCSIZE nOffset = 0;
3256 switch (rEntry.eOp)
3258 case SC_TOPVAL:
3260 rEntry.eOp = SC_GREATER_EQUAL;
3261 if (nVal > nValidCount)
3262 nVal = nValidCount;
3263 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
3265 break;
3266 case SC_BOTVAL:
3268 rEntry.eOp = SC_LESS_EQUAL;
3269 if (nVal > nValidCount)
3270 nVal = nValidCount;
3271 nOffset = nVal - 1; // 1 <= nVal <= nValidCount
3273 break;
3274 case SC_TOPPERC:
3276 rEntry.eOp = SC_GREATER_EQUAL;
3277 if (nVal > 100)
3278 nVal = 100;
3279 nOffset = nValidCount - (nValidCount * nVal / 100);
3280 if (nOffset >= nValidCount)
3281 nOffset = nValidCount - 1;
3283 break;
3284 case SC_BOTPERC:
3286 rEntry.eOp = SC_LESS_EQUAL;
3287 if (nVal > 100)
3288 nVal = 100;
3289 nOffset = (nValidCount * nVal / 100);
3290 if (nOffset >= nValidCount)
3291 nOffset = nValidCount - 1;
3293 break;
3294 default:
3296 // added to avoid warnings
3299 ScRefCellValue aCell = ppInfo[nOffset].maCell;
3300 if (aCell.hasNumeric())
3301 rItem.mfVal = aCell.getValue();
3302 else
3304 OSL_FAIL("TopTenQuery: pCell no ValueData");
3305 rEntry.eOp = SC_GREATER_EQUAL;
3306 rItem.mfVal = 0;
3309 else
3311 rEntry.eOp = SC_GREATER_EQUAL;
3312 rItem.meType = ScQueryEntry::ByValue;
3313 rItem.mfVal = 0;
3316 break;
3317 default:
3319 // added to avoid warnings
3324 if ( bSortCollatorInitialized )
3325 DestroySortCollator();
3328 namespace {
3330 bool CanOptimizeQueryStringToNumber( const SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat )
3332 // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
3333 // The problem with this optimization is that the autofilter dialog apparently converts
3334 // the value to text and then converts that back to a number for filtering.
3335 // If that leads to any change of value (such as when time is rounded to seconds),
3336 // even matching values will be filtered out. Therefore query by value only for formats
3337 // where no such change should occur.
3338 if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex))
3340 switch(pEntry->GetType())
3342 case SvNumFormatType::NUMBER:
3343 case SvNumFormatType::FRACTION:
3344 case SvNumFormatType::SCIENTIFIC:
3345 return true;
3346 case SvNumFormatType::DATE:
3347 case SvNumFormatType::DATETIME:
3348 bDateFormat = true;
3349 break;
3350 default:
3351 break;
3354 return false;
3357 class PrepareQueryItem
3359 const ScDocument& mrDoc;
3360 const bool mbRoundForFilter;
3361 public:
3362 explicit PrepareQueryItem(const ScDocument& rDoc, bool bRoundForFilter) :
3363 mrDoc(rDoc), mbRoundForFilter(bRoundForFilter) {}
3365 void operator() (ScQueryEntry::Item& rItem)
3367 rItem.mbRoundForFilter = mbRoundForFilter;
3369 if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
3370 return;
3372 sal_uInt32 nIndex = 0;
3373 bool bNumber = mrDoc.GetFormatTable()->
3374 IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
3376 // Advanced Filter creates only ByString queries that need to be
3377 // converted to ByValue if appropriate. rItem.mfVal now holds the value
3378 // if bNumber==true.
3380 if (rItem.meType == ScQueryEntry::ByString)
3382 bool bDateFormat = false;
3383 if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat ))
3384 rItem.meType = ScQueryEntry::ByValue;
3385 if (!bDateFormat)
3386 return;
3389 // Double-check if the query by date is really appropriate.
3391 if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
3393 const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
3394 if (pEntry)
3396 SvNumFormatType nNumFmtType = pEntry->GetType();
3397 if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME))
3398 rItem.meType = ScQueryEntry::ByValue; // not a date only
3399 else
3400 rItem.meType = ScQueryEntry::ByDate; // date only
3402 else
3403 rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
3405 else
3406 rItem.meType = ScQueryEntry::ByValue; // not a date
3410 void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, bool bRoundForFilter )
3412 bool bTopTen = false;
3413 SCSIZE nEntryCount = rParam.GetEntryCount();
3415 for ( SCSIZE i = 0; i < nEntryCount; ++i )
3417 ScQueryEntry& rEntry = rParam.GetEntry(i);
3418 if (!rEntry.bDoQuery)
3419 continue;
3421 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
3422 std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc, bRoundForFilter));
3424 if ( !bTopTen )
3426 switch ( rEntry.eOp )
3428 case SC_TOPVAL:
3429 case SC_BOTVAL:
3430 case SC_TOPPERC:
3431 case SC_BOTPERC:
3433 bTopTen = true;
3435 break;
3436 default:
3443 if ( bTopTen )
3445 pTab->TopTenQuery( rParam );
3451 void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
3453 lcl_PrepareQuery(&rDocument, this, rQueryParam, false);
3456 SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
3458 ScQueryParam aParam( rParamOrg );
3459 typedef std::unordered_set<OUString> StrSetType;
3460 StrSetType aStrSet;
3462 bool bStarted = false;
3463 bool bOldResult = true;
3464 SCROW nOldStart = 0;
3465 SCROW nOldEnd = 0;
3467 SCSIZE nCount = 0;
3468 SCROW nOutRow = 0;
3469 SCROW nHeader = aParam.bHasHeader ? 1 : 0;
3471 lcl_PrepareQuery(&rDocument, this, aParam, true);
3473 if (!aParam.bInplace)
3475 nOutRow = aParam.nDestRow + nHeader;
3476 if (nHeader > 0)
3477 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
3478 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
3481 sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access
3482 ValidQueryCache validQueryCache;
3484 SCROW nRealRow2 = aParam.nRow2;
3485 for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
3487 bool bResult; // Filter result
3488 bool bValid = ValidQuery(j, aParam, nullptr, nullptr, nullptr, &blockPos, &validQueryCache);
3489 if (!bValid && bKeepSub) // Keep subtotals
3491 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
3493 ScRefCellValue aCell = GetCellValue(nCol, j);
3494 if (aCell.meType != CELLTYPE_FORMULA)
3495 continue;
3497 if (!aCell.mpFormula->IsSubTotal())
3498 continue;
3500 if (RefVisible(aCell.mpFormula))
3501 bValid = true;
3504 if (bValid)
3506 if (aParam.bDuplicate)
3507 bResult = true;
3508 else
3510 OUStringBuffer aStr;
3511 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
3513 OUString aCellStr = GetString(k, j);
3514 aStr.append(aCellStr + u"\x0001");
3517 bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted.
3520 else
3521 bResult = false;
3523 if (aParam.bInplace)
3525 if (bResult == bOldResult && bStarted)
3526 nOldEnd = j;
3527 else
3529 if (bStarted)
3530 DBShowRows(nOldStart,nOldEnd, bOldResult);
3531 nOldStart = nOldEnd = j;
3532 bOldResult = bResult;
3534 bStarted = true;
3536 else
3538 if (bResult)
3540 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
3541 if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints
3542 blockPos.invalidate();
3543 ++nOutRow;
3546 if (bResult)
3547 ++nCount;
3550 if (aParam.bInplace && bStarted)
3551 DBShowRows(nOldStart,nOldEnd, bOldResult);
3553 if (aParam.bInplace)
3554 SetDrawPageSize();
3556 return nCount;
3559 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3561 bool bValid = true;
3562 std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]);
3563 OUString aCellStr;
3564 SCCOL nCol = nCol1;
3565 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3566 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3567 SCROW nDBRow1 = rQueryParam.nRow1;
3568 SCCOL nDBCol2 = rQueryParam.nCol2;
3569 // First row must be column headers
3570 while (bValid && (nCol <= nCol2))
3572 OUString aQueryStr = GetUpperCellString(nCol, nRow1);
3573 bool bFound = false;
3574 SCCOL i = rQueryParam.nCol1;
3575 while (!bFound && (i <= nDBCol2))
3577 if ( nTab == nDBTab )
3578 aCellStr = GetUpperCellString(i, nDBRow1);
3579 else
3580 aCellStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
3581 bFound = (aCellStr == aQueryStr);
3582 if (!bFound) i++;
3584 if (bFound)
3585 pFields[nCol - nCol1] = i;
3586 else
3587 bValid = false;
3588 nCol++;
3590 if (bValid)
3592 sal_uLong nVisible = 0;
3593 for ( nCol=nCol1; nCol<=nCol2; nCol++ )
3594 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
3596 if ( nVisible > SCSIZE_MAX / sizeof(void*) )
3598 OSL_FAIL("too many filter criteria");
3599 nVisible = 0;
3602 SCSIZE nNewEntries = nVisible;
3603 rQueryParam.Resize( nNewEntries );
3605 SCSIZE nIndex = 0;
3606 SCROW nRow = nRow1 + 1;
3607 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
3608 while (nRow <= nRow2)
3610 nCol = nCol1;
3611 while (nCol <= nCol2)
3613 aCellStr = GetInputString( nCol, nRow );
3614 if (!aCellStr.isEmpty())
3616 if (nIndex < nNewEntries)
3618 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
3619 rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
3620 nIndex++;
3621 if (nIndex < nNewEntries)
3622 rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
3624 else
3625 bValid = false;
3627 nCol++;
3629 nRow++;
3630 if (nIndex < nNewEntries)
3631 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
3634 return bValid;
3637 bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3639 // A valid StarQuery must be at least 4 columns wide. To be precise it
3640 // should be exactly 4 columns ...
3641 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
3642 // column Excel style query range immediately left to itself would result
3643 // in a circular reference when the field name or operator or value (first
3644 // to third query range column) is obtained (#i58354#). Furthermore, if the
3645 // range wasn't sufficiently specified data changes wouldn't flag formula
3646 // cells for recalculation.
3647 if (nCol2 - nCol1 < 3)
3648 return false;
3650 bool bValid;
3651 OUString aCellStr;
3652 SCSIZE nIndex = 0;
3653 SCROW nRow = nRow1;
3654 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3655 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3656 SCROW nDBRow1 = rQueryParam.nRow1;
3657 SCCOL nDBCol2 = rQueryParam.nCol2;
3659 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
3660 rQueryParam.Resize( nNewEntries );
3661 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
3665 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
3667 bValid = false;
3668 // First column AND/OR
3669 if (nIndex > 0)
3671 aCellStr = GetUpperCellString(nCol1, nRow);
3672 if ( aCellStr == ScResId(STR_TABLE_AND) )
3674 rEntry.eConnect = SC_AND;
3675 bValid = true;
3677 else if ( aCellStr == ScResId(STR_TABLE_OR) )
3679 rEntry.eConnect = SC_OR;
3680 bValid = true;
3683 // Second column field name
3684 if ((nIndex < 1) || bValid)
3686 bool bFound = false;
3687 aCellStr = GetUpperCellString(nCol1 + 1, nRow);
3688 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
3690 OUString aFieldStr;
3691 if ( nTab == nDBTab )
3692 aFieldStr = GetUpperCellString(i, nDBRow1);
3693 else
3694 aFieldStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
3695 bFound = (aCellStr == aFieldStr);
3696 if (bFound)
3698 rEntry.nField = i;
3699 bValid = true;
3701 else
3702 bValid = false;
3705 // Third column operator =<>...
3706 if (bValid)
3708 aCellStr = GetUpperCellString(nCol1 + 2, nRow);
3709 if (aCellStr.startsWith("<"))
3711 if (aCellStr[1] == '>')
3712 rEntry.eOp = SC_NOT_EQUAL;
3713 else if (aCellStr[1] == '=')
3714 rEntry.eOp = SC_LESS_EQUAL;
3715 else
3716 rEntry.eOp = SC_LESS;
3718 else if (aCellStr.startsWith(">"))
3720 if (aCellStr[1] == '=')
3721 rEntry.eOp = SC_GREATER_EQUAL;
3722 else
3723 rEntry.eOp = SC_GREATER;
3725 else if (aCellStr.startsWith("="))
3726 rEntry.eOp = SC_EQUAL;
3729 // Fourth column values
3730 if (bValid)
3732 OUString aStr = GetString(nCol1 + 3, nRow);
3733 rEntry.GetQueryItem().maString = rPool.intern(aStr);
3734 rEntry.bDoQuery = true;
3736 nIndex++;
3737 nRow++;
3739 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
3740 return bValid;
3743 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3745 SCSIZE i, nCount;
3746 PutInOrder(nCol1, nCol2);
3747 PutInOrder(nRow1, nRow2);
3749 nCount = rQueryParam.GetEntryCount();
3750 for (i=0; i < nCount; i++)
3751 rQueryParam.GetEntry(i).Clear();
3753 // Standard query table
3754 bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3755 // Excel Query table
3756 if (!bValid)
3757 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3759 SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
3760 nCount = rQueryParam.GetEntryCount();
3761 if (bValid)
3763 // query type must be set
3764 for (i=0; i < nCount; i++)
3766 ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem();
3767 sal_uInt32 nIndex = 0;
3768 bool bNumber = pFormatter->IsNumberFormat(
3769 rItem.maString.getString(), nIndex, rItem.mfVal);
3770 bool bDateFormat = false;
3771 rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat )
3772 ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString);
3775 else
3777 for (i=0; i < nCount; i++)
3778 rQueryParam.GetEntry(i).Clear();
3780 return bValid;
3783 bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
3785 if (nStartRow == nEndRow)
3786 // Assume only data.
3787 /* XXX NOTE: previous behavior still checked this one row and could
3788 * evaluate it has header row, but that doesn't make much sense. */
3789 return false;
3791 if (nStartCol == nEndCol)
3793 CellType eFirstCellType = GetCellType(nStartCol, nStartRow);
3794 CellType eSecondCellType = GetCellType(nStartCol, nStartRow+1);
3795 return ((eFirstCellType == CELLTYPE_STRING || eFirstCellType == CELLTYPE_EDIT) &&
3796 (eSecondCellType != CELLTYPE_STRING && eSecondCellType != CELLTYPE_EDIT));
3799 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3801 CellType eType = GetCellType( nCol, nStartRow );
3802 // Any non-text cell in first row => not headers.
3803 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3804 return false;
3807 // First row all text cells, any non-text cell in second row => headers.
3808 SCROW nTestRow = nStartRow + 1;
3809 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3811 CellType eType = GetCellType( nCol, nTestRow );
3812 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3813 return true;
3816 // Also second row all text cells => first row not headers.
3817 return false;
3820 bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
3822 if (nStartCol == nEndCol)
3823 // Assume only data.
3824 /* XXX NOTE: previous behavior still checked this one column and could
3825 * evaluate it has header column, but that doesn't make much sense. */
3826 return false;
3828 if (nStartRow == nEndRow)
3830 CellType eFirstCellType = GetCellType(nStartCol, nStartRow);
3831 CellType eSecondCellType = GetCellType(nStartCol+1, nStartRow);
3832 return ((eFirstCellType == CELLTYPE_STRING || eFirstCellType == CELLTYPE_EDIT) &&
3833 (eSecondCellType != CELLTYPE_STRING && eSecondCellType != CELLTYPE_EDIT));
3836 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3838 CellType eType = GetCellType( nStartCol, nRow );
3839 // Any non-text cell in first column => not headers.
3840 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3841 return false;
3844 // First column all text cells, any non-text cell in second column => headers.
3845 SCCOL nTestCol = nStartCol + 1;
3846 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3848 CellType eType = GetCellType( nRow, nTestCol );
3849 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3850 return true;
3853 // Also second column all text cells => first column not headers.
3854 return false;
3857 void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering )
3859 if (nCol >= aCol.size())
3860 return;
3862 sc::ColumnBlockConstPosition aBlockPos;
3863 aCol[nCol].InitBlockPosition(aBlockPos);
3864 aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, bFiltering);
3867 void ScTable::GetFilteredFilterEntries(
3868 SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering )
3870 if (nCol >= aCol.size())
3871 return;
3873 sc::ColumnBlockConstPosition aBlockPos;
3874 aCol[nCol].InitBlockPosition(aBlockPos);
3876 // remove the entry for this column from the query parameter
3877 ScQueryParam aParam( rParam );
3878 aParam.RemoveEntryByField(nCol);
3880 lcl_PrepareQuery(&rDocument, this, aParam, true);
3881 for ( SCROW j = nRow1; j <= nRow2; ++j )
3883 if (ValidQuery(j, aParam))
3885 aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering);
3890 bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings)
3892 return aCol[nCol].GetDataEntries( nRow, rStrings);
3895 sal_uLong ScTable::GetCellCount() const
3897 sal_uLong nCellCount = 0;
3899 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3900 nCellCount += aCol[nCol].GetCellCount();
3902 return nCellCount;
3905 sal_uLong ScTable::GetWeightedCount() const
3907 sal_uLong nCellCount = 0;
3909 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3910 nCellCount += aCol[nCol].GetWeightedCount();
3912 return nCellCount;
3915 sal_uLong ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
3917 sal_uLong nCellCount = 0;
3919 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3920 nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow);
3922 return nCellCount;
3925 sal_uLong ScTable::GetCodeCount() const
3927 sal_uLong nCodeCount = 0;
3929 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3930 if ( aCol[nCol].GetCellCount() )
3931 nCodeCount += aCol[nCol].GetCodeCount();
3933 return nCodeCount;
3936 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
3937 SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
3939 if ( IsColValid( nCol ) )
3940 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
3941 else
3942 return 0;
3945 sal_Int32 ScTable::GetMaxNumberStringLen(
3946 sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
3948 if ( IsColValid( nCol ) )
3949 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
3950 else
3951 return 0;
3954 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
3956 ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab );
3957 ScRange aMarkArea( ScAddress::UNINITIALIZED );
3958 if (rMark.IsMultiMarked())
3959 rMark.GetMultiMarkArea( aMarkArea );
3960 else if (rMark.IsMarked())
3961 rMark.GetMarkArea( aMarkArea );
3962 else
3964 assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
3965 aMarkArea.aStart.SetCol(0);
3966 aMarkArea.aEnd.SetCol(rDocument.MaxCol());
3968 const SCCOL nStartCol = aMarkArea.aStart.Col();
3969 const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col());
3970 for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol)
3972 if (mpColFlags && ColHidden(nCol))
3973 continue;
3975 aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3979 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */