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