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