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