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