tdf#76258 Various fixes for color filter
[LibreOffice.git] / sc / source / core / data / table3.cxx
blob54c0850931eeb303c2a15a8e7bced063cf9c0575
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/zforlist.hxx>
26 #include <svl/zformat.hxx>
27 #include <unotools/charclass.hxx>
28 #include <unotools/collatorwrapper.hxx>
29 #include <stdlib.h>
30 #include <unotools/transliterationwrapper.hxx>
31 #include <com/sun/star/i18n/KParseTokens.hpp>
32 #include <com/sun/star/i18n/KParseType.hpp>
33 #include <sal/log.hxx>
34 #include <osl/diagnose.h>
36 #include <refdata.hxx>
37 #include <table.hxx>
38 #include <scitems.hxx>
39 #include <formulacell.hxx>
40 #include <document.hxx>
41 #include <globstr.hrc>
42 #include <scresid.hxx>
43 #include <global.hxx>
44 #include <stlpool.hxx>
45 #include <patattr.hxx>
46 #include <subtotal.hxx>
47 #include <docoptio.hxx>
48 #include <markdata.hxx>
49 #include <rangelst.hxx>
50 #include <userlist.hxx>
51 #include <progress.hxx>
52 #include <cellform.hxx>
53 #include <queryparam.hxx>
54 #include <queryentry.hxx>
55 #include <subtotalparam.hxx>
56 #include <docpool.hxx>
57 #include <cellvalue.hxx>
58 #include <tokenarray.hxx>
59 #include <mtvcellfunc.hxx>
60 #include <columnspanset.hxx>
61 #include <fstalgorithm.hxx>
62 #include <listenercontext.hxx>
63 #include <sharedformula.hxx>
64 #include <stlsheet.hxx>
65 #include <refhint.hxx>
66 #include <listenerquery.hxx>
67 #include <bcaslot.hxx>
68 #include <reordermap.hxx>
69 #include <drwlayer.hxx>
71 #include <svl/sharedstringpool.hxx>
73 #include <memory>
74 #include <set>
75 #include <unordered_set>
76 #include <vector>
77 #include <mdds/flat_segment_tree.hpp>
79 using namespace ::com::sun::star;
81 namespace naturalsort {
83 using namespace ::com::sun::star::i18n;
85 /** Splits a given string into three parts: the prefix, number string, and
86 the suffix.
88 @param sWhole
89 Original string to be split into pieces
91 @param sPrefix
92 Prefix string that consists of the part before the first number token.
93 If no number was found, sPrefix is unchanged.
95 @param sSuffix
96 String after the last number token. This may still contain number strings.
97 If no number was found, sSuffix is unchanged.
99 @param fNum
100 Number converted from the middle number string
101 If no number was found, fNum is unchanged.
103 @return Returns TRUE if a numeral element is found in a given string, or
104 FALSE if no numeral element is found.
106 static bool SplitString( const OUString &sWhole,
107 OUString &sPrefix, OUString &sSuffix, double &fNum )
109 // Get prefix element, search for any digit and stop.
110 sal_Int32 nPos = 0;
111 while (nPos < sWhole.getLength())
113 const sal_uInt16 nType = ScGlobal::getCharClassPtr()->getCharacterType( sWhole, nPos);
114 if (nType & KCharacterType::DIGIT)
115 break;
116 sWhole.iterateCodePoints( &nPos );
119 // Return FALSE if no numeral element is found
120 if ( nPos == sWhole.getLength() )
121 return false;
123 // Get numeral element
124 const OUString& sUser = ScGlobal::getLocaleDataPtr()->getNumDecimalSep();
125 ParseResult aPRNum = ScGlobal::getCharClassPtr()->parsePredefinedToken(
126 KParseType::ANY_NUMBER, sWhole, nPos,
127 KParseTokens::ANY_NUMBER, "", KParseTokens::ANY_NUMBER, sUser );
129 if ( aPRNum.EndPos == nPos )
131 SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
132 nPos << " : " << sWhole);
133 return false;
136 sPrefix = sWhole.copy( 0, nPos );
137 fNum = aPRNum.Value;
138 sSuffix = sWhole.copy( aPRNum.EndPos );
140 return true;
143 /** Naturally compares two given strings.
145 This is the main function that should be called externally. It returns
146 either 1, 0, or -1 depending on the comparison result of given two strings.
148 @param sInput1
149 Input string 1
151 @param sInput2
152 Input string 2
154 @param bCaseSens
155 Boolean value for case sensitivity
157 @param pData
158 Pointer to user defined sort list
160 @param pCW
161 Pointer to collator wrapper for normal string comparison
163 @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
164 sInput2 is greater.
166 static short Compare( const OUString &sInput1, const OUString &sInput2,
167 const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
169 OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
173 double nNum1, nNum2;
174 bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
175 bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
177 short nPreRes; // Prefix comparison result
178 if ( pData )
180 if ( bCaseSens )
182 if ( !bNumFound1 || !bNumFound2 )
183 return static_cast<short>(pData->Compare( sStr1, sStr2 ));
184 else
185 nPreRes = pData->Compare( sPre1, sPre2 );
187 else
189 if ( !bNumFound1 || !bNumFound2 )
190 return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
191 else
192 nPreRes = pData->ICompare( sPre1, sPre2 );
195 else
197 if ( !bNumFound1 || !bNumFound2 )
198 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
199 else
200 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
203 // Prefix strings differ. Return immediately.
204 if ( nPreRes != 0 ) return nPreRes;
206 if ( nNum1 != nNum2 )
208 if ( nNum1 < nNum2 ) return -1;
209 return (nNum1 > nNum2) ? 1 : 0;
212 // The prefix and the first numerical elements are equal, but the suffix
213 // strings may still differ. Stay in the loop.
215 sStr1 = sSuf1;
216 sStr2 = sSuf2;
218 } while (true);
220 return 0;
225 namespace {
227 struct ScSortInfo final
229 ScRefCellValue maCell;
230 SCCOLROW nOrg;
235 class ScSortInfoArray
237 public:
239 struct Cell
241 ScRefCellValue maCell;
242 const sc::CellTextAttr* mpAttr;
243 const ScPostIt* mpNote;
244 std::vector<SdrObject*> maDrawObjects;
245 const ScPatternAttr* mpPattern;
247 Cell() : mpAttr(nullptr), mpNote(nullptr), maDrawObjects(), mpPattern(nullptr) {}
250 struct Row
252 std::vector<Cell> maCells;
254 bool mbHidden:1;
255 bool mbFiltered:1;
257 explicit Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
260 typedef std::vector<Row> RowsType;
262 private:
263 std::unique_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
265 std::vector<std::unique_ptr<ScSortInfo[]>> mvppInfo;
266 SCCOLROW nStart;
267 SCCOLROW mnLastIndex; /// index of last non-empty cell position.
269 std::vector<SCCOLROW> maOrderIndices;
270 bool mbKeepQuery;
271 bool mbUpdateRefs;
273 public:
274 ScSortInfoArray(const ScSortInfoArray&) = delete;
275 const ScSortInfoArray& operator=(const ScSortInfoArray&) = delete;
277 ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
278 mvppInfo(nSorts),
279 nStart( nInd1 ),
280 mnLastIndex(nInd2),
281 mbKeepQuery(false),
282 mbUpdateRefs(false)
284 SCSIZE nCount( nInd2 - nInd1 + 1 );
285 if (nSorts)
287 for ( sal_uInt16 nSort = 0; nSort < nSorts; nSort++ )
289 mvppInfo[nSort].reset(new ScSortInfo[nCount]);
293 for (size_t i = 0; i < nCount; ++i)
294 maOrderIndices.push_back(i+nStart);
297 void SetKeepQuery( bool b ) { mbKeepQuery = b; }
299 bool IsKeepQuery() const { return mbKeepQuery; }
301 void SetUpdateRefs( bool b ) { mbUpdateRefs = b; }
303 bool IsUpdateRefs() const { return mbUpdateRefs; }
306 * Call this only during normal sorting, not from reordering.
308 std::unique_ptr<ScSortInfo[]> const & GetFirstArray() const
310 return mvppInfo[0];
314 * Call this only during normal sorting, not from reordering.
316 ScSortInfo & Get( sal_uInt16 nSort, SCCOLROW nInd )
318 return mvppInfo[nSort][ nInd - nStart ];
322 * Call this only during normal sorting, not from reordering.
324 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
326 if (nInd1 == nInd2) // avoid self-move-assign
327 return;
328 SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
329 SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
330 for ( sal_uInt16 nSort = 0; nSort < static_cast<sal_uInt16>(mvppInfo.size()); nSort++ )
332 auto & ppInfo = mvppInfo[nSort];
333 std::swap(ppInfo[n1], ppInfo[n2]);
336 std::swap(maOrderIndices[n1], maOrderIndices[n2]);
338 if (mpRows)
340 // Swap rows in data table.
341 RowsType& rRows = *mpRows;
342 std::swap(rRows[n1], rRows[n2]);
346 void SetOrderIndices( const std::vector<SCCOLROW>& rIndices )
348 maOrderIndices = rIndices;
352 * @param rIndices indices are actual row positions on the sheet, not an
353 * offset from the top row.
355 void ReorderByRow( const std::vector<SCCOLROW>& rIndices )
357 if (!mpRows)
358 return;
360 RowsType& rRows = *mpRows;
362 std::vector<SCCOLROW> aOrderIndices2;
363 aOrderIndices2.reserve(rIndices.size());
365 RowsType aRows2;
366 aRows2.reserve(rRows.size());
368 for (const auto& rIndex : rIndices)
370 size_t nPos = rIndex - nStart; // switch to an offset to top row.
371 aRows2.push_back(rRows[nPos]);
372 aOrderIndices2.push_back(maOrderIndices[nPos]);
375 rRows.swap(aRows2);
376 maOrderIndices.swap(aOrderIndices2);
379 sal_uInt16 GetUsedSorts() const { return mvppInfo.size(); }
381 SCCOLROW GetStart() const { return nStart; }
382 SCCOLROW GetLast() const { return mnLastIndex; }
384 const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; }
386 RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
388 mpRows.reset(new RowsType);
389 mpRows->resize(nRowSize, Row(nColSize));
390 return *mpRows;
393 RowsType* GetDataRows()
395 return mpRows.get();
399 namespace {
401 void initDataRows(
402 ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols,
403 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
404 bool bPattern, bool bHiddenFiltered )
406 // Fill row-wise data table.
407 ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
409 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
411 ScColumn& rCol = rCols[nCol];
413 // Skip reordering of cell formats if the whole span is on the same pattern entry.
414 bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
416 sc::ColumnBlockConstPosition aBlockPos;
417 rCol.InitBlockPosition(aBlockPos);
418 std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects;
419 ScDrawLayer* pDrawLayer = rTab.GetDoc().GetDrawLayer();
420 if (pDrawLayer)
421 aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2);
423 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
425 ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
426 ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
427 rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
428 rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
429 rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
430 if (pDrawLayer)
431 rCell.maDrawObjects = aRowDrawObjects[nRow];
433 if (!bUniformPattern && bPattern)
434 rCell.mpPattern = rCol.GetPattern(nRow);
438 if (bHiddenFiltered)
440 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
442 ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
443 rRow.mbHidden = rTab.RowHidden(nRow);
444 rRow.mbFiltered = rTab.RowFiltered(nRow);
451 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
453 std::unique_ptr<ScSortInfoArray> pArray;
455 if (rParam.mbByRow)
457 // Create a sort info array with just the data table.
458 SCROW nRow1 = rParam.maSortRange.aStart.Row();
459 SCROW nRow2 = rParam.maSortRange.aEnd.Row();
460 SCCOL nCol1 = rParam.maSortRange.aStart.Col();
461 SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
463 pArray.reset(new ScSortInfoArray(0, nRow1, nRow2));
464 pArray->SetKeepQuery(rParam.mbHiddenFiltered);
465 pArray->SetUpdateRefs(rParam.mbUpdateRefs);
467 initDataRows(
468 *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2,
469 rParam.mbPattern, rParam.mbHiddenFiltered);
471 else
473 SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
474 SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
476 pArray.reset(new ScSortInfoArray(0, nCol1, nCol2));
477 pArray->SetKeepQuery(rParam.mbHiddenFiltered);
478 pArray->SetUpdateRefs(rParam.mbUpdateRefs);
481 return pArray;
484 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray(
485 const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
486 bool bKeepQuery, bool bUpdateRefs )
488 sal_uInt16 nUsedSorts = 1;
489 while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
490 nUsedSorts++;
491 std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ));
492 pArray->SetKeepQuery(bKeepQuery);
493 pArray->SetUpdateRefs(bUpdateRefs);
495 if ( rSortParam.bByRow )
497 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
499 SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
500 ScColumn* pCol = &aCol[nCol];
501 sc::ColumnBlockConstPosition aBlockPos;
502 pCol->InitBlockPosition(aBlockPos);
503 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
505 ScSortInfo & rInfo = pArray->Get( nSort, nRow );
506 rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow);
507 rInfo.nOrg = nRow;
511 initDataRows(
512 *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2,
513 rSortParam.bIncludePattern, bKeepQuery);
515 else
517 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
519 SCROW nRow = rSortParam.maKeyState[nSort].nField;
520 for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
521 nCol <= static_cast<SCCOL>(nInd2); nCol++ )
523 ScSortInfo & rInfo = pArray->Get( nSort, nCol );
524 rInfo.maCell = GetCellValue(nCol, nRow);
525 rInfo.nOrg = nCol;
529 return pArray;
532 namespace {
534 struct SortedColumn
536 typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
538 sc::CellStoreType maCells;
539 sc::CellTextAttrStoreType maCellTextAttrs;
540 sc::BroadcasterStoreType maBroadcasters;
541 sc::CellNoteStoreType maCellNotes;
542 std::vector<std::vector<SdrObject*>> maCellDrawObjects;
544 PatRangeType maPatterns;
545 PatRangeType::const_iterator miPatternPos;
547 SortedColumn(const SortedColumn&) = delete;
548 const SortedColumn operator=(const SortedColumn&) = delete;
550 explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) :
551 maCells(nTopEmptyRows),
552 maCellTextAttrs(nTopEmptyRows),
553 maBroadcasters(nTopEmptyRows),
554 maCellNotes(nTopEmptyRows),
555 maCellDrawObjects(),
556 maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr),
557 miPatternPos(maPatterns.begin()) {}
559 void setPattern( SCROW nRow, const ScPatternAttr* pPat )
561 miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
565 struct SortedRowFlags
567 typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
569 FlagsType maRowsHidden;
570 FlagsType maRowsFiltered;
571 FlagsType::const_iterator miPosHidden;
572 FlagsType::const_iterator miPosFiltered;
574 SortedRowFlags(const ScSheetLimits& rSheetLimits) :
575 maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false),
576 maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false),
577 miPosHidden(maRowsHidden.begin()),
578 miPosFiltered(maRowsFiltered.begin()) {}
580 void setRowHidden( SCROW nRow, bool b )
582 miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
585 void setRowFiltered( SCROW nRow, bool b )
587 miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
590 void swap( SortedRowFlags& r )
592 maRowsHidden.swap(r.maRowsHidden);
593 maRowsFiltered.swap(r.maRowsFiltered);
595 // Just reset the position hints.
596 miPosHidden = maRowsHidden.begin();
597 miPosFiltered = maRowsFiltered.begin();
601 struct PatternSpan
603 SCROW mnRow1;
604 SCROW mnRow2;
605 const ScPatternAttr* mpPattern;
607 PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
608 mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
613 bool ScTable::IsSortCollatorGlobal() const
615 return pSortCollator == ScGlobal::GetCollator() ||
616 pSortCollator == ScGlobal::GetCaseCollator();
619 void ScTable::InitSortCollator( const ScSortParam& rPar )
621 if ( !rPar.aCollatorLocale.Language.isEmpty() )
623 if ( !pSortCollator || IsSortCollatorGlobal() )
624 pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
625 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
626 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
628 else
629 { // SYSTEM
630 DestroySortCollator();
631 pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() :
632 ScGlobal::GetCollator());
636 void ScTable::DestroySortCollator()
638 if ( pSortCollator )
640 if ( !IsSortCollatorGlobal() )
641 delete pSortCollator;
642 pSortCollator = nullptr;
646 namespace {
648 template<typename Hint, typename ReorderMap, typename Index>
649 class ReorderNotifier
651 Hint maHint;
652 public:
653 ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) :
654 maHint(rMap, nTab, nPos1, nPos2) {}
656 void operator() ( SvtListener* p )
658 p->Notify(maHint);
662 class FormulaGroupPosCollector
664 sc::RefQueryFormulaGroup& mrQuery;
666 public:
667 explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
669 void operator() ( const SvtListener* p )
671 p->Query(mrQuery);
675 void fillSortedColumnArray(
676 std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
677 SortedRowFlags& rRowFlags,
678 std::vector<SvtListener*>& rCellListeners,
679 ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable )
681 SCROW nRow1 = pArray->GetStart();
682 ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
683 std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices();
685 size_t nColCount = nCol2 - nCol1 + 1;
686 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
687 SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
688 aSortedCols.reserve(nColCount);
689 for (size_t i = 0; i < nColCount; ++i)
691 // In the sorted column container, element positions and row
692 // positions must match, else formula cells may mis-behave during
693 // grouping.
694 aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
697 for (size_t i = 0; i < pRows->size(); ++i)
699 ScSortInfoArray::Row& rRow = (*pRows)[i];
700 for (size_t j = 0; j < rRow.maCells.size(); ++j)
702 ScAddress aCellPos(nCol1 + j, nRow1 + i, nTab);
704 ScSortInfoArray::Cell& rCell = rRow.maCells[j];
706 sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells;
707 switch (rCell.maCell.meType)
709 case CELLTYPE_STRING:
710 assert(rCell.mpAttr);
711 rCellStore.push_back(*rCell.maCell.mpString);
712 break;
713 case CELLTYPE_VALUE:
714 assert(rCell.mpAttr);
715 rCellStore.push_back(rCell.maCell.mfValue);
716 break;
717 case CELLTYPE_EDIT:
718 assert(rCell.mpAttr);
719 rCellStore.push_back(rCell.maCell.mpEditText->Clone().release());
720 break;
721 case CELLTYPE_FORMULA:
723 assert(rCell.mpAttr);
724 ScAddress aOldPos = rCell.maCell.mpFormula->aPos;
726 ScFormulaCell* pNew = rCell.maCell.mpFormula->Clone( aCellPos );
727 if (pArray->IsUpdateRefs())
729 pNew->CopyAllBroadcasters(*rCell.maCell.mpFormula);
730 pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
732 else
734 pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
737 if (!rCellListeners.empty())
739 // Original source cells will be deleted during
740 // sc::CellStoreType::transfer(), SvtListener is a base
741 // class, so we need to replace it.
742 auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.mpFormula));
743 if (it != rCellListeners.end())
744 *it = pNew;
747 rCellStore.push_back(pNew);
749 break;
750 default:
751 //assert(!rCell.mpAttr);
752 // This assert doesn't hold, for example
753 // CopyCellsFromClipHandler may omit copying cells during
754 // PasteSpecial for which CopyTextAttrsFromClipHandler
755 // still copies a CellTextAttr. So if that really is not
756 // expected then fix it there.
757 rCellStore.push_back_empty();
760 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs;
761 if (rCell.mpAttr)
762 rAttrStore.push_back(*rCell.mpAttr);
763 else
764 rAttrStore.push_back_empty();
766 if (pArray->IsUpdateRefs())
768 // At this point each broadcaster instance is managed by 2
769 // containers. We will release those in the original storage
770 // below before transferring them to the document.
771 const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
772 sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters;
773 if (pBroadcaster)
774 // A const pointer would be implicitly converted to a bool type.
775 rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster));
776 else
777 rBCStore.push_back_empty();
780 // The same with cell note instances ...
781 sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes;
782 if (rCell.mpNote)
783 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
784 else
785 rNoteStore.push_back_empty();
787 // Add cell anchored images
788 aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects);
790 if (rCell.mpPattern)
791 aSortedCols.at(j)->setPattern(aCellPos.Row(), rCell.mpPattern);
794 if (pArray->IsKeepQuery())
796 // Hidden and filtered flags are first converted to segments.
797 SCROW nRow = nRow1 + i;
798 aRowFlags.setRowHidden(nRow, rRow.mbHidden);
799 aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
802 if (pProgress)
803 pProgress->SetStateOnPercent(i);
806 rSortedCols.swap(aSortedCols);
807 rRowFlags.swap(aRowFlags);
810 void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
812 if (nTop < rRange.aStart.Row())
813 rRange.aStart.SetRow(nTop);
815 if (rRange.aEnd.Row() < nBottom)
816 rRange.aEnd.SetRow(nBottom);
819 class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
821 std::vector<ScFormulaCell*>& mrCells;
822 ScColumn* mpCol;
824 public:
825 explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
826 mrCells(rCells), mpCol(nullptr) {}
828 virtual void startColumn( ScColumn* pCol ) override
830 mpCol = pCol;
833 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
835 assert(mpCol);
837 if (!bVal)
838 return;
840 mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
844 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
846 ScColumn* mpCol;
848 std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
849 sc::StartListeningContext maStartCxt;
850 sc::EndListeningContext maEndCxt;
852 public:
853 explicit ListenerStartAction( ScDocument& rDoc ) :
854 mpCol(nullptr),
855 mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)),
856 maStartCxt(rDoc, mpPosSet),
857 maEndCxt(rDoc, mpPosSet) {}
859 virtual void startColumn( ScColumn* pCol ) override
861 mpCol = pCol;
864 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
866 assert(mpCol);
868 if (!bVal)
869 return;
871 mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
877 void ScTable::SortReorderByColumn(
878 const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
880 SCCOLROW nStart = pArray->GetStart();
881 SCCOLROW nLast = pArray->GetLast();
883 std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
884 size_t nCount = aIndices.size();
886 // Cut formula grouping at row and reference boundaries before the reordering.
887 ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
888 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
889 aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
891 // Collect all listeners of cell broadcasters of sorted range.
892 std::vector<SvtListener*> aCellListeners;
894 if (!pArray->IsUpdateRefs())
896 // Collect listeners of cell broadcasters.
897 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
898 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
900 // Remove any duplicate listener entries. We must ensure that we
901 // notify each unique listener only once.
902 std::sort(aCellListeners.begin(), aCellListeners.end());
903 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
905 // Notify the cells' listeners to stop listening.
906 /* TODO: for performance this could be enhanced to stop and later
907 * restart only listening to within the reordered range and keep
908 * listening to everything outside untouched. */
909 sc::RefStopListeningHint aHint;
910 for (auto const & l : aCellListeners)
911 l->Notify(aHint);
914 // table to keep track of column index to position in the index table.
915 std::vector<SCCOLROW> aPosTable(nCount);
916 for (size_t i = 0; i < nCount; ++i)
917 aPosTable[aIndices[i]-nStart] = i;
919 SCCOLROW nDest = nStart;
920 for (size_t i = 0; i < nCount; ++i, ++nDest)
922 SCCOLROW nSrc = aIndices[i];
923 if (nDest != nSrc)
925 aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
927 // Update the position of the index that was originally equal to nDest.
928 size_t nPos = aPosTable[nDest-nStart];
929 aIndices[nPos] = nSrc;
930 aPosTable[nSrc-nStart] = nPos;
933 if (pProgress)
934 pProgress->SetStateOnPercent(i);
937 // Reset formula cell positions which became out-of-sync after column reordering.
938 bool bUpdateRefs = pArray->IsUpdateRefs();
939 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
940 aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
942 if (pArray->IsUpdateRefs())
944 // Set up column reorder map (for later broadcasting of reference updates).
945 sc::ColRowReorderMapType aColMap;
946 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
947 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
949 SCCOL nNew = i + nStart;
950 SCCOL nOld = rOldIndices[i];
951 aColMap.emplace(nOld, nNew);
954 // Collect all listeners within sorted range ahead of time.
955 std::vector<SvtListener*> aListeners;
957 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
958 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
960 // Get all area listeners that listen on one column within the range
961 // and end their listening.
962 ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
963 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
964 aMoveRange, sc::AreaOverlapType::OneColumnInside);
966 for (auto& rAreaListener : aAreaListeners)
968 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
969 aListeners.push_back( rAreaListener.mpListener);
973 // Remove any duplicate listener entries and notify all listeners
974 // afterward. We must ensure that we notify each unique listener only
975 // once.
976 std::sort(aListeners.begin(), aListeners.end());
977 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
978 ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
979 std::for_each(aListeners.begin(), aListeners.end(), aFunc);
981 // Re-start area listeners on the reordered columns.
983 for (auto& rAreaListener : aAreaListeners)
985 ScRange aNewRange = rAreaListener.maArea;
986 sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
987 if (itCol != aColMap.end())
989 aNewRange.aStart.SetCol( itCol->second);
990 aNewRange.aEnd.SetCol( itCol->second);
992 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
996 else // !(pArray->IsUpdateRefs())
998 // Notify the cells' listeners to (re-)start listening.
999 sc::RefStartListeningHint aHint;
1000 for (auto const & l : aCellListeners)
1001 l->Notify(aHint);
1004 // Re-join formulas at row boundaries now that all the references have
1005 // been adjusted for column reordering.
1006 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1008 sc::CellStoreType& rCells = aCol[nCol].maCells;
1009 sc::CellStoreType::position_type aPos = rCells.position(nRow1);
1010 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1011 if (nRow2 < rDocument.MaxRow())
1013 aPos = rCells.position(aPos.first, nRow2+1);
1014 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1019 void ScTable::SortReorderByRow(
1020 ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1022 assert(!pArray->IsUpdateRefs());
1024 if (nCol2 < nCol1)
1025 return;
1027 SCROW nRow1 = pArray->GetStart();
1028 SCROW nRow2 = pArray->GetLast();
1030 // Collect all listeners of cell broadcasters of sorted range.
1031 std::vector<SvtListener*> aCellListeners;
1033 // When the update ref mode is disabled, we need to detach all formula
1034 // cells in the sorted range before reordering, and re-start them
1035 // afterward.
1037 sc::EndListeningContext aCxt(rDocument);
1038 DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1041 // Collect listeners of cell broadcasters.
1042 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1043 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
1045 // Remove any duplicate listener entries. We must ensure that we notify
1046 // each unique listener only once.
1047 std::sort(aCellListeners.begin(), aCellListeners.end());
1048 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
1050 // Notify the cells' listeners to stop listening.
1051 /* TODO: for performance this could be enhanced to stop and later
1052 * restart only listening to within the reordered range and keep
1053 * listening to everything outside untouched. */
1055 sc::RefStopListeningHint aHint;
1056 for (auto const & l : aCellListeners)
1057 l->Notify(aHint);
1060 // Split formula groups at the sort range boundaries (if applicable).
1061 std::vector<SCROW> aRowBounds;
1062 aRowBounds.reserve(2);
1063 aRowBounds.push_back(nRow1);
1064 aRowBounds.push_back(nRow2+1);
1065 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1066 SplitFormulaGroups(nCol, aRowBounds);
1068 // Cells in the data rows only reference values in the document. Make
1069 // a copy before updating the document.
1070 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1071 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1072 fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2, pProgress, this);
1074 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1076 SCCOL nThisCol = i + nCol1;
1079 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1080 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1081 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1085 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1086 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1087 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1091 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1092 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1094 // Do the same as broadcaster storage transfer (to prevent double deletion).
1095 rDest.release_range(nRow1, nRow2);
1096 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1097 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1100 // Update draw object positions
1101 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1104 // Get all row spans where the pattern is not NULL.
1105 std::vector<PatternSpan> aSpans =
1106 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1107 aSortedCols[i]->maPatterns);
1109 for (const auto& rSpan : aSpans)
1111 assert(rSpan.mpPattern); // should never be NULL.
1112 rDocument.GetPool()->Put(*rSpan.mpPattern);
1115 for (const auto& rSpan : aSpans)
1117 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1118 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1122 aCol[nThisCol].CellStorageModified();
1125 if (pArray->IsKeepQuery())
1127 aRowFlags.maRowsHidden.build_tree();
1128 aRowFlags.maRowsFiltered.build_tree();
1130 // Remove all flags in the range first.
1131 SetRowHidden(nRow1, nRow2, false);
1132 SetRowFiltered(nRow1, nRow2, false);
1134 std::vector<sc::RowSpan> aSpans =
1135 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1137 for (const auto& rSpan : aSpans)
1138 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1140 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1142 for (const auto& rSpan : aSpans)
1143 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1146 // Notify the cells' listeners to (re-)start listening.
1148 sc::RefStartListeningHint aHint;
1149 for (auto const & l : aCellListeners)
1150 l->Notify(aHint);
1153 // Re-group columns in the sorted range too.
1154 for (SCCOL i = nCol1; i <= nCol2; ++i)
1155 aCol[i].RegroupFormulaCells();
1158 sc::StartListeningContext aCxt(rDocument);
1159 AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1163 void ScTable::SortReorderByRowRefUpdate(
1164 ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1166 assert(pArray->IsUpdateRefs());
1168 if (nCol2 < nCol1)
1169 return;
1171 SCROW nRow1 = pArray->GetStart();
1172 SCROW nRow2 = pArray->GetLast();
1174 ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1175 sc::ColumnSpanSet aGrpListenerRanges;
1178 // Get the range of formula group listeners within sorted range (if any).
1179 sc::QueryRange aQuery;
1181 ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM();
1182 std::vector<sc::AreaListener> aGrpListeners =
1183 pBASM->GetAllListeners(
1184 aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group);
1187 for (const auto& rGrpListener : aGrpListeners)
1189 assert(rGrpListener.mbGroupListening);
1190 SvtListener* pGrpLis = rGrpListener.mpListener;
1191 pGrpLis->Query(aQuery);
1192 rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis);
1196 ScRangeList aTmp;
1197 aQuery.swapRanges(aTmp);
1199 // If the range is within the sorted range, we need to expand its rows
1200 // to the top and bottom of the sorted range, since the formula cells
1201 // could be anywhere in the sorted range after reordering.
1202 for (size_t i = 0, n = aTmp.size(); i < n; ++i)
1204 ScRange aRange = aTmp[i];
1205 if (!aMoveRange.Intersects(aRange))
1207 // Doesn't overlap with the sorted range at all.
1208 aGrpListenerRanges.set(GetDoc(), aRange, true);
1209 continue;
1212 if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
1214 // Its column range is within the column range of the sorted range.
1215 expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1216 aGrpListenerRanges.set(GetDoc(), aRange, true);
1217 continue;
1220 // It intersects with the sorted range, but its column range is
1221 // not within the column range of the sorted range. Split it into
1222 // 2 ranges.
1223 ScRange aR1 = aRange;
1224 ScRange aR2 = aRange;
1225 if (aRange.aStart.Col() < aMoveRange.aStart.Col())
1227 // Left half is outside the sorted range while the right half is inside.
1228 aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
1229 aR2.aStart.SetCol(aMoveRange.aStart.Col());
1230 expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1232 else
1234 // Left half is inside the sorted range while the right half is outside.
1235 aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
1236 aR2.aStart.SetCol(aMoveRange.aEnd.Col());
1237 expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1240 aGrpListenerRanges.set(GetDoc(), aR1, true);
1241 aGrpListenerRanges.set(GetDoc(), aR2, true);
1245 // Split formula groups at the sort range boundaries (if applicable).
1246 std::vector<SCROW> aRowBounds;
1247 aRowBounds.reserve(2);
1248 aRowBounds.push_back(nRow1);
1249 aRowBounds.push_back(nRow2+1);
1250 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1251 SplitFormulaGroups(nCol, aRowBounds);
1253 // Cells in the data rows only reference values in the document. Make
1254 // a copy before updating the document.
1255 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1256 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1257 std::vector<SvtListener*> aListenersDummy;
1258 fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this);
1260 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1262 SCCOL nThisCol = i + nCol1;
1265 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1266 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1267 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1271 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1272 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1273 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1277 sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters;
1278 sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
1280 // Release current broadcasters first, to prevent them from getting deleted.
1281 rDest.release_range(nRow1, nRow2);
1283 // Transfer sorted broadcaster segment to the document.
1284 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1288 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1289 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1291 // Do the same as broadcaster storage transfer (to prevent double deletion).
1292 rDest.release_range(nRow1, nRow2);
1293 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1294 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1297 // Update draw object positions
1298 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1301 // Get all row spans where the pattern is not NULL.
1302 std::vector<PatternSpan> aSpans =
1303 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1304 aSortedCols[i]->maPatterns);
1306 for (const auto& rSpan : aSpans)
1308 assert(rSpan.mpPattern); // should never be NULL.
1309 rDocument.GetPool()->Put(*rSpan.mpPattern);
1312 for (const auto& rSpan : aSpans)
1314 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1315 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1319 aCol[nThisCol].CellStorageModified();
1322 if (pArray->IsKeepQuery())
1324 aRowFlags.maRowsHidden.build_tree();
1325 aRowFlags.maRowsFiltered.build_tree();
1327 // Remove all flags in the range first.
1328 SetRowHidden(nRow1, nRow2, false);
1329 SetRowFiltered(nRow1, nRow2, false);
1331 std::vector<sc::RowSpan> aSpans =
1332 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1334 for (const auto& rSpan : aSpans)
1335 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1337 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1339 for (const auto& rSpan : aSpans)
1340 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1343 // Set up row reorder map (for later broadcasting of reference updates).
1344 sc::ColRowReorderMapType aRowMap;
1345 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1346 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1348 SCROW nNew = i + nRow1;
1349 SCROW nOld = rOldIndices[i];
1350 aRowMap.emplace(nOld, nNew);
1353 // Collect all listeners within sorted range ahead of time.
1354 std::vector<SvtListener*> aListeners;
1356 // Collect listeners of cell broadcasters.
1357 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1358 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1360 // Get all area listeners that listen on one row within the range and end
1361 // their listening.
1362 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1363 aMoveRange, sc::AreaOverlapType::OneRowInside);
1365 for (auto& rAreaListener : aAreaListeners)
1367 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1368 aListeners.push_back( rAreaListener.mpListener);
1373 // Get all formula cells from the former group area listener ranges.
1375 std::vector<ScFormulaCell*> aFCells;
1376 FormulaCellCollectAction aAction(aFCells);
1377 aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1379 aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() );
1382 // Remove any duplicate listener entries. We must ensure that we notify
1383 // each unique listener only once.
1384 std::sort(aListeners.begin(), aListeners.end());
1385 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1387 // Collect positions of all shared formula cells outside the sorted range,
1388 // and make them unshared before notifying them.
1389 sc::RefQueryFormulaGroup aFormulaGroupPos;
1390 aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1392 std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
1393 const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
1394 for (const auto& [rTab, rCols] : rGroupTabs)
1396 for (const auto& [nCol, rCol] : rCols)
1398 std::vector<SCROW> aBounds(rCol);
1399 rDocument.UnshareFormulaCells(rTab, nCol, aBounds);
1403 // Notify the listeners to update their references.
1404 ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
1405 std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1407 // Re-group formulas in affected columns.
1408 for (const auto& [rTab, rCols] : rGroupTabs)
1410 for (const auto& rEntry : rCols)
1411 rDocument.RegroupFormulaCells(rTab, rEntry.first);
1414 // Re-start area listeners on the reordered rows.
1415 for (const auto& rAreaListener : aAreaListeners)
1417 ScRange aNewRange = rAreaListener.maArea;
1418 sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
1419 if (itRow != aRowMap.end())
1421 aNewRange.aStart.SetRow( itRow->second);
1422 aNewRange.aEnd.SetRow( itRow->second);
1424 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1427 // Re-group columns in the sorted range too.
1428 for (SCCOL i = nCol1; i <= nCol2; ++i)
1429 aCol[i].RegroupFormulaCells();
1432 // Re-start area listeners on the old group listener ranges.
1433 ListenerStartAction aAction(rDocument);
1434 aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1438 short ScTable::CompareCell(
1439 sal_uInt16 nSort,
1440 ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1441 ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1443 short nRes = 0;
1445 CellType eType1 = rCell1.meType, eType2 = rCell2.meType;
1447 if (!rCell1.isEmpty())
1449 if (!rCell2.isEmpty())
1451 bool bErr1 = false;
1452 bool bStr1 = ( eType1 != CELLTYPE_VALUE );
1453 if (eType1 == CELLTYPE_FORMULA)
1455 if (rCell1.mpFormula->GetErrCode() != FormulaError::NONE)
1457 bErr1 = true;
1458 bStr1 = false;
1460 else if (rCell1.mpFormula->IsValue())
1462 bStr1 = false;
1466 bool bErr2 = false;
1467 bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1468 if (eType2 == CELLTYPE_FORMULA)
1470 if (rCell2.mpFormula->GetErrCode() != FormulaError::NONE)
1472 bErr2 = true;
1473 bStr2 = false;
1475 else if (rCell2.mpFormula->IsValue())
1477 bStr2 = false;
1481 if ( bStr1 && bStr2 ) // only compare strings as strings!
1483 OUString aStr1;
1484 OUString aStr2;
1485 if (eType1 == CELLTYPE_STRING)
1486 aStr1 = rCell1.mpString->getString();
1487 else
1488 GetString(nCell1Col, nCell1Row, aStr1);
1489 if (eType2 == CELLTYPE_STRING)
1490 aStr2 = rCell2.mpString->getString();
1491 else
1492 GetString(nCell2Col, nCell2Row, aStr2);
1494 bool bUserDef = aSortParam.bUserDef; // custom sort order
1495 bool bNaturalSort = aSortParam.bNaturalSort; // natural sort
1496 bool bCaseSens = aSortParam.bCaseSens; // case sensitivity
1498 ScUserList* pList = ScGlobal::GetUserList();
1499 if (bUserDef && pList && pList->size() > aSortParam.nUserIndex )
1501 const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
1503 if ( bNaturalSort )
1504 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator );
1505 else
1507 if ( bCaseSens )
1508 nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) );
1509 else
1510 nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) );
1514 if (!bUserDef)
1516 if ( bNaturalSort )
1517 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator );
1518 else
1519 nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
1522 else if ( bStr1 ) // String <-> Number or Error
1524 if (bErr2)
1525 nRes = -1; // String in front of Error
1526 else
1527 nRes = 1; // Number in front of String
1529 else if ( bStr2 ) // Number or Error <-> String
1531 if (bErr1)
1532 nRes = 1; // String in front of Error
1533 else
1534 nRes = -1; // Number in front of String
1536 else if (bErr1 && bErr2)
1538 // nothing, two Errors are equal
1540 else if (bErr1) // Error <-> Number
1542 nRes = 1; // Number in front of Error
1544 else if (bErr2) // Number <-> Error
1546 nRes = -1; // Number in front of Error
1548 else // Mixed numbers
1550 double nVal1 = rCell1.getValue();
1551 double nVal2 = rCell2.getValue();
1552 if (nVal1 < nVal2)
1553 nRes = -1;
1554 else if (nVal1 > nVal2)
1555 nRes = 1;
1557 if ( !aSortParam.maKeyState[nSort].bAscending )
1558 nRes = -nRes;
1560 else
1561 nRes = -1;
1563 else
1565 if (!rCell2.isEmpty())
1566 nRes = 1;
1567 else
1568 nRes = 0; // both empty
1570 return nRes;
1573 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1575 short nRes;
1576 sal_uInt16 nSort = 0;
1579 ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 );
1580 ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 );
1581 if ( aSortParam.bByRow )
1582 nRes = CompareCell( nSort,
1583 rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg,
1584 rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg );
1585 else
1586 nRes = CompareCell( nSort,
1587 rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField,
1588 rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField );
1589 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
1590 if( nRes == 0 )
1592 ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 );
1593 ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 );
1594 if( rInfo1.nOrg < rInfo2.nOrg )
1595 nRes = -1;
1596 else if( rInfo1.nOrg > rInfo2.nOrg )
1597 nRes = 1;
1599 return nRes;
1602 void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi )
1604 if ((nHi - nLo) == 1)
1606 if (Compare(pArray, nLo, nHi) > 0)
1607 pArray->Swap( nLo, nHi );
1609 else
1611 SCCOLROW ni = nLo;
1612 SCCOLROW nj = nHi;
1615 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1616 ni++;
1617 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1618 nj--;
1619 if (ni <= nj)
1621 if (ni != nj)
1622 pArray->Swap( ni, nj );
1623 ni++;
1624 nj--;
1626 } while (ni < nj);
1627 if ((nj - nLo) < (nHi - ni))
1629 if (nLo < nj)
1630 QuickSort(pArray, nLo, nj);
1631 if (ni < nHi)
1632 QuickSort(pArray, ni, nHi);
1634 else
1636 if (ni < nHi)
1637 QuickSort(pArray, ni, nHi);
1638 if (nLo < nj)
1639 QuickSort(pArray, nLo, nj);
1644 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1646 short nRes;
1647 sal_uInt16 nSort = 0;
1648 const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
1649 if (aSortParam.bByRow)
1653 SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
1654 ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
1655 ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
1656 nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
1657 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1659 else
1663 SCROW nRow = aSortParam.maKeyState[nSort].nField;
1664 ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow);
1665 ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow);
1666 nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
1667 nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
1668 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1670 return nRes;
1673 bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // over aSortParam
1675 for (SCCOLROW i=nStart; i<nEnd; i++)
1677 if (Compare( i, i+1 ) > 0)
1678 return false;
1680 return true;
1683 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1685 SCROW nRow;
1686 int nMax = nRow2 - nRow1;
1687 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
1689 nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
1690 pArray->Swap(i, nRow1 + nRow);
1694 void ScTable::Sort(
1695 const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1696 ScProgress* pProgress, sc::ReorderParam* pUndo )
1698 InitSortCollator( rSortParam );
1699 bGlobalKeepQuery = bKeepQuery;
1701 if (pUndo)
1703 // Copy over the basic sort parameters.
1704 pUndo->mbByRow = rSortParam.bByRow;
1705 pUndo->mbPattern = rSortParam.bIncludePattern;
1706 pUndo->mbHiddenFiltered = bKeepQuery;
1707 pUndo->mbUpdateRefs = bUpdateRefs;
1708 pUndo->mbHasHeaders = rSortParam.bHasHeader;
1711 // It is assumed that the data area has already been trimmed as necessary.
1713 aSortParam = rSortParam; // must be assigned before calling IsSorted()
1714 if (rSortParam.bByRow)
1716 SCROW nLastRow = rSortParam.nRow2;
1717 SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
1718 if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
1720 if(pProgress)
1721 pProgress->SetState( 0, nLastRow-nRow1 );
1723 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
1725 if ( nLastRow - nRow1 > 255 )
1726 DecoladeRow(pArray.get(), nRow1, nLastRow);
1728 QuickSort(pArray.get(), nRow1, nLastRow);
1729 if (pArray->IsUpdateRefs())
1730 SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1731 else
1732 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1734 if (pUndo)
1736 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1737 pUndo->maOrderIndices = pArray->GetOrderIndices();
1741 else
1743 SCCOL nLastCol = rSortParam.nCol2;
1744 SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1745 if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1747 if(pProgress)
1748 pProgress->SetState( 0, nLastCol-nCol1 );
1750 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
1752 QuickSort(pArray.get(), nCol1, nLastCol);
1753 SortReorderByColumn(pArray.get(), aSortParam.nRow1, aSortParam.nRow2, aSortParam.bIncludePattern, pProgress);
1755 if (pUndo)
1757 pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
1758 pUndo->maOrderIndices = pArray->GetOrderIndices();
1762 DestroySortCollator();
1765 void ScTable::Reorder( const sc::ReorderParam& rParam )
1767 if (rParam.maOrderIndices.empty())
1768 return;
1770 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1771 if (!pArray)
1772 return;
1774 if (rParam.mbByRow)
1776 // Re-play sorting from the known sort indices.
1777 pArray->ReorderByRow(rParam.maOrderIndices);
1778 if (pArray->IsUpdateRefs())
1779 SortReorderByRowRefUpdate(
1780 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
1781 else
1782 SortReorderByRow(
1783 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
1785 else
1787 // Ordering by column is much simpler. Just set the order indices and we are done.
1788 pArray->SetOrderIndices(rParam.maOrderIndices);
1789 SortReorderByColumn(
1790 pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1791 rParam.mbPattern, nullptr);
1795 namespace {
1797 class SubTotalRowFinder
1799 const ScTable& mrTab;
1800 const ScSubTotalParam& mrParam;
1802 public:
1803 SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
1804 mrTab(rTab), mrParam(rParam) {}
1806 bool operator() (size_t nRow, const ScFormulaCell* pCell)
1808 if (!pCell->IsSubTotal())
1809 return false;
1811 SCCOL nStartCol = mrParam.nCol1;
1812 SCCOL nEndCol = mrParam.nCol2;
1814 for (SCCOL nCol : mrTab.GetColumnsRange(0, nStartCol - 1))
1816 if (mrTab.HasData(nCol, nRow))
1817 return true;
1819 for (SCCOL nCol : mrTab.GetColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol()))
1821 if (mrTab.HasData(nCol, nRow))
1822 return true;
1824 return false;
1830 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
1832 SCCOL nStartCol = rParam.nCol1;
1833 SCROW nStartRow = rParam.nRow1 + 1; // Header
1834 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
1835 SCROW nEndRow = rParam.nRow2;
1837 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1839 const sc::CellStoreType& rCells = aCol[nCol].maCells;
1840 SubTotalRowFinder aFunc(*this, rParam);
1841 std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
1842 sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
1843 if (aPos.first != rCells.end())
1844 return true;
1846 return false;
1849 namespace {
1851 struct RemoveSubTotalsHandler
1853 std::set<SCROW> aRemoved;
1855 void operator() (size_t nRow, const ScFormulaCell* p)
1857 if (p->IsSubTotal())
1858 aRemoved.insert(nRow);
1864 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
1866 SCCOL nStartCol = rParam.nCol1;
1867 SCROW nStartRow = rParam.nRow1 + 1; // Header
1868 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
1869 SCROW nEndRow = rParam.nRow2; // will change
1871 RemoveSubTotalsHandler aFunc;
1872 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1874 const sc::CellStoreType& rCells = aCol[nCol].maCells;
1875 sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
1878 auto& aRows = aFunc.aRemoved;
1880 std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) {
1881 RemoveRowBreak(nRow+1, false, true);
1882 rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1);
1885 rParam.nRow2 -= aRows.size();
1888 // Delete hard number formats (for result formulas)
1890 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
1892 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
1893 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
1894 == SfxItemState::SET )
1896 auto pNewPattern = std::make_unique<ScPatternAttr>( *pPattern );
1897 SfxItemSet& rSet = pNewPattern->GetItemSet();
1898 rSet.ClearItem( ATTR_VALUE_FORMAT );
1899 rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
1900 pTab->SetPattern( nCol, nRow, std::move(pNewPattern) );
1904 namespace {
1906 struct RowEntry
1908 sal_uInt16 nGroupNo;
1909 SCROW nSubStartRow;
1910 SCROW nDestRow;
1911 SCROW nFuncStart;
1912 SCROW nFuncEnd;
1917 static const char* lcl_GetSubTotalStrId(int id)
1919 switch ( id )
1921 case SUBTOTAL_FUNC_AVE: return STR_FUN_TEXT_AVG;
1922 case SUBTOTAL_FUNC_CNT:
1923 case SUBTOTAL_FUNC_CNT2: return STR_FUN_TEXT_COUNT;
1924 case SUBTOTAL_FUNC_MAX: return STR_FUN_TEXT_MAX;
1925 case SUBTOTAL_FUNC_MIN: return STR_FUN_TEXT_MIN;
1926 case SUBTOTAL_FUNC_PROD: return STR_FUN_TEXT_PRODUCT;
1927 case SUBTOTAL_FUNC_STD:
1928 case SUBTOTAL_FUNC_STDP: return STR_FUN_TEXT_STDDEV;
1929 case SUBTOTAL_FUNC_SUM: return STR_FUN_TEXT_SUM;
1930 case SUBTOTAL_FUNC_VAR:
1931 case SUBTOTAL_FUNC_VARP: return STR_FUN_TEXT_VAR;
1932 default:
1934 return STR_EMPTYDATA;
1935 // added to avoid warnings
1940 // new intermediate results
1941 // rParam.nRow2 is changed!
1943 bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
1945 SCCOL nStartCol = rParam.nCol1;
1946 SCROW nStartRow = rParam.nRow1 + 1; // Header
1947 SCCOL nEndCol = rParam.nCol2;
1948 SCROW nEndRow = rParam.nRow2; // will change
1949 sal_uInt16 i;
1951 // Remove empty rows at the end
1952 // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
1953 // If sorted, all empty rows are at the end.
1954 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
1955 nEndRow -= nEmpty;
1957 sal_uInt16 nLevelCount = 0; // Number of levels
1958 bool bDoThis = true;
1959 for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
1960 if (rParam.bGroupActive[i])
1961 nLevelCount = i+1;
1962 else
1963 bDoThis = false;
1965 if (nLevelCount==0) // do nothing
1966 return true;
1968 SCCOL* nGroupCol = rParam.nField; // columns which will be used when grouping
1970 // With (blank) as a separate category, subtotal rows from
1971 // the other columns must always be tested
1972 // (previously only when a column occurred more than once)
1973 bool bTestPrevSub = ( nLevelCount > 1 );
1975 OUString aSubString;
1977 bool bIgnoreCase = !rParam.bCaseSens;
1979 OUString aCompString[MAXSUBTOTAL];
1981 //TODO: sort?
1983 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find(
1984 ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para ));
1986 bool bSpaceLeft = true; // Success when inserting?
1988 // For performance reasons collect formula entries so their
1989 // references don't have to be tested for updates each time a new row is
1990 // inserted
1991 RowEntry aRowEntry;
1992 ::std::vector< RowEntry > aRowVector;
1994 for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++)
1996 aRowEntry.nGroupNo = nLevelCount - nLevel - 1;
1998 // how many results per level
1999 SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
2000 // result functions
2001 ScSubTotalFunc* pResFunc = rParam.pFunctions[aRowEntry.nGroupNo];
2003 if (nResCount > 0) // otherwise only sort
2005 for (i=0; i<=aRowEntry.nGroupNo; i++)
2007 GetString( nGroupCol[i], nStartRow, aSubString );
2008 if ( bIgnoreCase )
2009 aCompString[i] = ScGlobal::getCharClassPtr()->uppercase( aSubString );
2010 else
2011 aCompString[i] = aSubString;
2012 } // aSubString stays on the last
2014 bool bBlockVis = false; // group visible?
2015 aRowEntry.nSubStartRow = nStartRow;
2016 for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
2018 bool bChanged;
2019 if (nRow>nEndRow)
2020 bChanged = true;
2021 else
2023 bChanged = false;
2024 OUString aString;
2025 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
2027 GetString( nGroupCol[i], nRow, aString );
2028 if (bIgnoreCase)
2029 aString = ScGlobal::getCharClassPtr()->uppercase(aString);
2030 // when sorting, blanks are separate group
2031 // otherwise blank cells are allowed below
2032 bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
2033 aString != aCompString[i] );
2035 if ( bChanged && bTestPrevSub )
2037 // No group change on rows that will contain subtotal formulas
2038 bChanged = std::none_of(aRowVector.begin(), aRowVector.end(),
2039 [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; });
2042 if ( bChanged )
2044 aRowEntry.nDestRow = nRow;
2045 aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
2046 aRowEntry.nFuncEnd = nRow-1;
2048 bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab,
2049 aRowEntry.nDestRow, 1 );
2050 DBShowRow( aRowEntry.nDestRow, bBlockVis );
2051 if ( rParam.bPagebreak && nRow < rDocument.MaxRow() &&
2052 aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2053 SetRowBreak(aRowEntry.nSubStartRow, false, true);
2055 if (bSpaceLeft)
2057 for ( auto& rRowEntry : aRowVector)
2059 if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow )
2060 ++rRowEntry.nSubStartRow;
2061 if ( aRowEntry.nDestRow <= rRowEntry.nDestRow )
2062 ++rRowEntry.nDestRow;
2063 if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart )
2064 ++rRowEntry.nFuncStart;
2065 if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd )
2066 ++rRowEntry.nFuncEnd;
2068 // collect formula positions
2069 aRowVector.push_back( aRowEntry );
2071 OUString aOutString = aSubString;
2072 if (aOutString.isEmpty())
2073 aOutString = ScResId( STR_EMPTYDATA );
2074 aOutString += " ";
2075 const char* pStrId = STR_TABLE_ERGEBNIS;
2076 if ( nResCount == 1 )
2077 pStrId = lcl_GetSubTotalStrId(pResFunc[0]);
2078 aOutString += ScResId(pStrId);
2079 SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
2080 ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle );
2082 ++nRow;
2083 ++nEndRow;
2084 aRowEntry.nSubStartRow = nRow;
2085 for (i=0; i<=aRowEntry.nGroupNo; i++)
2087 GetString( nGroupCol[i], nRow, aSubString );
2088 if ( bIgnoreCase )
2089 aCompString[i] = ScGlobal::getCharClassPtr()->uppercase( aSubString );
2090 else
2091 aCompString[i] = aSubString;
2095 bBlockVis = !RowFiltered(nRow);
2100 if (!aRowVector.empty())
2102 // generate global total
2103 SCROW nGlobalStartRow = aRowVector[0].nSubStartRow;
2104 SCROW nGlobalStartFunc = aRowVector[0].nFuncStart;
2105 SCROW nGlobalEndRow = 0;
2106 SCROW nGlobalEndFunc = 0;
2107 for (const auto& rRowEntry : aRowVector)
2109 nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow;
2110 nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow;
2113 for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++)
2115 const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1;
2116 const ScSubTotalFunc* pResFunc = rParam.pFunctions[nGroupNo];
2117 if (!pResFunc)
2119 // No subtotal function given for this group => no formula or
2120 // label and do not insert a row.
2121 continue;
2124 // increment end row
2125 nGlobalEndRow++;
2127 // add row entry for formula
2128 aRowEntry.nGroupNo = nGroupNo;
2129 aRowEntry.nSubStartRow = nGlobalStartRow;
2130 aRowEntry.nFuncStart = nGlobalStartFunc;
2131 aRowEntry.nDestRow = nGlobalEndRow;
2132 aRowEntry.nFuncEnd = nGlobalEndFunc;
2134 // increment row
2135 nGlobalEndFunc++;
2137 bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1);
2139 if (bSpaceLeft)
2141 aRowVector.push_back(aRowEntry);
2142 nEndRow++;
2143 DBShowRow(aRowEntry.nDestRow, true);
2145 // insert label
2146 OUString label = ScResId(STR_TABLE_GRAND) + " " + ScResId(lcl_GetSubTotalStrId(pResFunc[0]));
2147 SetString(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, label);
2148 ApplyStyle(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle);
2153 // now insert the formulas
2154 ScComplexRefData aRef;
2155 aRef.InitFlags();
2156 aRef.Ref1.SetAbsTab(nTab);
2157 aRef.Ref2.SetAbsTab(nTab);
2158 for (const auto& rRowEntry : aRowVector)
2160 SCCOL nResCount = rParam.nSubTotals[rRowEntry.nGroupNo];
2161 SCCOL* nResCols = rParam.pSubTotals[rRowEntry.nGroupNo];
2162 ScSubTotalFunc* pResFunc = rParam.pFunctions[rRowEntry.nGroupNo];
2163 for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
2165 aRef.Ref1.SetAbsCol(nResCols[nResult]);
2166 aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart);
2167 aRef.Ref2.SetAbsCol(nResCols[nResult]);
2168 aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd);
2170 ScTokenArray aArr(rDocument);
2171 aArr.AddOpCode( ocSubTotal );
2172 aArr.AddOpCode( ocOpen );
2173 aArr.AddDouble( static_cast<double>(pResFunc[nResult]) );
2174 aArr.AddOpCode( ocSep );
2175 aArr.AddDoubleReference( aRef );
2176 aArr.AddOpCode( ocClose );
2177 aArr.AddOpCode( ocStop );
2178 ScFormulaCell* pCell = new ScFormulaCell(
2179 rDocument, ScAddress(nResCols[nResult], rRowEntry.nDestRow, nTab), aArr);
2180 if ( rParam.bIncludePattern )
2181 pCell->SetNeedNumberFormat(true);
2183 SetFormulaCell(nResCols[nResult], rRowEntry.nDestRow, pCell);
2184 if ( nResCols[nResult] != nGroupCol[rRowEntry.nGroupNo] )
2186 ApplyStyle( nResCols[nResult], rRowEntry.nDestRow, pStyle );
2188 lcl_RemoveNumberFormat( this, nResCols[nResult], rRowEntry.nDestRow );
2194 //TODO: according to setting, shift intermediate-sum rows up?
2196 //TODO: create Outlines directly?
2198 if (bSpaceLeft)
2199 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2201 rParam.nRow2 = nEndRow; // new end
2202 return bSpaceLeft;
2205 namespace {
2207 class QueryEvaluator
2209 ScDocument& mrDoc;
2210 svl::SharedStringPool& mrStrPool;
2211 const ScTable& mrTab;
2212 const ScQueryParam& mrParam;
2213 bool mpTestEqualCondition;
2214 utl::TransliterationWrapper* mpTransliteration;
2215 CollatorWrapper* mpCollator;
2216 const bool mbMatchWholeCell;
2217 const bool mbCaseSensitive;
2219 static bool isPartialTextMatchOp(const ScQueryEntry& rEntry)
2221 switch (rEntry.eOp)
2223 // these operators can only be used with textural comparisons.
2224 case SC_CONTAINS:
2225 case SC_DOES_NOT_CONTAIN:
2226 case SC_BEGINS_WITH:
2227 case SC_ENDS_WITH:
2228 case SC_DOES_NOT_BEGIN_WITH:
2229 case SC_DOES_NOT_END_WITH:
2230 return true;
2231 default:
2234 return false;
2237 static bool isTextMatchOp(const ScQueryEntry& rEntry)
2239 if (isPartialTextMatchOp(rEntry))
2240 return true;
2242 switch (rEntry.eOp)
2244 // these operators can be used for either textural or value comparison.
2245 case SC_EQUAL:
2246 case SC_NOT_EQUAL:
2247 return true;
2248 default:
2251 return false;
2254 bool isRealWildOrRegExp(const ScQueryEntry& rEntry) const
2256 if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
2257 return false;
2259 return isTextMatchOp(rEntry);
2262 bool isTestWildOrRegExp(const ScQueryEntry& rEntry) const
2264 if (!mpTestEqualCondition)
2265 return false;
2267 if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
2268 return false;
2270 return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
2273 void setupTransliteratorIfNeeded()
2275 if (!mpTransliteration)
2276 mpTransliteration = mrParam.bCaseSens ? ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration();
2279 void setupCollatorIfNeeded()
2281 if (!mpCollator)
2282 mpCollator = mrParam.bCaseSens ? ScGlobal::GetCaseCollator() : ScGlobal::GetCollator();
2285 public:
2286 QueryEvaluator(ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
2287 bool pTestEqualCondition) :
2288 mrDoc(rDoc),
2289 mrStrPool(rDoc.GetSharedStringPool()),
2290 mrTab(rTab),
2291 mrParam(rParam),
2292 mpTestEqualCondition(pTestEqualCondition),
2293 mpTransliteration(nullptr),
2294 mpCollator(nullptr),
2295 mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell()),
2296 mbCaseSensitive( rParam.bCaseSens )
2300 bool isQueryByValue(
2301 const ScQueryEntry::Item& rItem, SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
2303 if (rItem.meType == ScQueryEntry::ByString)
2304 return false;
2306 if (!rCell.isEmpty())
2308 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2309 // Error values are compared as string.
2310 return false;
2312 return rCell.hasNumeric();
2315 return mrTab.HasValueData(nCol, nRow);
2318 bool isQueryByString(
2319 const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2320 SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell)
2322 if (isTextMatchOp(rEntry))
2323 return true;
2325 if (rItem.meType != ScQueryEntry::ByString)
2326 return false;
2328 if (!rCell.isEmpty())
2329 return rCell.hasString();
2331 return mrTab.HasStringData(nCol, nRow);
2334 std::pair<bool,bool> compareByValue(
2335 const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2336 const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2337 const ScInterpreterContext* pContext)
2339 bool bOk = false;
2340 bool bTestEqual = false;
2341 double nCellVal;
2342 if (!rCell.isEmpty())
2344 switch (rCell.meType)
2346 case CELLTYPE_VALUE :
2347 nCellVal = rCell.mfValue;
2348 break;
2349 case CELLTYPE_FORMULA :
2350 nCellVal = rCell.mpFormula->GetValue();
2351 break;
2352 default:
2353 nCellVal = 0.0;
2357 else
2358 nCellVal = mrTab.GetValue(nCol, nRow);
2360 /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
2361 * date+time format was queried rEntry.bQueryByDate is not set. In
2362 * case other queries wanted to use this mechanism they should do
2363 * the same, in other words only if rEntry.nVal is an integer value
2364 * rEntry.bQueryByDate should be true and the time fraction be
2365 * stripped here. */
2366 if (rItem.meType == ScQueryEntry::ByDate)
2368 sal_uInt32 nNumFmt = pContext ? mrTab.GetNumberFormat(*pContext, ScAddress(nCol, nRow, mrTab.GetTab())) :
2369 mrTab.GetNumberFormat(nCol, nRow);
2370 SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : mrDoc.GetFormatTable();
2371 const SvNumberformat* pEntry = pFormatter->GetEntry(nNumFmt);
2372 if (pEntry)
2374 SvNumFormatType nNumFmtType = pEntry->GetType();
2375 /* NOTE: Omitting the check for absence of
2376 * css::util::NumberFormat::TIME would include also date+time formatted
2377 * values of the same day. That may be desired in some
2378 * cases, querying all time values of a day, but confusing
2379 * in other cases. A user can always setup a standard
2380 * filter query for x >= date AND x < date+1 */
2381 if ((nNumFmtType & SvNumFormatType::DATE) && !(nNumFmtType & SvNumFormatType::TIME))
2383 // The format is of date type. Strip off the time
2384 // element.
2385 nCellVal = ::rtl::math::approxFloor(nCellVal);
2390 switch (rEntry.eOp)
2392 case SC_EQUAL :
2393 bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2394 break;
2395 case SC_LESS :
2396 bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2397 break;
2398 case SC_GREATER :
2399 bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2400 break;
2401 case SC_LESS_EQUAL :
2402 bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2403 if ( bOk && mpTestEqualCondition )
2404 bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2405 break;
2406 case SC_GREATER_EQUAL :
2407 bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual( nCellVal, rItem.mfVal);
2408 if ( bOk && mpTestEqualCondition )
2409 bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2410 break;
2411 case SC_NOT_EQUAL :
2412 bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2413 break;
2414 default:
2416 // added to avoid warnings
2420 return std::pair<bool,bool>(bOk, bTestEqual);
2423 std::pair<bool,bool> compareByString(
2424 const ScRefCellValue& rCell, SCROW nRow, const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2425 const ScInterpreterContext* pContext)
2427 if (!rCell.isEmpty())
2429 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2431 // Error cell is evaluated as string (for now).
2432 const svl::SharedString aCellStr = mrStrPool.intern(ScGlobal::GetErrorString(rCell.mpFormula->GetErrCode()));
2433 return compareByStringComparator(rEntry, rItem, &aCellStr, nullptr);
2435 else if (rCell.meType == CELLTYPE_STRING)
2437 return compareByStringComparator(rEntry, rItem, rCell.mpString, nullptr);
2439 else
2441 sal_uInt32 nFormat = pContext ? mrTab.GetNumberFormat( *pContext, ScAddress(static_cast<SCCOL>(rEntry.nField), nRow, mrTab.GetTab()) ) :
2442 mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
2443 OUString aStr;
2444 SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : mrDoc.GetFormatTable();
2445 ScCellFormat::GetInputString(rCell, nFormat, aStr, *pFormatter, mrDoc, rEntry.bDoQuery);
2446 return compareByStringComparator(rEntry, rItem, nullptr, &aStr);
2449 else
2451 OUString aStr;
2452 mrTab.GetInputString(static_cast<SCCOL>(rEntry.nField), nRow, aStr);
2453 return compareByStringComparator(rEntry, rItem, nullptr, &aStr);
2457 // Called from compareByString() method, where different sources of strings are checked.
2458 // The value is placed inside one parameter: [pValueSource1] or [pValueSource2] but never in both.
2459 std::pair<bool,bool> compareByStringComparator(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2460 const svl::SharedString* pValueSource1, const OUString * pValueSource2)
2462 bool bOk = false;
2463 bool bTestEqual = false;
2464 bool bMatchWholeCell = mbMatchWholeCell;
2465 if (isPartialTextMatchOp(rEntry))
2466 // may have to do partial textural comparison.
2467 bMatchWholeCell = false;
2469 const bool bRealWildOrRegExp = isRealWildOrRegExp(rEntry);
2470 const bool bTestWildOrRegExp = isTestWildOrRegExp(rEntry);
2472 // [pValueSource1] or [pValueSource2] but never both of them or none of them
2473 assert((pValueSource1 != nullptr) != (pValueSource2 != nullptr));
2475 if ( bRealWildOrRegExp || bTestWildOrRegExp )
2477 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2479 sal_Int32 nStart = 0;
2480 sal_Int32 nEnd = rValue.getLength();
2482 // from 614 on, nEnd is behind the found text
2483 bool bMatch = false;
2484 if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
2486 nEnd = 0;
2487 nStart = rValue.getLength();
2488 bMatch = rEntry.GetSearchTextPtr( mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell )
2489 ->SearchBackward(rValue, &nStart, &nEnd);
2491 else
2493 bMatch = rEntry.GetSearchTextPtr( mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell )
2494 ->SearchForward(rValue, &nStart, &nEnd);
2496 if ( bMatch && bMatchWholeCell
2497 && (nStart != 0 || nEnd != rValue.getLength()) )
2498 bMatch = false; // RegExp must match entire cell string
2499 if ( bRealWildOrRegExp )
2501 switch (rEntry.eOp)
2503 case SC_EQUAL:
2504 case SC_CONTAINS:
2505 bOk = bMatch;
2506 break;
2507 case SC_NOT_EQUAL:
2508 case SC_DOES_NOT_CONTAIN:
2509 bOk = !bMatch;
2510 break;
2511 case SC_BEGINS_WITH:
2512 bOk = ( bMatch && (nStart == 0) );
2513 break;
2514 case SC_DOES_NOT_BEGIN_WITH:
2515 bOk = !( bMatch && (nStart == 0) );
2516 break;
2517 case SC_ENDS_WITH:
2518 bOk = ( bMatch && (nEnd == rValue.getLength()) );
2519 break;
2520 case SC_DOES_NOT_END_WITH:
2521 bOk = !( bMatch && (nEnd == rValue.getLength()) );
2522 break;
2523 default:
2525 // added to avoid warnings
2529 else
2530 bTestEqual = bMatch;
2532 if ( !bRealWildOrRegExp )
2534 // Simple string matching i.e. no regexp match.
2535 if (isTextMatchOp(rEntry))
2537 if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
2539 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
2540 // the query value is assigned directly, and the string is empty. In that case,
2541 // don't find any string (isEqual would find empty string results in formula cells).
2542 bOk = false;
2543 if ( rEntry.eOp == SC_NOT_EQUAL )
2544 bOk = !bOk;
2546 else if ( bMatchWholeCell )
2548 if (pValueSource1)
2550 // Fast string equality check by comparing string identifiers.
2551 if (mrParam.bCaseSens)
2553 bOk = pValueSource1->getData() == rItem.maString.getData();
2555 else
2557 bOk = pValueSource1->getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2560 else // if (pValueSource2)
2562 if (mrParam.bCaseSens)
2564 bOk = (*pValueSource2 == rItem.maString.getString());
2566 else
2568 // fallback
2569 const svl::SharedString rSource2(mrStrPool.intern(*pValueSource2));
2570 // Fast string equality check by comparing string identifiers.
2571 bOk = rSource2.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2575 if ( rEntry.eOp == SC_NOT_EQUAL )
2576 bOk = !bOk;
2578 else
2580 // Where do we find a match (if at all)
2581 sal_Int32 nStrPos;
2583 if (!mbCaseSensitive)
2584 { // Common case for vlookup etc.
2585 const svl::SharedString rSource(pValueSource1? *pValueSource1 : mrStrPool.intern(*pValueSource2));
2587 const rtl_uString *pQuer = rItem.maString.getDataIgnoreCase();
2588 const rtl_uString *pCellStr = rSource.getDataIgnoreCase();
2590 assert(pQuer != nullptr);
2591 assert(pCellStr != nullptr);
2593 const sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH ||
2594 rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2595 (pCellStr->length - pQuer->length) : 0;
2597 if (nIndex < 0)
2598 nStrPos = -1;
2599 else if (rEntry.eOp == SC_EQUAL ||
2600 rEntry.eOp == SC_NOT_EQUAL)
2602 nStrPos = pCellStr == pQuer ? 0 : -1;
2604 else
2605 { // OUString::indexOf
2606 nStrPos = rtl_ustr_indexOfStr_WithLength(
2607 pCellStr->buffer + nIndex, pCellStr->length - nIndex,
2608 pQuer->buffer, pQuer->length );
2610 if (nStrPos >= 0)
2611 nStrPos += nIndex;
2614 else
2616 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2617 const OUString aQueryStr = rItem.maString.getString();
2618 const LanguageType nLang = ScGlobal::xSysLocale->GetLanguageTag().getLanguageType();
2619 setupTransliteratorIfNeeded();
2620 const OUString aCell( mpTransliteration->transliterate(
2621 rValue, nLang, 0, rValue.getLength(),
2622 nullptr ) );
2624 const OUString aQuer( mpTransliteration->transliterate(
2625 aQueryStr, nLang, 0, aQueryStr.getLength(),
2626 nullptr ) );
2628 const sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2629 (aCell.getLength() - aQuer.getLength()) : 0;
2630 nStrPos = ((nIndex < 0) ? -1 : aCell.indexOf( aQuer, nIndex ));
2632 switch (rEntry.eOp)
2634 case SC_EQUAL:
2635 bOk = ( nStrPos == 0 );
2636 break;
2637 case SC_CONTAINS:
2638 bOk = ( nStrPos != -1 );
2639 break;
2640 case SC_NOT_EQUAL:
2641 bOk = ( nStrPos != 0 );
2642 break;
2643 case SC_DOES_NOT_CONTAIN:
2644 bOk = ( nStrPos == -1 );
2645 break;
2646 case SC_BEGINS_WITH:
2647 bOk = ( nStrPos == 0 );
2648 break;
2649 case SC_DOES_NOT_BEGIN_WITH:
2650 bOk = ( nStrPos != 0 );
2651 break;
2652 case SC_ENDS_WITH:
2653 bOk = ( nStrPos >= 0 );
2654 break;
2655 case SC_DOES_NOT_END_WITH:
2656 bOk = ( nStrPos < 0 );
2657 break;
2658 default:
2660 // added to avoid warnings
2665 else
2666 { // use collator here because data was probably sorted
2667 const OUString & rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
2668 setupCollatorIfNeeded();
2669 sal_Int32 nCompare = mpCollator->compareString(
2670 rValue, rItem.maString.getString());
2671 switch (rEntry.eOp)
2673 case SC_LESS :
2674 bOk = (nCompare < 0);
2675 break;
2676 case SC_GREATER :
2677 bOk = (nCompare > 0);
2678 break;
2679 case SC_LESS_EQUAL :
2680 bOk = (nCompare <= 0);
2681 if ( bOk && mpTestEqualCondition && !bTestEqual )
2682 bTestEqual = (nCompare == 0);
2683 break;
2684 case SC_GREATER_EQUAL :
2685 bOk = (nCompare >= 0);
2686 if ( bOk && mpTestEqualCondition && !bTestEqual )
2687 bTestEqual = (nCompare == 0);
2688 break;
2689 default:
2691 // added to avoid warnings
2697 return std::pair<bool,bool>(bOk, bTestEqual);
2700 std::pair<bool, bool> compareByTextColor(SCCOL nCol, SCROW nRow, SCTAB nTab,
2701 const ScQueryEntry::Item& rItem)
2703 ScAddress aPos(nCol, nRow, nTab);
2704 const SvxColorItem* pColor = mrDoc.GetAttr(aPos, ATTR_FONT_COLOR);
2705 Color color = pColor->GetValue();
2706 bool bMatch = rItem.maColor == color;
2707 return std::pair<bool, bool>(bMatch, false);
2710 std::pair<bool, bool> compareByBackgroundColor(SCCOL nCol, SCROW nRow, SCTAB nTab,
2711 const ScQueryEntry::Item& rItem)
2713 ScAddress aPos(nCol, nRow, nTab);
2714 const SvxBrushItem* pBrush = mrDoc.GetAttr(aPos, ATTR_BACKGROUND);
2715 Color color = pBrush->GetColor();
2716 bool bMatch = rItem.maColor == color;
2717 return std::pair<bool, bool>(bMatch, false);
2720 // To be called only if both isQueryByValue() and isQueryByString()
2721 // returned false and range lookup is wanted! In range lookup comparison
2722 // numbers are less than strings. Nothing else is compared.
2723 std::pair<bool,bool> compareByRangeLookup(
2724 const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2725 const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
2727 bool bTestEqual = false;
2729 if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS && rEntry.eOp != SC_LESS_EQUAL)
2730 return std::pair<bool,bool>(false, bTestEqual);
2732 if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER && rEntry.eOp != SC_GREATER_EQUAL)
2733 return std::pair<bool,bool>(false, bTestEqual);
2735 if (!rCell.isEmpty())
2737 if (rItem.meType == ScQueryEntry::ByString)
2739 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode() != FormulaError::NONE)
2740 // Error values are compared as string.
2741 return std::pair<bool,bool>(false, bTestEqual);
2743 return std::pair<bool,bool>(rCell.hasNumeric(), bTestEqual);
2746 return std::pair<bool,bool>(!rCell.hasNumeric(), bTestEqual);
2749 if (rItem.meType == ScQueryEntry::ByString)
2750 return std::pair<bool,bool>(mrTab.HasValueData(nCol, nRow), bTestEqual);
2752 return std::pair<bool,bool>(!mrTab.HasValueData(nCol, nRow), bTestEqual);
2758 bool ScTable::ValidQuery(
2759 SCROW nRow, const ScQueryParam& rParam, const ScRefCellValue* pCell, bool* pbTestEqualCondition,
2760 const ScInterpreterContext* pContext, sc::TableColumnBlockPositionSet* pBlockPos)
2762 if (!rParam.GetEntry(0).bDoQuery)
2763 return true;
2765 //---------------------------------------------------------------
2767 const SCSIZE nFixedBools = 32;
2768 bool aBool[nFixedBools];
2769 bool aTest[nFixedBools];
2770 SCSIZE nEntryCount = rParam.GetEntryCount();
2771 bool* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new bool[nEntryCount] );
2772 bool* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new bool[nEntryCount] );
2774 tools::Long nPos = -1;
2775 QueryEvaluator aEval(rDocument, *this, rParam, pbTestEqualCondition != nullptr);
2776 ScQueryParam::const_iterator it, itBeg = rParam.begin(), itEnd = rParam.end();
2777 for (it = itBeg; it != itEnd && (*it)->bDoQuery; ++it)
2779 const ScQueryEntry& rEntry = **it;
2780 SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
2782 // We can only handle one single direct query passed as a known pCell,
2783 // subsequent queries have to obtain the cell.
2784 ScRefCellValue aCell;
2785 if(pCell && it == itBeg)
2786 aCell = *pCell;
2787 else if( pBlockPos )
2788 { // hinted mdds access
2789 ScColumn* column = FetchColumn(nCol);
2790 aCell = column->GetCellValue(*pBlockPos->getBlockPosition( nCol ), nRow);
2792 else
2793 aCell = GetCellValue(nCol, nRow);
2795 std::pair<bool,bool> aRes(false, false);
2797 const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2798 if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
2800 bool hasData;
2801 if( pBlockPos )
2803 ScColumn* column = FetchColumn(rEntry.nField);
2804 hasData = column->HasDataAt(*pBlockPos->getBlockPosition(rEntry.nField), nRow);
2806 else
2807 hasData = aCol[rEntry.nField].HasDataAt(nRow);
2808 if (rEntry.IsQueryByEmpty())
2809 aRes.first = !hasData;
2810 else
2812 assert(rEntry.IsQueryByNonEmpty());
2813 aRes.first = hasData;
2816 else
2818 for (const auto& rItem : rItems)
2820 if (rItem.meType == ScQueryEntry::ByTextColor)
2822 std::pair<bool, bool> aThisRes
2823 = aEval.compareByTextColor(nCol, nRow, nTab, rItem);
2824 aRes.first |= aThisRes.first;
2825 aRes.second |= aThisRes.second;
2827 else if (rItem.meType == ScQueryEntry::ByBackgroundColor)
2829 std::pair<bool,bool> aThisRes =
2830 aEval.compareByBackgroundColor(nCol, nRow, nTab, rItem);
2831 aRes.first |= aThisRes.first;
2832 aRes.second |= aThisRes.second;
2834 else if (aEval.isQueryByValue(rItem, nCol, nRow, aCell))
2836 std::pair<bool,bool> aThisRes =
2837 aEval.compareByValue(aCell, nCol, nRow, rEntry, rItem, pContext);
2838 aRes.first |= aThisRes.first;
2839 aRes.second |= aThisRes.second;
2841 else if (aEval.isQueryByString(rEntry, rItem, nCol, nRow, aCell))
2843 std::pair<bool,bool> aThisRes =
2844 aEval.compareByString(aCell, nRow, rEntry, rItem, pContext);
2845 aRes.first |= aThisRes.first;
2846 aRes.second |= aThisRes.second;
2848 else if (rParam.mbRangeLookup)
2850 std::pair<bool,bool> aThisRes =
2851 aEval.compareByRangeLookup(aCell, nCol, nRow, rEntry, rItem);
2852 aRes.first |= aThisRes.first;
2853 aRes.second |= aThisRes.second;
2856 if (aRes.first && aRes.second)
2857 break;
2861 if (nPos == -1)
2863 nPos++;
2864 pPasst[nPos] = aRes.first;
2865 pTest[nPos] = aRes.second;
2867 else
2869 if (rEntry.eConnect == SC_AND)
2871 pPasst[nPos] = pPasst[nPos] && aRes.first;
2872 pTest[nPos] = pTest[nPos] && aRes.second;
2874 else
2876 nPos++;
2877 pPasst[nPos] = aRes.first;
2878 pTest[nPos] = aRes.second;
2883 for ( tools::Long j=1; j <= nPos; j++ )
2885 pPasst[0] = pPasst[0] || pPasst[j];
2886 pTest[0] = pTest[0] || pTest[j];
2889 bool bRet = pPasst[0];
2890 if ( pPasst != &aBool[0] )
2891 delete [] pPasst;
2892 if ( pbTestEqualCondition )
2893 *pbTestEqualCondition = pTest[0];
2894 if ( pTest != &aTest[0] )
2895 delete [] pTest;
2897 return bRet;
2900 void ScTable::TopTenQuery( ScQueryParam& rParam )
2902 bool bSortCollatorInitialized = false;
2903 SCSIZE nEntryCount = rParam.GetEntryCount();
2904 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
2905 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
2906 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
2908 ScQueryEntry& rEntry = rParam.GetEntry(i);
2909 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
2911 switch ( rEntry.eOp )
2913 case SC_TOPVAL:
2914 case SC_BOTVAL:
2915 case SC_TOPPERC:
2916 case SC_BOTPERC:
2918 ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) );
2919 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
2920 if ( !bSortCollatorInitialized )
2922 bSortCollatorInitialized = true;
2923 InitSortCollator( aLocalSortParam );
2925 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
2926 DecoladeRow( pArray.get(), nRow1, rParam.nRow2 );
2927 QuickSort( pArray.get(), nRow1, rParam.nRow2 );
2928 std::unique_ptr<ScSortInfo[]> const & ppInfo = pArray->GetFirstArray();
2929 SCSIZE nValidCount = nCount;
2930 // Don't count note or blank cells, they are sorted to the end
2931 while (nValidCount > 0 && ppInfo[nValidCount-1].maCell.isEmpty())
2932 nValidCount--;
2933 // Don't count Strings, they are between Value and blank
2934 while (nValidCount > 0 && ppInfo[nValidCount-1].maCell.hasString())
2935 nValidCount--;
2936 if ( nValidCount > 0 )
2938 if ( rItem.meType == ScQueryEntry::ByString )
2939 { // by string ain't going to work
2940 rItem.meType = ScQueryEntry::ByValue;
2941 rItem.mfVal = 10; // 10 and 10% respectively
2943 SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
2944 SCSIZE nOffset = 0;
2945 switch ( rEntry.eOp )
2947 case SC_TOPVAL:
2949 rEntry.eOp = SC_GREATER_EQUAL;
2950 if ( nVal > nValidCount )
2951 nVal = nValidCount;
2952 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
2954 break;
2955 case SC_BOTVAL:
2957 rEntry.eOp = SC_LESS_EQUAL;
2958 if ( nVal > nValidCount )
2959 nVal = nValidCount;
2960 nOffset = nVal - 1; // 1 <= nVal <= nValidCount
2962 break;
2963 case SC_TOPPERC:
2965 rEntry.eOp = SC_GREATER_EQUAL;
2966 if ( nVal > 100 )
2967 nVal = 100;
2968 nOffset = nValidCount - (nValidCount * nVal / 100);
2969 if ( nOffset >= nValidCount )
2970 nOffset = nValidCount - 1;
2972 break;
2973 case SC_BOTPERC:
2975 rEntry.eOp = SC_LESS_EQUAL;
2976 if ( nVal > 100 )
2977 nVal = 100;
2978 nOffset = (nValidCount * nVal / 100);
2979 if ( nOffset >= nValidCount )
2980 nOffset = nValidCount - 1;
2982 break;
2983 default:
2985 // added to avoid warnings
2988 ScRefCellValue aCell = ppInfo[nOffset].maCell;
2989 if (aCell.hasNumeric())
2990 rItem.mfVal = aCell.getValue();
2991 else
2993 OSL_FAIL( "TopTenQuery: pCell no ValueData" );
2994 rEntry.eOp = SC_GREATER_EQUAL;
2995 rItem.mfVal = 0;
2998 else
3000 rEntry.eOp = SC_GREATER_EQUAL;
3001 rItem.meType = ScQueryEntry::ByValue;
3002 rItem.mfVal = 0;
3005 break;
3006 default:
3008 // added to avoid warnings
3012 if ( bSortCollatorInitialized )
3013 DestroySortCollator();
3016 namespace {
3018 bool CanOptimizeQueryStringToNumber( SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat )
3020 // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
3021 // The problem with this optimization is that the autofilter dialog apparently converts
3022 // the value to text and then converts that back to a number for filtering.
3023 // If that leads to any change of value (such as when time is rounded to seconds),
3024 // even matching values will be filtered out. Therefore query by value only for formats
3025 // where no such change should occur.
3026 if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex))
3028 switch(pEntry->GetType())
3030 case SvNumFormatType::NUMBER:
3031 case SvNumFormatType::FRACTION:
3032 case SvNumFormatType::SCIENTIFIC:
3033 return true;
3034 case SvNumFormatType::DATE:
3035 case SvNumFormatType::DATETIME:
3036 bDateFormat = true;
3037 break;
3038 default:
3039 break;
3042 return false;
3045 class PrepareQueryItem
3047 const ScDocument& mrDoc;
3048 public:
3049 explicit PrepareQueryItem(const ScDocument& rDoc) : mrDoc(rDoc) {}
3051 void operator() (ScQueryEntry::Item& rItem)
3053 if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
3054 return;
3056 if (rItem.mbFormattedValue)
3057 return;
3059 sal_uInt32 nIndex = 0;
3060 bool bNumber = mrDoc.GetFormatTable()->
3061 IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
3063 // Advanced Filter creates only ByString queries that need to be
3064 // converted to ByValue if appropriate. rItem.mfVal now holds the value
3065 // if bNumber==true.
3067 if (rItem.meType == ScQueryEntry::ByString)
3069 bool bDateFormat = false;
3070 if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat ))
3071 rItem.meType = ScQueryEntry::ByValue;
3072 if (!bDateFormat)
3073 return;
3076 // Double-check if the query by date is really appropriate.
3078 if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
3080 const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
3081 if (pEntry)
3083 SvNumFormatType nNumFmtType = pEntry->GetType();
3084 if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME))
3085 rItem.meType = ScQueryEntry::ByValue; // not a date only
3086 else
3087 rItem.meType = ScQueryEntry::ByDate; // date only
3089 else
3090 rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
3092 else
3093 rItem.meType = ScQueryEntry::ByValue; // not a date
3097 void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam )
3099 bool bTopTen = false;
3100 SCSIZE nEntryCount = rParam.GetEntryCount();
3102 for ( SCSIZE i = 0; i < nEntryCount; ++i )
3104 ScQueryEntry& rEntry = rParam.GetEntry(i);
3105 if (!rEntry.bDoQuery)
3106 continue;
3108 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
3109 std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc));
3111 if ( !bTopTen )
3113 switch ( rEntry.eOp )
3115 case SC_TOPVAL:
3116 case SC_BOTVAL:
3117 case SC_TOPPERC:
3118 case SC_BOTPERC:
3120 bTopTen = true;
3122 break;
3123 default:
3130 if ( bTopTen )
3132 pTab->TopTenQuery( rParam );
3138 void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
3140 lcl_PrepareQuery(&rDocument, this, rQueryParam);
3143 SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
3145 ScQueryParam aParam( rParamOrg );
3146 typedef std::unordered_set<OUString> StrSetType;
3147 StrSetType aStrSet;
3149 bool bStarted = false;
3150 bool bOldResult = true;
3151 SCROW nOldStart = 0;
3152 SCROW nOldEnd = 0;
3154 SCSIZE nCount = 0;
3155 SCROW nOutRow = 0;
3156 SCROW nHeader = aParam.bHasHeader ? 1 : 0;
3158 lcl_PrepareQuery(&rDocument, this, aParam);
3160 if (!aParam.bInplace)
3162 nOutRow = aParam.nDestRow + nHeader;
3163 if (nHeader > 0)
3164 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
3165 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
3168 sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access
3170 SCROW nRealRow2 = aParam.nRow2;
3171 for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
3173 bool bResult; // Filter result
3174 bool bValid = ValidQuery(j, aParam, nullptr, nullptr, nullptr, &blockPos);
3175 if (!bValid && bKeepSub) // Keep subtotals
3177 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
3179 ScRefCellValue aCell = GetCellValue(nCol, j);
3180 if (aCell.meType != CELLTYPE_FORMULA)
3181 continue;
3183 if (!aCell.mpFormula->IsSubTotal())
3184 continue;
3186 if (RefVisible(aCell.mpFormula))
3187 bValid = true;
3190 if (bValid)
3192 if (aParam.bDuplicate)
3193 bResult = true;
3194 else
3196 OUStringBuffer aStr;
3197 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
3199 OUString aCellStr;
3200 GetString(k, j, aCellStr);
3201 aStr.append(aCellStr + u"\x0001");
3204 bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted.
3207 else
3208 bResult = false;
3210 if (aParam.bInplace)
3212 if (bResult == bOldResult && bStarted)
3213 nOldEnd = j;
3214 else
3216 if (bStarted)
3217 DBShowRows(nOldStart,nOldEnd, bOldResult);
3218 nOldStart = nOldEnd = j;
3219 bOldResult = bResult;
3221 bStarted = true;
3223 else
3225 if (bResult)
3227 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
3228 if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints
3229 blockPos.invalidate();
3230 ++nOutRow;
3233 if (bResult)
3234 ++nCount;
3237 if (aParam.bInplace && bStarted)
3238 DBShowRows(nOldStart,nOldEnd, bOldResult);
3240 if (aParam.bInplace)
3241 SetDrawPageSize();
3243 return nCount;
3246 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3248 bool bValid = true;
3249 std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]);
3250 OUString aCellStr;
3251 SCCOL nCol = nCol1;
3252 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3253 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3254 SCROW nDBRow1 = rQueryParam.nRow1;
3255 SCCOL nDBCol2 = rQueryParam.nCol2;
3256 // First row must be column headers
3257 while (bValid && (nCol <= nCol2))
3259 OUString aQueryStr;
3260 GetUpperCellString(nCol, nRow1, aQueryStr);
3261 bool bFound = false;
3262 SCCOL i = rQueryParam.nCol1;
3263 while (!bFound && (i <= nDBCol2))
3265 if ( nTab == nDBTab )
3266 GetUpperCellString(i, nDBRow1, aCellStr);
3267 else
3268 rDocument.GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
3269 bFound = (aCellStr == aQueryStr);
3270 if (!bFound) i++;
3272 if (bFound)
3273 pFields[nCol - nCol1] = i;
3274 else
3275 bValid = false;
3276 nCol++;
3278 if (bValid)
3280 sal_uLong nVisible = 0;
3281 for ( nCol=nCol1; nCol<=nCol2; nCol++ )
3282 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
3284 if ( nVisible > SCSIZE_MAX / sizeof(void*) )
3286 OSL_FAIL("too many filter criteria");
3287 nVisible = 0;
3290 SCSIZE nNewEntries = nVisible;
3291 rQueryParam.Resize( nNewEntries );
3293 SCSIZE nIndex = 0;
3294 SCROW nRow = nRow1 + 1;
3295 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
3296 while (nRow <= nRow2)
3298 nCol = nCol1;
3299 while (nCol <= nCol2)
3301 GetInputString( nCol, nRow, aCellStr );
3302 if (!aCellStr.isEmpty())
3304 if (nIndex < nNewEntries)
3306 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
3307 rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
3308 nIndex++;
3309 if (nIndex < nNewEntries)
3310 rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
3312 else
3313 bValid = false;
3315 nCol++;
3317 nRow++;
3318 if (nIndex < nNewEntries)
3319 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
3322 return bValid;
3325 bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3327 // A valid StarQuery must be at least 4 columns wide. To be precise it
3328 // should be exactly 4 columns ...
3329 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
3330 // column Excel style query range immediately left to itself would result
3331 // in a circular reference when the field name or operator or value (first
3332 // to third query range column) is obtained (#i58354#). Furthermore, if the
3333 // range wasn't sufficiently specified data changes wouldn't flag formula
3334 // cells for recalculation.
3335 if (nCol2 - nCol1 < 3)
3336 return false;
3338 bool bValid;
3339 OUString aCellStr;
3340 SCSIZE nIndex = 0;
3341 SCROW nRow = nRow1;
3342 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3343 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3344 SCROW nDBRow1 = rQueryParam.nRow1;
3345 SCCOL nDBCol2 = rQueryParam.nCol2;
3347 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
3348 rQueryParam.Resize( nNewEntries );
3349 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
3353 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
3355 bValid = false;
3356 // First column AND/OR
3357 if (nIndex > 0)
3359 GetUpperCellString(nCol1, nRow, aCellStr);
3360 if ( aCellStr == ScResId(STR_TABLE_AND) )
3362 rEntry.eConnect = SC_AND;
3363 bValid = true;
3365 else if ( aCellStr == ScResId(STR_TABLE_OR) )
3367 rEntry.eConnect = SC_OR;
3368 bValid = true;
3371 // Second column field name
3372 if ((nIndex < 1) || bValid)
3374 bool bFound = false;
3375 GetUpperCellString(nCol1 + 1, nRow, aCellStr);
3376 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
3378 OUString aFieldStr;
3379 if ( nTab == nDBTab )
3380 GetUpperCellString(i, nDBRow1, aFieldStr);
3381 else
3382 rDocument.GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
3383 bFound = (aCellStr == aFieldStr);
3384 if (bFound)
3386 rEntry.nField = i;
3387 bValid = true;
3389 else
3390 bValid = false;
3393 // Third column operator =<>...
3394 if (bValid)
3396 GetUpperCellString(nCol1 + 2, nRow, aCellStr);
3397 if (aCellStr.startsWith("<"))
3399 if (aCellStr[1] == '>')
3400 rEntry.eOp = SC_NOT_EQUAL;
3401 else if (aCellStr[1] == '=')
3402 rEntry.eOp = SC_LESS_EQUAL;
3403 else
3404 rEntry.eOp = SC_LESS;
3406 else if (aCellStr.startsWith(">"))
3408 if (aCellStr[1] == '=')
3409 rEntry.eOp = SC_GREATER_EQUAL;
3410 else
3411 rEntry.eOp = SC_GREATER;
3413 else if (aCellStr.startsWith("="))
3414 rEntry.eOp = SC_EQUAL;
3417 // Fourth column values
3418 if (bValid)
3420 OUString aStr;
3421 GetString(nCol1 + 3, nRow, aStr);
3422 rEntry.GetQueryItem().maString = rPool.intern(aStr);
3423 rEntry.bDoQuery = true;
3425 nIndex++;
3426 nRow++;
3428 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
3429 return bValid;
3432 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3434 SCSIZE i, nCount;
3435 PutInOrder(nCol1, nCol2);
3436 PutInOrder(nRow1, nRow2);
3438 nCount = rQueryParam.GetEntryCount();
3439 for (i=0; i < nCount; i++)
3440 rQueryParam.GetEntry(i).Clear();
3442 // Standard query table
3443 bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3444 // Excel Query table
3445 if (!bValid)
3446 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3448 SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
3449 nCount = rQueryParam.GetEntryCount();
3450 if (bValid)
3452 // query type must be set
3453 for (i=0; i < nCount; i++)
3455 ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem();
3456 sal_uInt32 nIndex = 0;
3457 bool bNumber = pFormatter->IsNumberFormat(
3458 rItem.maString.getString(), nIndex, rItem.mfVal);
3459 bool bDateFormat = false;
3460 rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat )
3461 ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString);
3464 else
3466 for (i=0; i < nCount; i++)
3467 rQueryParam.GetEntry(i).Clear();
3469 return bValid;
3472 bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
3474 if (nStartRow == nEndRow)
3475 // Assume only data.
3476 /* XXX NOTE: previous behavior still checked this one row and could
3477 * evaluate it has header row, but that doesn't make much sense. */
3478 return false;
3480 if (nStartCol == nEndCol)
3482 CellType eFirstCellType = GetCellType(nStartCol, nStartRow);
3483 CellType eSecondCellType = GetCellType(nStartCol, nStartRow+1);
3484 return ((eFirstCellType == CELLTYPE_STRING || eFirstCellType == CELLTYPE_EDIT) &&
3485 (eSecondCellType != CELLTYPE_STRING && eSecondCellType != CELLTYPE_EDIT));
3488 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3490 CellType eType = GetCellType( nCol, nStartRow );
3491 // Any non-text cell in first row => not headers.
3492 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3493 return false;
3496 // First row all text cells, any non-text cell in second row => headers.
3497 SCROW nTestRow = nStartRow + 1;
3498 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3500 CellType eType = GetCellType( nCol, nTestRow );
3501 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3502 return true;
3505 // Also second row all text cells => first row not headers.
3506 return false;
3509 bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
3511 if (nStartCol == nEndCol)
3512 // Assume only data.
3513 /* XXX NOTE: previous behavior still checked this one column and could
3514 * evaluate it has header column, but that doesn't make much sense. */
3515 return false;
3517 if (nStartRow == nEndRow)
3519 CellType eFirstCellType = GetCellType(nStartCol, nStartRow);
3520 CellType eSecondCellType = GetCellType(nStartCol+1, nStartRow);
3521 return ((eFirstCellType == CELLTYPE_STRING || eFirstCellType == CELLTYPE_EDIT) &&
3522 (eSecondCellType != CELLTYPE_STRING && eSecondCellType != CELLTYPE_EDIT));
3525 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3527 CellType eType = GetCellType( nStartCol, nRow );
3528 // Any non-text cell in first column => not headers.
3529 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3530 return false;
3533 // First column all text cells, any non-text cell in second column => headers.
3534 SCCOL nTestCol = nStartCol + 1;
3535 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3537 CellType eType = GetCellType( nRow, nTestCol );
3538 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3539 return true;
3542 // Also second column all text cells => first column not headers.
3543 return false;
3546 void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries )
3548 sc::ColumnBlockConstPosition aBlockPos;
3549 aCol[nCol].InitBlockPosition(aBlockPos);
3550 aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, false);
3553 void ScTable::GetFilteredFilterEntries(
3554 SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering )
3556 sc::ColumnBlockConstPosition aBlockPos;
3557 aCol[nCol].InitBlockPosition(aBlockPos);
3559 // remove the entry for this column from the query parameter
3560 ScQueryParam aParam( rParam );
3561 aParam.RemoveEntryByField(nCol);
3563 lcl_PrepareQuery(&rDocument, this, aParam);
3564 for ( SCROW j = nRow1; j <= nRow2; ++j )
3566 if (ValidQuery(j, aParam))
3568 aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering);
3573 bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings, bool bLimit)
3575 return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit );
3578 sal_uLong ScTable::GetCellCount() const
3580 sal_uLong nCellCount = 0;
3582 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3583 nCellCount += aCol[nCol].GetCellCount();
3585 return nCellCount;
3588 sal_uLong ScTable::GetWeightedCount() const
3590 sal_uLong nCellCount = 0;
3592 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3593 nCellCount += aCol[nCol].GetWeightedCount();
3595 return nCellCount;
3598 sal_uLong ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
3600 sal_uLong nCellCount = 0;
3602 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3603 nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow);
3605 return nCellCount;
3608 sal_uLong ScTable::GetCodeCount() const
3610 sal_uLong nCodeCount = 0;
3612 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3613 if ( aCol[nCol].GetCellCount() )
3614 nCodeCount += aCol[nCol].GetCodeCount();
3616 return nCodeCount;
3619 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
3620 SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
3622 if ( IsColValid( nCol ) )
3623 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
3624 else
3625 return 0;
3628 sal_Int32 ScTable::GetMaxNumberStringLen(
3629 sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
3631 if ( IsColValid( nCol ) )
3632 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
3633 else
3634 return 0;
3637 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
3639 ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab );
3640 ScRange aMarkArea( ScAddress::UNINITIALIZED );
3641 if (rMark.IsMultiMarked())
3642 rMark.GetMultiMarkArea( aMarkArea );
3643 else if (rMark.IsMarked())
3644 rMark.GetMarkArea( aMarkArea );
3645 else
3647 assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
3648 aMarkArea.aStart.SetCol(0);
3649 aMarkArea.aEnd.SetCol(rDocument.MaxCol());
3651 const SCCOL nStartCol = aMarkArea.aStart.Col();
3652 const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col());
3653 for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol)
3655 if (mpColFlags && ColHidden(nCol))
3656 continue;
3658 aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */