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 <svl/numformat.hxx>
23 #include <svl/zforlist.hxx>
24 #include <svl/zformat.hxx>
25 #include <unotools/collatorwrapper.hxx>
27 #include <com/sun/star/i18n/KParseTokens.hpp>
28 #include <com/sun/star/i18n/KParseType.hpp>
29 #include <sal/log.hxx>
30 #include <osl/diagnose.h>
32 #include <refdata.hxx>
34 #include <scitems.hxx>
35 #include <formulacell.hxx>
36 #include <document.hxx>
37 #include <globstr.hrc>
38 #include <scresid.hxx>
40 #include <stlpool.hxx>
41 #include <patattr.hxx>
42 #include <subtotal.hxx>
43 #include <markdata.hxx>
44 #include <rangelst.hxx>
45 #include <userlist.hxx>
46 #include <progress.hxx>
47 #include <queryparam.hxx>
48 #include <queryentry.hxx>
49 #include <subtotalparam.hxx>
50 #include <cellvalue.hxx>
51 #include <tokenarray.hxx>
52 #include <mtvcellfunc.hxx>
53 #include <columnspanset.hxx>
54 #include <fstalgorithm.hxx>
55 #include <listenercontext.hxx>
56 #include <sharedformula.hxx>
57 #include <stlsheet.hxx>
58 #include <refhint.hxx>
59 #include <listenerquery.hxx>
60 #include <bcaslot.hxx>
61 #include <reordermap.hxx>
62 #include <drwlayer.hxx>
63 #include <queryevaluator.hxx>
64 #include <scopetools.hxx>
66 #include <svl/sharedstringpool.hxx>
70 #include <unordered_set>
72 #include <mdds/flat_segment_tree.hpp>
74 using namespace ::com::sun::star
;
76 namespace naturalsort
{
78 using namespace ::com::sun::star::i18n
;
80 /** Splits a given string into three parts: the prefix, number string, and
84 Original string to be split into pieces
87 Prefix string that consists of the part before the first number token.
88 If no number was found, sPrefix is unchanged.
91 String after the last number token. This may still contain number strings.
92 If no number was found, sSuffix is unchanged.
95 Number converted from the middle number string
96 If no number was found, fNum is unchanged.
98 @return Returns TRUE if a numeral element is found in a given string, or
99 FALSE if no numeral element is found.
101 static bool SplitString( const OUString
&sWhole
,
102 OUString
&sPrefix
, OUString
&sSuffix
, double &fNum
)
104 // Get prefix element, search for any digit and stop.
106 while (nPos
< sWhole
.getLength())
108 const sal_uInt16 nType
= ScGlobal::getCharClass().getCharacterType( sWhole
, nPos
);
109 if (nType
& KCharacterType::DIGIT
)
111 sWhole
.iterateCodePoints( &nPos
);
114 // Return FALSE if no numeral element is found
115 if ( nPos
== sWhole
.getLength() )
118 // Get numeral element
119 const OUString
& sUser
= ScGlobal::getLocaleData().getNumDecimalSep();
120 ParseResult aPRNum
= ScGlobal::getCharClass().parsePredefinedToken(
121 KParseType::ANY_NUMBER
, sWhole
, nPos
,
122 KParseTokens::ANY_NUMBER
, u
""_ustr
, KParseTokens::ANY_NUMBER
, sUser
);
124 if ( aPRNum
.EndPos
== nPos
)
126 SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
127 nPos
<< " : " << sWhole
);
131 sPrefix
= sWhole
.copy( 0, nPos
);
133 sSuffix
= sWhole
.copy( aPRNum
.EndPos
);
138 /** Naturally compares two given strings.
140 This is the main function that should be called externally. It returns
141 either 1, 0, or -1 depending on the comparison result of given two strings.
150 Boolean value for case sensitivity
153 Pointer to user defined sort list
156 Pointer to collator wrapper for normal string comparison
158 @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
161 static short Compare( const OUString
&sInput1
, const OUString
&sInput2
,
162 const bool bCaseSens
, const ScUserListData
* pData
, const CollatorWrapper
*pCW
)
164 OUString
sStr1( sInput1
), sStr2( sInput2
), sPre1
, sSuf1
, sPre2
, sSuf2
;
169 bool bNumFound1
= SplitString( sStr1
, sPre1
, sSuf1
, nNum1
);
170 bool bNumFound2
= SplitString( sStr2
, sPre2
, sSuf2
, nNum2
);
172 short nPreRes
; // Prefix comparison result
177 if ( !bNumFound1
|| !bNumFound2
)
178 return static_cast<short>(pData
->Compare( sStr1
, sStr2
));
180 nPreRes
= pData
->Compare( sPre1
, sPre2
);
184 if ( !bNumFound1
|| !bNumFound2
)
185 return static_cast<short>(pData
->ICompare( sStr1
, sStr2
));
187 nPreRes
= pData
->ICompare( sPre1
, sPre2
);
192 if ( !bNumFound1
|| !bNumFound2
)
193 return static_cast<short>(pCW
->compareString( sStr1
, sStr2
));
195 nPreRes
= static_cast<short>(pCW
->compareString( sPre1
, sPre2
));
198 // Prefix strings differ. Return immediately.
199 if ( nPreRes
!= 0 ) return nPreRes
;
201 if ( nNum1
!= nNum2
)
203 if ( nNum1
< nNum2
) return -1;
204 return (nNum1
> nNum2
) ? 1 : 0;
207 // The prefix and the first numerical elements are equal, but the suffix
208 // strings may still differ. Stay in the loop.
220 // Assume that we can handle 512MB, which with a ~100 bytes
221 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
222 // overhead in one chunk.
223 constexpr sal_Int32 kSortCellsChunk
= 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell
);
228 ScSortInfoArray
& rArray
, ScTable
& rTab
, ScColContainer
& rCols
,
229 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
230 bool bHiddenFiltered
, bool bPattern
, bool bCellNotes
, bool bCellDrawObjects
, bool bOnlyDataAreaExtras
)
232 // Fill row-wise data table.
233 ScSortInfoArray::RowsType
& rRows
= rArray
.InitDataRows(nRow2
-nRow1
+1, nCol2
-nCol1
+1);
235 const std::vector
<SCCOLROW
>& rOrderIndices
= rArray
.GetOrderIndices();
236 assert(!bOnlyDataAreaExtras
|| (rOrderIndices
.size() == static_cast<size_t>(nRow2
- nRow1
+ 1)
237 && nRow1
== rArray
.GetStart()));
239 ScDrawLayer
* pDrawLayer
= (bCellDrawObjects
? rTab
.GetDoc().GetDrawLayer() : nullptr);
240 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
242 ScColumn
& rCol
= rCols
[nCol
];
244 // Skip reordering of cell formats if the whole span is on the same pattern entry.
245 bool bUniformPattern
= rCol
.GetPatternCount(nRow1
, nRow2
) < 2u;
247 sc::ColumnBlockConstPosition aBlockPos
;
248 rCol
.InitBlockPosition(aBlockPos
);
249 std::map
<SCROW
, std::vector
<SdrObject
*>> aRowDrawObjects
;
251 aRowDrawObjects
= pDrawLayer
->GetObjectsAnchoredToRange(rTab
.GetTab(), nCol
, nRow1
, nRow2
);
253 for (SCROW nR
= nRow1
; nR
<= nRow2
; ++nR
)
255 const SCROW nRow
= (bOnlyDataAreaExtras
? rOrderIndices
[nR
- rArray
.GetStart()] : nR
);
256 ScSortInfoArray::Row
& rRow
= rRows
[nR
-nRow1
];
257 ScSortInfoArray::Cell
& rCell
= rRow
.maCells
[nCol
-nCol1
];
258 if (!bOnlyDataAreaExtras
)
260 rCell
.maCell
= rCol
.GetCellValue(aBlockPos
, nRow
);
261 rCell
.mpAttr
= rCol
.GetCellTextAttr(aBlockPos
, nRow
);
264 rCell
.mpNote
= rCol
.GetCellNote(aBlockPos
, nRow
);
266 rCell
.maDrawObjects
= aRowDrawObjects
[nRow
];
268 if (!bUniformPattern
&& bPattern
)
269 rCell
.maPattern
.setScPatternAttr(rCol
.GetPattern(nRow
));
273 if (!bOnlyDataAreaExtras
&& bHiddenFiltered
)
275 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
277 ScSortInfoArray::Row
& rRow
= rRows
[nRow
-nRow1
];
278 rRow
.mbHidden
= rTab
.RowHidden(nRow
);
279 rRow
.mbFiltered
= rTab
.RowFiltered(nRow
);
286 std::unique_ptr
<ScSortInfoArray
> ScTable::CreateSortInfoArray( const sc::ReorderParam
& rParam
)
288 std::unique_ptr
<ScSortInfoArray
> pArray
;
292 // Create a sort info array with just the data table.
293 SCROW nRow1
= rParam
.maSortRange
.aStart
.Row();
294 SCROW nRow2
= rParam
.maSortRange
.aEnd
.Row();
295 SCCOL nCol1
= rParam
.maSortRange
.aStart
.Col();
296 SCCOL nCol2
= rParam
.maSortRange
.aEnd
.Col();
298 pArray
.reset(new ScSortInfoArray(0, nRow1
, nRow2
));
299 pArray
->SetKeepQuery(rParam
.mbHiddenFiltered
);
300 pArray
->SetUpdateRefs(rParam
.mbUpdateRefs
);
302 CreateColumnIfNotExists(nCol2
);
303 initDataRows( *pArray
, *this, aCol
, nCol1
, nRow1
, nCol2
, nRow2
, rParam
.mbHiddenFiltered
,
304 rParam
.maDataAreaExtras
.mbCellFormats
, true, true, false);
308 SCCOLROW nCol1
= rParam
.maSortRange
.aStart
.Col();
309 SCCOLROW nCol2
= rParam
.maSortRange
.aEnd
.Col();
311 pArray
.reset(new ScSortInfoArray(0, nCol1
, nCol2
));
312 pArray
->SetKeepQuery(rParam
.mbHiddenFiltered
);
313 pArray
->SetUpdateRefs(rParam
.mbUpdateRefs
);
319 std::unique_ptr
<ScSortInfoArray
> ScTable::CreateSortInfoArray(
320 const ScSortParam
& rSortParam
, SCCOLROW nInd1
, SCCOLROW nInd2
,
321 bool bKeepQuery
, bool bUpdateRefs
)
323 sal_uInt16 nUsedSorts
= 1;
324 while ( nUsedSorts
< rSortParam
.GetSortKeyCount() && rSortParam
.maKeyState
[nUsedSorts
].bDoSort
)
326 std::unique_ptr
<ScSortInfoArray
> pArray(new ScSortInfoArray( nUsedSorts
, nInd1
, nInd2
));
327 pArray
->SetKeepQuery(bKeepQuery
);
328 pArray
->SetUpdateRefs(bUpdateRefs
);
330 if ( rSortParam
.bByRow
)
332 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
334 SCCOL nCol
= static_cast<SCCOL
>(rSortParam
.maKeyState
[nSort
].nField
);
335 ScColumn
* pCol
= &aCol
[nCol
];
336 sc::ColumnBlockConstPosition aBlockPos
;
337 pCol
->InitBlockPosition(aBlockPos
);
338 for ( SCROW nRow
= nInd1
; nRow
<= nInd2
; nRow
++ )
340 ScSortInfo
& rInfo
= pArray
->Get( nSort
, nRow
);
341 rInfo
.maCell
= pCol
->GetCellValue(aBlockPos
, nRow
);
346 CreateColumnIfNotExists(rSortParam
.nCol2
);
347 initDataRows( *pArray
, *this, aCol
, rSortParam
.nCol1
, nInd1
, rSortParam
.nCol2
, nInd2
, bKeepQuery
,
348 rSortParam
.aDataAreaExtras
.mbCellFormats
, true, true, false);
352 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
354 SCROW nRow
= rSortParam
.maKeyState
[nSort
].nField
;
355 for ( SCCOL nCol
= static_cast<SCCOL
>(nInd1
);
356 nCol
<= static_cast<SCCOL
>(nInd2
); nCol
++ )
358 ScSortInfo
& rInfo
= pArray
->Get( nSort
, nCol
);
359 rInfo
.maCell
= GetCellValue(nCol
, nRow
);
371 typedef mdds::flat_segment_tree
<SCROW
, CellAttributeHolder
> PatRangeType
;
373 sc::CellStoreType maCells
;
374 sc::CellTextAttrStoreType maCellTextAttrs
;
375 sc::BroadcasterStoreType maBroadcasters
;
376 sc::CellNoteStoreType maCellNotes
;
377 std::vector
<std::vector
<SdrObject
*>> maCellDrawObjects
;
379 PatRangeType maPatterns
;
380 PatRangeType::const_iterator miPatternPos
;
382 SortedColumn(const SortedColumn
&) = delete;
383 const SortedColumn
operator=(const SortedColumn
&) = delete;
385 explicit SortedColumn( size_t nTopEmptyRows
, const ScSheetLimits
& rSheetLimits
) :
386 maCells(nTopEmptyRows
),
387 maCellTextAttrs(nTopEmptyRows
),
388 maBroadcasters(nTopEmptyRows
),
389 maCellNotes(nTopEmptyRows
),
390 maPatterns(0, rSheetLimits
.GetMaxRowCount(), nullptr),
391 miPatternPos(maPatterns
.begin()) {}
393 void setPattern( SCROW nRow
, const CellAttributeHolder
& rPat
)
395 miPatternPos
= maPatterns
.insert(miPatternPos
, nRow
, nRow
+1, rPat
).first
;
399 struct SortedRowFlags
401 typedef mdds::flat_segment_tree
<SCROW
,bool> FlagsType
;
403 FlagsType maRowsHidden
;
404 FlagsType maRowsFiltered
;
405 FlagsType::const_iterator miPosHidden
;
406 FlagsType::const_iterator miPosFiltered
;
408 SortedRowFlags(const ScSheetLimits
& rSheetLimits
) :
409 maRowsHidden(0, rSheetLimits
.GetMaxRowCount(), false),
410 maRowsFiltered(0, rSheetLimits
.GetMaxRowCount(), false),
411 miPosHidden(maRowsHidden
.begin()),
412 miPosFiltered(maRowsFiltered
.begin()) {}
414 void setRowHidden( SCROW nRow
, bool b
)
416 miPosHidden
= maRowsHidden
.insert(miPosHidden
, nRow
, nRow
+1, b
).first
;
419 void setRowFiltered( SCROW nRow
, bool b
)
421 miPosFiltered
= maRowsFiltered
.insert(miPosFiltered
, nRow
, nRow
+1, b
).first
;
424 void swap( SortedRowFlags
& r
)
426 maRowsHidden
.swap(r
.maRowsHidden
);
427 maRowsFiltered
.swap(r
.maRowsFiltered
);
429 // Just reset the position hints.
430 miPosHidden
= maRowsHidden
.begin();
431 miPosFiltered
= maRowsFiltered
.begin();
439 CellAttributeHolder maPattern
;
441 PatternSpan( SCROW nRow1
, SCROW nRow2
, const CellAttributeHolder
& rPat
) :
442 mnRow1(nRow1
), mnRow2(nRow2
), maPattern(rPat
) {}
447 bool ScTable::IsSortCollatorGlobal() const
449 return pSortCollator
== &ScGlobal::GetCollator() ||
450 pSortCollator
== &ScGlobal::GetCaseCollator();
453 void ScTable::InitSortCollator( const ScSortParam
& rPar
)
455 if ( !rPar
.aCollatorLocale
.Language
.isEmpty() )
457 if ( !pSortCollator
|| IsSortCollatorGlobal() )
458 pSortCollator
= new CollatorWrapper( comphelper::getProcessComponentContext() );
459 pSortCollator
->loadCollatorAlgorithm( rPar
.aCollatorAlgorithm
,
460 rPar
.aCollatorLocale
, (rPar
.bCaseSens
? 0 : SC_COLLATOR_IGNORES
) );
464 DestroySortCollator();
465 pSortCollator
= &ScGlobal::GetCollator(rPar
.bCaseSens
);
469 void ScTable::DestroySortCollator()
473 if ( !IsSortCollatorGlobal() )
474 delete pSortCollator
;
475 pSortCollator
= nullptr;
481 template<typename Hint
, typename ReorderMap
, typename Index
>
482 class ReorderNotifier
486 ReorderNotifier( const ReorderMap
& rMap
, SCTAB nTab
, Index nPos1
, Index nPos2
) :
487 maHint(rMap
, nTab
, nPos1
, nPos2
) {}
489 void operator() ( SvtListener
* p
)
495 class FormulaGroupPosCollector
497 sc::RefQueryFormulaGroup
& mrQuery
;
500 explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup
& rQuery
) : mrQuery(rQuery
) {}
502 void operator() ( const SvtListener
* p
)
508 void fillSortedColumnArray(
509 std::vector
<std::unique_ptr
<SortedColumn
>>& rSortedCols
,
510 SortedRowFlags
& rRowFlags
,
511 std::vector
<SvtListener
*>& rCellListeners
,
512 ScSortInfoArray
* pArray
, SCTAB nTab
, SCCOL nCol1
, SCCOL nCol2
, ScProgress
* pProgress
, const ScTable
* pTable
,
513 bool bOnlyDataAreaExtras
)
515 assert(!bOnlyDataAreaExtras
|| !pArray
->IsUpdateRefs());
517 SCROW nRow1
= pArray
->GetStart();
518 ScSortInfoArray::RowsType
* pRows
= pArray
->GetDataRows();
519 std::vector
<SCCOLROW
> aOrderIndices
= pArray
->GetOrderIndices();
521 size_t nColCount
= nCol2
- nCol1
+ 1;
522 std::vector
<std::unique_ptr
<SortedColumn
>> aSortedCols
; // storage for copied cells.
523 SortedRowFlags
aRowFlags(pTable
->GetDoc().GetSheetLimits());
524 aSortedCols
.reserve(nColCount
);
525 for (size_t i
= 0; i
< nColCount
; ++i
)
527 // In the sorted column container, element positions and row
528 // positions must match, else formula cells may mis-behave during
530 aSortedCols
.push_back(std::make_unique
<SortedColumn
>(nRow1
, pTable
->GetDoc().GetSheetLimits()));
533 for (size_t i
= 0; i
< pRows
->size(); ++i
)
535 const SCROW nRow
= nRow1
+ i
;
537 ScSortInfoArray::Row
& rRow
= (*pRows
)[i
];
538 for (size_t j
= 0; j
< rRow
.maCells
.size(); ++j
)
540 ScSortInfoArray::Cell
& rCell
= rRow
.maCells
[j
];
542 // If bOnlyDataAreaExtras,
543 // sc::CellStoreType aSortedCols.at(j)->maCells
545 // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs
546 // are by definition all empty mdds::multi_type_vector, so nothing
547 // needs to be done to push *all* empty.
549 if (!bOnlyDataAreaExtras
)
551 sc::CellStoreType
& rCellStore
= aSortedCols
.at(j
)->maCells
;
552 switch (rCell
.maCell
.getType())
554 case CELLTYPE_STRING
:
555 assert(rCell
.mpAttr
);
556 rCellStore
.push_back(*rCell
.maCell
.getSharedString());
559 assert(rCell
.mpAttr
);
560 rCellStore
.push_back(rCell
.maCell
.getDouble());
563 assert(rCell
.mpAttr
);
564 rCellStore
.push_back(rCell
.maCell
.getEditText()->Clone().release());
566 case CELLTYPE_FORMULA
:
568 assert(rCell
.mpAttr
);
569 ScAddress aOldPos
= rCell
.maCell
.getFormula()->aPos
;
571 const ScAddress
aCellPos(nCol1
+ j
, nRow
, nTab
);
572 ScFormulaCell
* pNew
= rCell
.maCell
.getFormula()->Clone( aCellPos
);
573 if (pArray
->IsUpdateRefs())
575 pNew
->CopyAllBroadcasters(*rCell
.maCell
.getFormula());
576 pNew
->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos
, aCellPos
);
580 pNew
->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos
, aCellPos
);
583 if (!rCellListeners
.empty())
585 // Original source cells will be deleted during
586 // sc::CellStoreType::transfer(), SvtListener is a base
587 // class, so we need to replace it.
588 auto it( ::std::find( rCellListeners
.begin(), rCellListeners
.end(), rCell
.maCell
.getFormula()));
589 if (it
!= rCellListeners
.end())
593 rCellStore
.push_back(pNew
);
597 //assert(!rCell.mpAttr);
598 // This assert doesn't hold, for example
599 // CopyCellsFromClipHandler may omit copying cells during
600 // PasteSpecial for which CopyTextAttrsFromClipHandler
601 // still copies a CellTextAttr. So if that really is not
602 // expected then fix it there.
603 rCellStore
.push_back_empty();
606 sc::CellTextAttrStoreType
& rAttrStore
= aSortedCols
.at(j
)->maCellTextAttrs
;
608 rAttrStore
.push_back(*rCell
.mpAttr
);
610 rAttrStore
.push_back_empty();
613 if (pArray
->IsUpdateRefs())
615 // At this point each broadcaster instance is managed by 2
616 // containers. We will release those in the original storage
617 // below before transferring them to the document.
618 const SvtBroadcaster
* pBroadcaster
= pTable
->GetBroadcaster( nCol1
+ j
, aOrderIndices
[i
]);
619 sc::BroadcasterStoreType
& rBCStore
= aSortedCols
.at(j
)->maBroadcasters
;
621 // A const pointer would be implicitly converted to a bool type.
622 rBCStore
.push_back(const_cast<SvtBroadcaster
*>(pBroadcaster
));
624 rBCStore
.push_back_empty();
627 // The same with cell note instances ...
628 sc::CellNoteStoreType
& rNoteStore
= aSortedCols
.at(j
)->maCellNotes
;
630 rNoteStore
.push_back(const_cast<ScPostIt
*>(rCell
.mpNote
));
632 rNoteStore
.push_back_empty();
634 // Add cell anchored images
635 aSortedCols
.at(j
)->maCellDrawObjects
.push_back(rCell
.maDrawObjects
);
638 aSortedCols
.at(j
)->setPattern(nRow
, rCell
.maPattern
);
641 if (!bOnlyDataAreaExtras
&& pArray
->IsKeepQuery())
643 // Hidden and filtered flags are first converted to segments.
644 aRowFlags
.setRowHidden(nRow
, rRow
.mbHidden
);
645 aRowFlags
.setRowFiltered(nRow
, rRow
.mbFiltered
);
649 pProgress
->SetStateOnPercent(i
);
652 rSortedCols
.swap(aSortedCols
);
653 rRowFlags
.swap(aRowFlags
);
656 void expandRowRange( ScRange
& rRange
, SCROW nTop
, SCROW nBottom
)
658 if (nTop
< rRange
.aStart
.Row())
659 rRange
.aStart
.SetRow(nTop
);
661 if (rRange
.aEnd
.Row() < nBottom
)
662 rRange
.aEnd
.SetRow(nBottom
);
665 class FormulaCellCollectAction
: public sc::ColumnSpanSet::ColumnAction
667 std::vector
<ScFormulaCell
*>& mrCells
;
671 explicit FormulaCellCollectAction( std::vector
<ScFormulaCell
*>& rCells
) :
672 mrCells(rCells
), mpCol(nullptr) {}
674 virtual void startColumn( ScColumn
* pCol
) override
679 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) override
686 mpCol
->CollectFormulaCells(mrCells
, nRow1
, nRow2
);
690 class ListenerStartAction
: public sc::ColumnSpanSet::ColumnAction
694 std::shared_ptr
<sc::ColumnBlockPositionSet
> mpPosSet
;
695 sc::StartListeningContext maStartCxt
;
696 sc::EndListeningContext maEndCxt
;
699 explicit ListenerStartAction( ScDocument
& rDoc
) :
701 mpPosSet(std::make_shared
<sc::ColumnBlockPositionSet
>(rDoc
)),
702 maStartCxt(rDoc
, mpPosSet
),
703 maEndCxt(rDoc
, mpPosSet
) {}
705 virtual void startColumn( ScColumn
* pCol
) override
710 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) override
717 mpCol
->StartListeningFormulaCells(maStartCxt
, maEndCxt
, nRow1
, nRow2
);
723 void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray
* pArray
,
724 SCCOL nDataCol1
, SCCOL nDataCol2
,
725 const ScDataAreaExtras
& rDataAreaExtras
, ScProgress
* pProgress
)
727 const SCROW nRow1
= pArray
->GetStart();
728 const SCROW nLastRow
= pArray
->GetLast();
729 const SCCOL nChunkCols
= std::max
<SCCOL
>( 1, kSortCellsChunk
/ (nLastRow
- nRow1
+ 1));
731 for (SCCOL nCol
= rDataAreaExtras
.mnStartCol
; nCol
< nDataCol1
; nCol
+= nChunkCols
)
733 const SCCOL nEndCol
= std::min
<SCCOL
>( nCol
+ nChunkCols
- 1, nDataCol1
- 1);
734 CreateColumnIfNotExists(nEndCol
);
735 initDataRows( *pArray
, *this, aCol
, nCol
, nRow1
, nEndCol
, nLastRow
, false,
736 rDataAreaExtras
.mbCellFormats
, rDataAreaExtras
.mbCellNotes
, rDataAreaExtras
.mbCellDrawObjects
, true);
737 SortReorderByRow( pArray
, nCol
, nEndCol
, pProgress
, true);
740 for (SCCOL nCol
= nDataCol2
+ 1; nCol
<= rDataAreaExtras
.mnEndCol
; nCol
+= nChunkCols
)
742 const SCCOL nEndCol
= std::min
<SCCOL
>( nCol
+ nChunkCols
- 1, rDataAreaExtras
.mnEndCol
);
743 CreateColumnIfNotExists(nEndCol
);
744 initDataRows( *pArray
, *this, aCol
, nCol
, nRow1
, nEndCol
, nLastRow
, false,
745 rDataAreaExtras
.mbCellFormats
, rDataAreaExtras
.mbCellNotes
, rDataAreaExtras
.mbCellDrawObjects
, true);
746 SortReorderByRow( pArray
, nCol
, nEndCol
, pProgress
, true);
750 void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray
* pArray
,
751 SCROW nDataRow1
, SCROW nDataRow2
, const ScDataAreaExtras
& rDataAreaExtras
, ScProgress
* pProgress
)
753 const SCCOL nCol1
= static_cast<SCCOL
>(pArray
->GetStart());
754 const SCCOL nLastCol
= static_cast<SCCOL
>(pArray
->GetLast());
755 const SCROW nChunkRows
= std::max
<SCROW
>( 1, kSortCellsChunk
/ (nLastCol
- nCol1
+ 1));
757 for (SCROW nRow
= rDataAreaExtras
.mnStartRow
; nRow
< nDataRow1
; nRow
+= nChunkRows
)
759 const SCROW nEndRow
= std::min
<SCROW
>( nRow
+ nChunkRows
- 1, nDataRow1
- 1);
760 SortReorderByColumn( pArray
, nRow
, nEndRow
, rDataAreaExtras
.mbCellFormats
, pProgress
);
763 for (SCROW nRow
= nDataRow2
+ 1; nRow
<= rDataAreaExtras
.mnEndRow
; nRow
+= nChunkRows
)
765 const SCROW nEndRow
= std::min
<SCROW
>( nRow
+ nChunkRows
- 1, rDataAreaExtras
.mnEndRow
);
766 SortReorderByColumn( pArray
, nRow
, nEndRow
, rDataAreaExtras
.mbCellFormats
, pProgress
);
770 void ScTable::SortReorderByColumn(
771 const ScSortInfoArray
* pArray
, SCROW nRow1
, SCROW nRow2
, bool bPattern
, ScProgress
* pProgress
)
773 SCCOLROW nStart
= pArray
->GetStart();
774 SCCOLROW nLast
= pArray
->GetLast();
776 std::vector
<SCCOLROW
> aIndices
= pArray
->GetOrderIndices();
777 size_t nCount
= aIndices
.size();
779 // Cut formula grouping at row and reference boundaries before the reordering.
780 ScRange
aSortRange(nStart
, nRow1
, nTab
, nLast
, nRow2
, nTab
);
781 for (SCCOL nCol
= nStart
; nCol
<= static_cast<SCCOL
>(nLast
); ++nCol
)
782 aCol
[nCol
].SplitFormulaGroupByRelativeRef(aSortRange
);
784 // Collect all listeners of cell broadcasters of sorted range.
785 std::vector
<SvtListener
*> aCellListeners
;
787 if (!pArray
->IsUpdateRefs())
789 // Collect listeners of cell broadcasters.
790 for (SCCOL nCol
= nStart
; nCol
<= static_cast<SCCOL
>(nLast
); ++nCol
)
791 aCol
[nCol
].CollectListeners(aCellListeners
, nRow1
, nRow2
);
793 // Remove any duplicate listener entries. We must ensure that we
794 // notify each unique listener only once.
795 std::sort(aCellListeners
.begin(), aCellListeners
.end());
796 aCellListeners
.erase(std::unique(aCellListeners
.begin(), aCellListeners
.end()), aCellListeners
.end());
798 // Notify the cells' listeners to stop listening.
799 /* TODO: for performance this could be enhanced to stop and later
800 * restart only listening to within the reordered range and keep
801 * listening to everything outside untouched. */
802 sc::RefStopListeningHint aHint
;
803 for (auto const & l
: aCellListeners
)
807 // table to keep track of column index to position in the index table.
808 std::vector
<SCCOLROW
> aPosTable(nCount
);
809 for (size_t i
= 0; i
< nCount
; ++i
)
810 aPosTable
[aIndices
[i
]-nStart
] = i
;
812 SCCOLROW nDest
= nStart
;
813 for (size_t i
= 0; i
< nCount
; ++i
, ++nDest
)
815 SCCOLROW nSrc
= aIndices
[i
];
818 aCol
[nDest
].Swap(aCol
[nSrc
], nRow1
, nRow2
, bPattern
);
820 // Update the position of the index that was originally equal to nDest.
821 size_t nPos
= aPosTable
[nDest
-nStart
];
822 aIndices
[nPos
] = nSrc
;
823 aPosTable
[nSrc
-nStart
] = nPos
;
827 pProgress
->SetStateOnPercent(i
);
830 // Reset formula cell positions which became out-of-sync after column reordering.
831 bool bUpdateRefs
= pArray
->IsUpdateRefs();
832 for (SCCOL nCol
= nStart
; nCol
<= static_cast<SCCOL
>(nLast
); ++nCol
)
833 aCol
[nCol
].ResetFormulaCellPositions(nRow1
, nRow2
, bUpdateRefs
);
835 if (pArray
->IsUpdateRefs())
837 // Set up column reorder map (for later broadcasting of reference updates).
838 sc::ColRowReorderMapType aColMap
;
839 const std::vector
<SCCOLROW
>& rOldIndices
= pArray
->GetOrderIndices();
840 for (size_t i
= 0, n
= rOldIndices
.size(); i
< n
; ++i
)
842 SCCOL nNew
= i
+ nStart
;
843 SCCOL nOld
= rOldIndices
[i
];
844 aColMap
.emplace(nOld
, nNew
);
847 // Collect all listeners within sorted range ahead of time.
848 std::vector
<SvtListener
*> aListeners
;
850 for (SCCOL nCol
= nStart
; nCol
<= static_cast<SCCOL
>(nLast
); ++nCol
)
851 aCol
[nCol
].CollectListeners(aListeners
, nRow1
, nRow2
);
853 // Get all area listeners that listen on one column within the range
854 // and end their listening.
855 ScRange
aMoveRange( nStart
, nRow1
, nTab
, nLast
, nRow2
, nTab
);
856 std::vector
<sc::AreaListener
> aAreaListeners
= rDocument
.GetBASM()->GetAllListeners(
857 aMoveRange
, sc::AreaOverlapType::OneColumnInside
);
859 for (auto& rAreaListener
: aAreaListeners
)
861 rDocument
.EndListeningArea(rAreaListener
.maArea
, rAreaListener
.mbGroupListening
, rAreaListener
.mpListener
);
862 aListeners
.push_back( rAreaListener
.mpListener
);
866 // Remove any duplicate listener entries and notify all listeners
867 // afterward. We must ensure that we notify each unique listener only
869 std::sort(aListeners
.begin(), aListeners
.end());
870 aListeners
.erase(std::unique(aListeners
.begin(), aListeners
.end()), aListeners
.end());
872 ReorderNotifier
<sc::RefColReorderHint
, sc::ColRowReorderMapType
, SCCOL
> aFunc(aColMap
, nTab
, nRow1
, nRow2
);
873 std::for_each(aListeners
.begin(), aListeners
.end(), std::move(aFunc
));
876 // Re-start area listeners on the reordered columns.
878 for (auto& rAreaListener
: aAreaListeners
)
880 ScRange aNewRange
= rAreaListener
.maArea
;
881 sc::ColRowReorderMapType::const_iterator itCol
= aColMap
.find( aNewRange
.aStart
.Col());
882 if (itCol
!= aColMap
.end())
884 aNewRange
.aStart
.SetCol( itCol
->second
);
885 aNewRange
.aEnd
.SetCol( itCol
->second
);
887 rDocument
.StartListeningArea(aNewRange
, rAreaListener
.mbGroupListening
, rAreaListener
.mpListener
);
891 else // !(pArray->IsUpdateRefs())
893 // Notify the cells' listeners to (re-)start listening.
894 sc::RefStartListeningHint aHint
;
895 for (auto const & l
: aCellListeners
)
899 // Re-join formulas at row boundaries now that all the references have
900 // been adjusted for column reordering.
901 for (SCCOL nCol
= nStart
; nCol
<= static_cast<SCCOL
>(nLast
); ++nCol
)
903 sc::CellStoreType
& rCells
= aCol
[nCol
].maCells
;
904 sc::CellStoreType::position_type aPos
= rCells
.position(nRow1
);
905 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos
);
906 if (nRow2
< rDocument
.MaxRow())
908 aPos
= rCells
.position(aPos
.first
, nRow2
+1);
909 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos
);
914 static void backupObjectsVisibility(const std::vector
<std::unique_ptr
<SortedColumn
>>& rSortedCols
,
915 std::vector
<std::vector
<std::vector
<bool>>>& rBackup
)
917 size_t nSortedCols
= rSortedCols
.size();
918 for (size_t iCol
= 0; iCol
< nSortedCols
; ++iCol
)
920 std::vector
<std::vector
<SdrObject
*>>& rSingleColCellDrawObjects
921 = rSortedCols
[iCol
]->maCellDrawObjects
;
922 size_t nSingleColCellDrawObjects
= rSingleColCellDrawObjects
.size();
923 std::vector
<std::vector
<bool>> aColBackup
;
924 for (size_t jRow
= 0; jRow
< nSingleColCellDrawObjects
; ++jRow
)
926 std::vector
<SdrObject
*>& rCellDrawObjects
= rSingleColCellDrawObjects
[jRow
];
927 std::vector
<bool> aCellBackup
;
928 for (auto& pObject
: rCellDrawObjects
)
930 aCellBackup
.push_back(pObject
->IsVisible());
932 aColBackup
.push_back(std::move(aCellBackup
));
934 rBackup
.push_back(std::move(aColBackup
));
938 static void restoreObjectsVisibility(std::vector
<std::unique_ptr
<SortedColumn
>>& rSortedCols
,
939 const std::vector
<std::vector
<std::vector
<bool>>>& rBackup
)
941 size_t nSortedCols
= rSortedCols
.size();
942 for (size_t iCol
= 0; iCol
< nSortedCols
; ++iCol
)
944 std::vector
<std::vector
<SdrObject
*>>& rSingleColCellDrawObjects
945 = rSortedCols
[iCol
]->maCellDrawObjects
;
946 size_t nSingleColCellDrawObjects
= rSingleColCellDrawObjects
.size();
947 for (size_t jRow
= 0; jRow
< nSingleColCellDrawObjects
; jRow
++)
949 std::vector
<SdrObject
*>& rCellDrawObjects
= rSingleColCellDrawObjects
[jRow
];
950 for (size_t kCell
= 0; kCell
< rCellDrawObjects
.size(); ++kCell
)
952 rCellDrawObjects
[kCell
]->SetVisible(rBackup
[iCol
][jRow
][kCell
]);
958 void ScTable::SortReorderByRow( ScSortInfoArray
* pArray
, SCCOL nCol1
, SCCOL nCol2
,
959 ScProgress
* pProgress
, bool bOnlyDataAreaExtras
)
961 assert(!pArray
->IsUpdateRefs());
966 // bOnlyDataAreaExtras:
967 // Data area extras by definition do not have any cell content so no
968 // formula cells either, so that handling doesn't need to be executed.
969 // However, there may be listeners of formulas listening to broadcasters of
972 SCROW nRow1
= pArray
->GetStart();
973 SCROW nRow2
= pArray
->GetLast();
975 // Collect all listeners of cell broadcasters of sorted range.
976 std::vector
<SvtListener
*> aCellListeners
;
978 // When the update ref mode is disabled, we need to detach all formula
979 // cells in the sorted range before reordering, and re-start them
981 if (!bOnlyDataAreaExtras
)
983 sc::EndListeningContext
aCxt(rDocument
);
984 DetachFormulaCells(aCxt
, nCol1
, nRow1
, nCol2
, nRow2
);
987 // Collect listeners of cell broadcasters.
988 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
989 aCol
[nCol
].CollectListeners(aCellListeners
, nRow1
, nRow2
);
991 // Remove any duplicate listener entries. We must ensure that we notify
992 // each unique listener only once.
993 std::sort(aCellListeners
.begin(), aCellListeners
.end());
994 aCellListeners
.erase(std::unique(aCellListeners
.begin(), aCellListeners
.end()), aCellListeners
.end());
996 // Notify the cells' listeners to stop listening.
997 /* TODO: for performance this could be enhanced to stop and later
998 * restart only listening to within the reordered range and keep
999 * listening to everything outside untouched. */
1001 sc::RefStopListeningHint aHint
;
1002 for (auto const & l
: aCellListeners
)
1006 // Split formula groups at the sort range boundaries (if applicable).
1007 if (!bOnlyDataAreaExtras
)
1009 std::vector
<SCROW
> aRowBounds
1014 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1015 SplitFormulaGroups(nCol
, aRowBounds
);
1018 // Cells in the data rows only reference values in the document. Make
1019 // a copy before updating the document.
1020 std::vector
<std::unique_ptr
<SortedColumn
>> aSortedCols
; // storage for copied cells.
1021 SortedRowFlags
aRowFlags(GetDoc().GetSheetLimits());
1022 fillSortedColumnArray(aSortedCols
, aRowFlags
, aCellListeners
, pArray
, nTab
, nCol1
, nCol2
,
1023 pProgress
, this, bOnlyDataAreaExtras
);
1025 for (size_t i
= 0, n
= aSortedCols
.size(); i
< n
; ++i
)
1027 SCCOL nThisCol
= i
+ nCol1
;
1029 if (!bOnlyDataAreaExtras
)
1032 sc::CellStoreType
& rDest
= aCol
[nThisCol
].maCells
;
1033 sc::CellStoreType
& rSrc
= aSortedCols
[i
]->maCells
;
1034 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1038 sc::CellTextAttrStoreType
& rDest
= aCol
[nThisCol
].maCellTextAttrs
;
1039 sc::CellTextAttrStoreType
& rSrc
= aSortedCols
[i
]->maCellTextAttrs
;
1040 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1045 sc::CellNoteStoreType
& rSrc
= aSortedCols
[i
]->maCellNotes
;
1046 sc::CellNoteStoreType
& rDest
= aCol
[nThisCol
].maCellNotes
;
1048 // Do the same as broadcaster storage transfer (to prevent double deletion).
1049 rDest
.release_range(nRow1
, nRow2
);
1050 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1051 aCol
[nThisCol
].UpdateNoteCaptions(nRow1
, nRow2
);
1055 // Get all row spans where the pattern is not NULL.
1056 std::vector
<PatternSpan
> aSpans
=
1057 sc::toSpanArrayWithValue
<SCROW
, CellAttributeHolder
, PatternSpan
>(
1058 aSortedCols
[i
]->maPatterns
);
1060 for (const auto& rSpan
: aSpans
)
1062 aCol
[nThisCol
].SetPatternArea(rSpan
.mnRow1
, rSpan
.mnRow2
, rSpan
.maPattern
);
1066 aCol
[nThisCol
].CellStorageModified();
1069 if (!bOnlyDataAreaExtras
&& pArray
->IsKeepQuery())
1071 aRowFlags
.maRowsHidden
.build_tree();
1072 aRowFlags
.maRowsFiltered
.build_tree();
1074 // Backup visibility state of objects. States will be lost when changing the flags below.
1075 std::vector
<std::vector
<std::vector
<bool>>> aBackup
;
1076 backupObjectsVisibility(aSortedCols
, aBackup
);
1078 // Remove all flags in the range first.
1079 SetRowHidden(nRow1
, nRow2
, false);
1080 SetRowFiltered(nRow1
, nRow2
, false);
1082 std::vector
<sc::RowSpan
> aSpans
=
1083 sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsHidden
, nRow1
);
1085 for (const auto& rSpan
: aSpans
)
1086 SetRowHidden(rSpan
.mnRow1
, rSpan
.mnRow2
, true);
1088 aSpans
= sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsFiltered
, nRow1
);
1090 for (const auto& rSpan
: aSpans
)
1091 SetRowFiltered(rSpan
.mnRow1
, rSpan
.mnRow2
, true);
1093 //Restore visibility state of objects
1094 restoreObjectsVisibility(aSortedCols
, aBackup
);
1097 // Update draw object positions
1098 for (size_t i
= 0, n
= aSortedCols
.size(); i
< n
; ++i
)
1100 SCCOL nThisCol
= i
+ nCol1
;
1101 aCol
[nThisCol
].UpdateDrawObjects(aSortedCols
[i
]->maCellDrawObjects
, nRow1
, nRow2
);
1104 // Notify the cells' listeners to (re-)start listening.
1106 sc::RefStartListeningHint aHint
;
1107 for (auto const & l
: aCellListeners
)
1111 if (!bOnlyDataAreaExtras
)
1113 // Re-group columns in the sorted range too.
1114 for (SCCOL i
= nCol1
; i
<= nCol2
; ++i
)
1115 aCol
[i
].RegroupFormulaCells();
1118 sc::StartListeningContext
aCxt(rDocument
);
1119 AttachFormulaCells(aCxt
, nCol1
, nRow1
, nCol2
, nRow2
);
1124 void ScTable::SortReorderByRowRefUpdate(
1125 ScSortInfoArray
* pArray
, SCCOL nCol1
, SCCOL nCol2
, ScProgress
* pProgress
)
1127 assert(pArray
->IsUpdateRefs());
1132 SCROW nRow1
= pArray
->GetStart();
1133 SCROW nRow2
= pArray
->GetLast();
1135 ScRange
aMoveRange( nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
);
1136 sc::ColumnSpanSet aGrpListenerRanges
;
1139 // Get the range of formula group listeners within sorted range (if any).
1140 sc::QueryRange aQuery
;
1142 ScBroadcastAreaSlotMachine
* pBASM
= rDocument
.GetBASM();
1143 std::vector
<sc::AreaListener
> aGrpListeners
=
1144 pBASM
->GetAllListeners(
1145 aMoveRange
, sc::AreaOverlapType::InsideOrOverlap
, sc::ListenerGroupType::Group
);
1148 for (const auto& rGrpListener
: aGrpListeners
)
1150 assert(rGrpListener
.mbGroupListening
);
1151 SvtListener
* pGrpLis
= rGrpListener
.mpListener
;
1152 pGrpLis
->Query(aQuery
);
1153 rDocument
.EndListeningArea(rGrpListener
.maArea
, rGrpListener
.mbGroupListening
, pGrpLis
);
1158 aQuery
.swapRanges(aTmp
);
1160 // If the range is within the sorted range, we need to expand its rows
1161 // to the top and bottom of the sorted range, since the formula cells
1162 // could be anywhere in the sorted range after reordering.
1163 for (size_t i
= 0, n
= aTmp
.size(); i
< n
; ++i
)
1165 ScRange aRange
= aTmp
[i
];
1166 if (!aMoveRange
.Intersects(aRange
))
1168 // Doesn't overlap with the sorted range at all.
1169 aGrpListenerRanges
.set(GetDoc(), aRange
, true);
1173 if (aMoveRange
.aStart
.Col() <= aRange
.aStart
.Col() && aRange
.aEnd
.Col() <= aMoveRange
.aEnd
.Col())
1175 // Its column range is within the column range of the sorted range.
1176 expandRowRange(aRange
, aMoveRange
.aStart
.Row(), aMoveRange
.aEnd
.Row());
1177 aGrpListenerRanges
.set(GetDoc(), aRange
, true);
1181 // It intersects with the sorted range, but its column range is
1182 // not within the column range of the sorted range. Split it into
1184 ScRange aR1
= aRange
;
1185 ScRange aR2
= aRange
;
1186 if (aRange
.aStart
.Col() < aMoveRange
.aStart
.Col())
1188 // Left half is outside the sorted range while the right half is inside.
1189 aR1
.aEnd
.SetCol(aMoveRange
.aStart
.Col()-1);
1190 aR2
.aStart
.SetCol(aMoveRange
.aStart
.Col());
1191 expandRowRange(aR2
, aMoveRange
.aStart
.Row(), aMoveRange
.aEnd
.Row());
1195 // Left half is inside the sorted range while the right half is outside.
1196 aR1
.aEnd
.SetCol(aMoveRange
.aEnd
.Col()-1);
1197 aR2
.aStart
.SetCol(aMoveRange
.aEnd
.Col());
1198 expandRowRange(aR1
, aMoveRange
.aStart
.Row(), aMoveRange
.aEnd
.Row());
1201 aGrpListenerRanges
.set(GetDoc(), aR1
, true);
1202 aGrpListenerRanges
.set(GetDoc(), aR2
, true);
1206 // Split formula groups at the sort range boundaries (if applicable).
1207 std::vector
<SCROW
> aRowBounds
1212 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1213 SplitFormulaGroups(nCol
, aRowBounds
);
1215 // Cells in the data rows only reference values in the document. Make
1216 // a copy before updating the document.
1217 std::vector
<std::unique_ptr
<SortedColumn
>> aSortedCols
; // storage for copied cells.
1218 SortedRowFlags
aRowFlags(GetDoc().GetSheetLimits());
1219 std::vector
<SvtListener
*> aListenersDummy
;
1220 fillSortedColumnArray(aSortedCols
, aRowFlags
, aListenersDummy
, pArray
, nTab
, nCol1
, nCol2
, pProgress
, this, false);
1222 for (size_t i
= 0, n
= aSortedCols
.size(); i
< n
; ++i
)
1224 SCCOL nThisCol
= i
+ nCol1
;
1227 sc::CellStoreType
& rDest
= aCol
[nThisCol
].maCells
;
1228 sc::CellStoreType
& rSrc
= aSortedCols
[i
]->maCells
;
1229 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1233 sc::CellTextAttrStoreType
& rDest
= aCol
[nThisCol
].maCellTextAttrs
;
1234 sc::CellTextAttrStoreType
& rSrc
= aSortedCols
[i
]->maCellTextAttrs
;
1235 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1239 sc::BroadcasterStoreType
& rSrc
= aSortedCols
[i
]->maBroadcasters
;
1240 sc::BroadcasterStoreType
& rDest
= aCol
[nThisCol
].maBroadcasters
;
1242 // Release current broadcasters first, to prevent them from getting deleted.
1243 rDest
.release_range(nRow1
, nRow2
);
1245 // Transfer sorted broadcaster segment to the document.
1246 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1250 sc::CellNoteStoreType
& rSrc
= aSortedCols
[i
]->maCellNotes
;
1251 sc::CellNoteStoreType
& rDest
= aCol
[nThisCol
].maCellNotes
;
1253 // Do the same as broadcaster storage transfer (to prevent double deletion).
1254 rDest
.release_range(nRow1
, nRow2
);
1255 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1256 aCol
[nThisCol
].UpdateNoteCaptions(nRow1
, nRow2
);
1260 // Get all row spans where the pattern is not NULL.
1261 std::vector
<PatternSpan
> aSpans
=
1262 sc::toSpanArrayWithValue
<SCROW
, CellAttributeHolder
, PatternSpan
>(
1263 aSortedCols
[i
]->maPatterns
);
1265 for (const auto& rSpan
: aSpans
)
1267 aCol
[nThisCol
].SetPatternArea(rSpan
.mnRow1
, rSpan
.mnRow2
, rSpan
.maPattern
);
1271 aCol
[nThisCol
].CellStorageModified();
1274 if (pArray
->IsKeepQuery())
1276 aRowFlags
.maRowsHidden
.build_tree();
1277 aRowFlags
.maRowsFiltered
.build_tree();
1279 // Backup visibility state of objects. States will be lost when changing the flags below.
1280 std::vector
<std::vector
<std::vector
<bool>>> aBackup
;
1281 backupObjectsVisibility(aSortedCols
, aBackup
);
1283 // Remove all flags in the range first.
1284 SetRowHidden(nRow1
, nRow2
, false);
1285 SetRowFiltered(nRow1
, nRow2
, false);
1287 std::vector
<sc::RowSpan
> aSpans
=
1288 sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsHidden
, nRow1
);
1290 for (const auto& rSpan
: aSpans
)
1291 SetRowHidden(rSpan
.mnRow1
, rSpan
.mnRow2
, true);
1293 aSpans
= sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsFiltered
, nRow1
);
1295 for (const auto& rSpan
: aSpans
)
1296 SetRowFiltered(rSpan
.mnRow1
, rSpan
.mnRow2
, true);
1298 //Restore visibility state of objects
1299 restoreObjectsVisibility(aSortedCols
, aBackup
);
1302 // Update draw object positions
1303 for (size_t i
= 0, n
= aSortedCols
.size(); i
< n
; ++i
)
1305 SCCOL nThisCol
= i
+ nCol1
;
1306 aCol
[nThisCol
].UpdateDrawObjects(aSortedCols
[i
]->maCellDrawObjects
, nRow1
, nRow2
);
1309 // Set up row reorder map (for later broadcasting of reference updates).
1310 sc::ColRowReorderMapType aRowMap
;
1311 const std::vector
<SCCOLROW
>& rOldIndices
= pArray
->GetOrderIndices();
1312 for (size_t i
= 0, n
= rOldIndices
.size(); i
< n
; ++i
)
1314 SCROW nNew
= i
+ nRow1
;
1315 SCROW nOld
= rOldIndices
[i
];
1316 aRowMap
.emplace(nOld
, nNew
);
1319 // Collect all listeners within sorted range ahead of time.
1320 std::vector
<SvtListener
*> aListeners
;
1322 // Collect listeners of cell broadcasters.
1323 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1324 aCol
[nCol
].CollectListeners(aListeners
, nRow1
, nRow2
);
1326 // Get all area listeners that listen on one row within the range and end
1328 std::vector
<sc::AreaListener
> aAreaListeners
= rDocument
.GetBASM()->GetAllListeners(
1329 aMoveRange
, sc::AreaOverlapType::OneRowInside
);
1331 for (auto& rAreaListener
: aAreaListeners
)
1333 rDocument
.EndListeningArea(rAreaListener
.maArea
, rAreaListener
.mbGroupListening
, rAreaListener
.mpListener
);
1334 aListeners
.push_back( rAreaListener
.mpListener
);
1339 // Get all formula cells from the former group area listener ranges.
1341 std::vector
<ScFormulaCell
*> aFCells
;
1342 FormulaCellCollectAction
aAction(aFCells
);
1343 aGrpListenerRanges
.executeColumnAction(rDocument
, aAction
);
1345 aListeners
.insert( aListeners
.end(), aFCells
.begin(), aFCells
.end() );
1348 // Remove any duplicate listener entries. We must ensure that we notify
1349 // each unique listener only once.
1350 std::sort(aListeners
.begin(), aListeners
.end());
1351 aListeners
.erase(std::unique(aListeners
.begin(), aListeners
.end()), aListeners
.end());
1353 // Collect positions of all shared formula cells outside the sorted range,
1354 // and make them unshared before notifying them.
1355 sc::RefQueryFormulaGroup aFormulaGroupPos
;
1356 aFormulaGroupPos
.setSkipRange(ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
));
1358 std::for_each(aListeners
.begin(), aListeners
.end(), FormulaGroupPosCollector(aFormulaGroupPos
));
1359 const sc::RefQueryFormulaGroup::TabsType
& rGroupTabs
= aFormulaGroupPos
.getAllPositions();
1360 for (const auto& [rTab
, rCols
] : rGroupTabs
)
1362 for (const auto& [nCol
, rCol
] : rCols
)
1364 std::vector
<SCROW
> aBounds(rCol
);
1365 rDocument
.UnshareFormulaCells(rTab
, nCol
, aBounds
);
1369 // Notify the listeners to update their references.
1371 ReorderNotifier
<sc::RefRowReorderHint
, sc::ColRowReorderMapType
, SCROW
> aFunc(aRowMap
, nTab
, nCol1
, nCol2
);
1372 std::for_each(aListeners
.begin(), aListeners
.end(), std::move(aFunc
));
1375 // Re-group formulas in affected columns.
1376 for (const auto& [rTab
, rCols
] : rGroupTabs
)
1378 for (const auto& rEntry
: rCols
)
1379 rDocument
.RegroupFormulaCells(rTab
, rEntry
.first
);
1382 // Re-start area listeners on the reordered rows.
1383 for (const auto& rAreaListener
: aAreaListeners
)
1385 ScRange aNewRange
= rAreaListener
.maArea
;
1386 sc::ColRowReorderMapType::const_iterator itRow
= aRowMap
.find( aNewRange
.aStart
.Row());
1387 if (itRow
!= aRowMap
.end())
1389 aNewRange
.aStart
.SetRow( itRow
->second
);
1390 aNewRange
.aEnd
.SetRow( itRow
->second
);
1392 rDocument
.StartListeningArea(aNewRange
, rAreaListener
.mbGroupListening
, rAreaListener
.mpListener
);
1395 // Re-group columns in the sorted range too.
1396 for (SCCOL i
= nCol1
; i
<= nCol2
; ++i
)
1397 aCol
[i
].RegroupFormulaCells();
1400 // Re-start area listeners on the old group listener ranges.
1401 ListenerStartAction
aAction(rDocument
);
1402 aGrpListenerRanges
.executeColumnAction(rDocument
, aAction
);
1406 short ScTable::CompareCell(
1408 ScRefCellValue
& rCell1
, SCCOL nCell1Col
, SCROW nCell1Row
,
1409 ScRefCellValue
& rCell2
, SCCOL nCell2Col
, SCROW nCell2Row
) const
1413 CellType eType1
= rCell1
.getType(), eType2
= rCell2
.getType();
1415 // tdf#95520 Sort by color - selected color goes on top, everything else according to compare function
1416 if (aSortParam
.maKeyState
[nSort
].aColorSortMode
== ScColorSortMode::TextColor
1417 || aSortParam
.maKeyState
[nSort
].aColorSortMode
== ScColorSortMode::BackgroundColor
)
1419 ScAddress
aPos1(nCell1Col
, nCell1Row
, GetTab());
1420 ScAddress
aPos2(nCell2Col
, nCell2Row
, GetTab());
1421 Color aTheChosenColor
= aSortParam
.maKeyState
[nSort
].aColorSortColor
;
1424 if (aSortParam
.maKeyState
[nSort
].aColorSortMode
== ScColorSortMode::TextColor
)
1426 aColor1
= GetCellTextColor(aPos1
);
1427 aColor2
= GetCellTextColor(aPos2
);
1431 aColor1
= GetCellBackgroundColor(aPos1
);
1432 aColor2
= GetCellBackgroundColor(aPos2
);
1434 if (aTheChosenColor
== aColor1
)
1436 if (aTheChosenColor
== aColor2
)
1438 if (aColor1
== aColor2
)
1440 if (aColor1
> aColor2
)
1442 if (aColor1
< aColor2
)
1446 if (!rCell1
.isEmpty())
1448 if (!rCell2
.isEmpty())
1451 bool bStr1
= ( eType1
!= CELLTYPE_VALUE
);
1452 if (eType1
== CELLTYPE_FORMULA
)
1454 if (rCell1
.getFormula()->GetErrCode() != FormulaError::NONE
)
1459 else if (rCell1
.getFormula()->IsValue())
1466 bool bStr2
= ( eType2
!= CELLTYPE_VALUE
);
1467 if (eType2
== CELLTYPE_FORMULA
)
1469 if (rCell2
.getFormula()->GetErrCode() != FormulaError::NONE
)
1474 else if (rCell2
.getFormula()->IsValue())
1480 if ( bStr1
&& bStr2
) // only compare strings as strings!
1484 if (eType1
== CELLTYPE_STRING
)
1485 aStr1
= rCell1
.getSharedString()->getString();
1487 aStr1
= GetString(nCell1Col
, nCell1Row
);
1488 if (eType2
== CELLTYPE_STRING
)
1489 aStr2
= rCell2
.getSharedString()->getString();
1491 aStr2
= GetString(nCell2Col
, nCell2Row
);
1493 bool bUserDef
= aSortParam
.bUserDef
; // custom sort order
1494 bool bNaturalSort
= aSortParam
.bNaturalSort
; // natural sort
1495 bool bCaseSens
= aSortParam
.bCaseSens
; // case sensitivity
1497 ScUserList
& rList
= ScGlobal::GetUserList();
1498 if (bUserDef
&& rList
.size() > aSortParam
.nUserIndex
)
1500 const ScUserListData
& rData
= rList
[aSortParam
.nUserIndex
];
1503 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, &rData
, pSortCollator
);
1507 nRes
= sal::static_int_cast
<short>( rData
.Compare(aStr1
, aStr2
) );
1509 nRes
= sal::static_int_cast
<short>( rData
.ICompare(aStr1
, aStr2
) );
1516 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, nullptr, pSortCollator
);
1518 nRes
= static_cast<short>( pSortCollator
->compareString( aStr1
, aStr2
) );
1521 else if ( bStr1
) // String <-> Number or Error
1524 nRes
= -1; // String in front of Error
1526 nRes
= 1; // Number in front of String
1528 else if ( bStr2
) // Number or Error <-> String
1531 nRes
= 1; // String in front of Error
1533 nRes
= -1; // Number in front of String
1535 else if (bErr1
&& bErr2
)
1537 // nothing, two Errors are equal
1539 else if (bErr1
) // Error <-> Number
1541 nRes
= 1; // Number in front of Error
1543 else if (bErr2
) // Number <-> Error
1545 nRes
= -1; // Number in front of Error
1547 else // Mixed numbers
1549 double nVal1
= rCell1
.getValue();
1550 double nVal2
= rCell2
.getValue();
1553 else if (nVal1
> nVal2
)
1556 if ( !aSortParam
.maKeyState
[nSort
].bAscending
)
1564 if (!rCell2
.isEmpty())
1567 nRes
= 0; // both empty
1572 short ScTable::Compare( ScSortInfoArray
* pArray
, SCCOLROW nIndex1
, SCCOLROW nIndex2
) const
1575 sal_uInt16 nSort
= 0;
1578 ScSortInfo
& rInfo1
= pArray
->Get( nSort
, nIndex1
);
1579 ScSortInfo
& rInfo2
= pArray
->Get( nSort
, nIndex2
);
1580 if ( aSortParam
.bByRow
)
1581 nRes
= CompareCell( nSort
,
1582 rInfo1
.maCell
, static_cast<SCCOL
>(aSortParam
.maKeyState
[nSort
].nField
), rInfo1
.nOrg
,
1583 rInfo2
.maCell
, static_cast<SCCOL
>(aSortParam
.maKeyState
[nSort
].nField
), rInfo2
.nOrg
);
1585 nRes
= CompareCell( nSort
,
1586 rInfo1
.maCell
, static_cast<SCCOL
>(rInfo1
.nOrg
), aSortParam
.maKeyState
[nSort
].nField
,
1587 rInfo2
.maCell
, static_cast<SCCOL
>(rInfo2
.nOrg
), aSortParam
.maKeyState
[nSort
].nField
);
1588 } while ( nRes
== 0 && ++nSort
< pArray
->GetUsedSorts() );
1591 ScSortInfo
& rInfo1
= pArray
->Get( 0, nIndex1
);
1592 ScSortInfo
& rInfo2
= pArray
->Get( 0, nIndex2
);
1593 if( rInfo1
.nOrg
< rInfo2
.nOrg
)
1595 else if( rInfo1
.nOrg
> rInfo2
.nOrg
)
1601 void ScTable::QuickSort( ScSortInfoArray
* pArray
, SCCOLROW nLo
, SCCOLROW nHi
)
1603 if ((nHi
- nLo
) == 1)
1605 if (Compare(pArray
, nLo
, nHi
) > 0)
1606 pArray
->Swap( nLo
, nHi
);
1614 while ((ni
<= nHi
) && (Compare(pArray
, ni
, nLo
)) < 0)
1616 while ((nj
>= nLo
) && (Compare(pArray
, nLo
, nj
)) < 0)
1621 pArray
->Swap( ni
, nj
);
1626 if ((nj
- nLo
) < (nHi
- ni
))
1629 QuickSort(pArray
, nLo
, nj
);
1631 QuickSort(pArray
, ni
, nHi
);
1636 QuickSort(pArray
, ni
, nHi
);
1638 QuickSort(pArray
, nLo
, nj
);
1643 short ScTable::Compare(SCCOLROW nIndex1
, SCCOLROW nIndex2
) const
1646 sal_uInt16 nSort
= 0;
1647 const sal_uInt32 nMaxSorts
= aSortParam
.GetSortKeyCount();
1648 if (aSortParam
.bByRow
)
1652 SCCOL nCol
= static_cast<SCCOL
>(aSortParam
.maKeyState
[nSort
].nField
);
1654 if(nCol
< GetAllocatedColumnsCount())
1656 ScRefCellValue aCell1
= aCol
[nCol
].GetCellValue(nIndex1
);
1657 ScRefCellValue aCell2
= aCol
[nCol
].GetCellValue(nIndex2
);
1658 nRes
= CompareCell(nSort
, aCell1
, nCol
, nIndex1
, aCell2
, nCol
, nIndex2
);
1660 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.maKeyState
[nSort
].bDoSort
);
1666 SCROW nRow
= aSortParam
.maKeyState
[nSort
].nField
;
1667 ScRefCellValue aCell1
;
1668 ScRefCellValue aCell2
;
1669 if(nIndex1
< GetAllocatedColumnsCount())
1670 aCell1
= aCol
[nIndex1
].GetCellValue(nRow
);
1671 if(nIndex2
< GetAllocatedColumnsCount())
1672 aCell2
= aCol
[nIndex2
].GetCellValue(nRow
);
1673 nRes
= CompareCell( nSort
, aCell1
, static_cast<SCCOL
>(nIndex1
),
1674 nRow
, aCell2
, static_cast<SCCOL
>(nIndex2
), nRow
);
1675 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.maKeyState
[nSort
].bDoSort
);
1680 bool ScTable::IsSorted( SCCOLROW nStart
, SCCOLROW nEnd
) const // over aSortParam
1682 for (SCCOLROW i
=nStart
; i
<nEnd
; i
++)
1684 if (Compare( i
, i
+1 ) > 0)
1690 void ScTable::DecoladeRow( ScSortInfoArray
* pArray
, SCROW nRow1
, SCROW nRow2
)
1693 int nMax
= nRow2
- nRow1
;
1694 for (SCROW i
= nRow1
; (i
+ 4) <= nRow2
; i
+= 4)
1696 nRow
= comphelper::rng::uniform_int_distribution(0, nMax
-1);
1697 pArray
->Swap(i
, nRow1
+ nRow
);
1702 const ScSortParam
& rSortParam
, bool bKeepQuery
, bool bUpdateRefs
,
1703 ScProgress
* pProgress
, sc::ReorderParam
* pUndo
)
1705 sc::DelayDeletingBroadcasters
delayDeletingBroadcasters(GetDoc());
1706 InitSortCollator( rSortParam
);
1707 bGlobalKeepQuery
= bKeepQuery
;
1711 // Copy over the basic sort parameters.
1712 pUndo
->maDataAreaExtras
= rSortParam
.aDataAreaExtras
;
1713 pUndo
->mbByRow
= rSortParam
.bByRow
;
1714 pUndo
->mbHiddenFiltered
= bKeepQuery
;
1715 pUndo
->mbUpdateRefs
= bUpdateRefs
;
1716 pUndo
->mbHasHeaders
= rSortParam
.bHasHeader
;
1719 // It is assumed that the data area has already been trimmed as necessary.
1721 aSortParam
= rSortParam
; // must be assigned before calling IsSorted()
1722 if (rSortParam
.bByRow
)
1724 const SCROW nLastRow
= rSortParam
.nRow2
;
1725 const SCROW nRow1
= (rSortParam
.bHasHeader
? rSortParam
.nRow1
+ 1 : rSortParam
.nRow1
);
1726 if (nRow1
< nLastRow
&& !IsSorted(nRow1
, nLastRow
))
1729 pProgress
->SetState( 0, nLastRow
-nRow1
);
1731 std::unique_ptr
<ScSortInfoArray
> pArray( CreateSortInfoArray(
1732 aSortParam
, nRow1
, nLastRow
, bKeepQuery
, bUpdateRefs
));
1734 if ( nLastRow
- nRow1
> 255 )
1735 DecoladeRow(pArray
.get(), nRow1
, nLastRow
);
1737 QuickSort(pArray
.get(), nRow1
, nLastRow
);
1738 if (pArray
->IsUpdateRefs())
1739 SortReorderByRowRefUpdate(pArray
.get(), aSortParam
.nCol1
, aSortParam
.nCol2
, pProgress
);
1742 SortReorderByRow(pArray
.get(), aSortParam
.nCol1
, aSortParam
.nCol2
, pProgress
, false);
1743 if (rSortParam
.aDataAreaExtras
.anyExtrasWanted())
1744 SortReorderAreaExtrasByRow( pArray
.get(), aSortParam
.nCol1
, aSortParam
.nCol2
,
1745 rSortParam
.aDataAreaExtras
, pProgress
);
1750 // Stored is the first data row without header row.
1751 pUndo
->maSortRange
= ScRange(rSortParam
.nCol1
, nRow1
, nTab
, rSortParam
.nCol2
, nLastRow
, nTab
);
1752 pUndo
->maDataAreaExtras
.mnStartRow
= nRow1
;
1753 pUndo
->maOrderIndices
= pArray
->GetOrderIndices();
1759 const SCCOL nLastCol
= rSortParam
.nCol2
;
1760 const SCCOL nCol1
= (rSortParam
.bHasHeader
? rSortParam
.nCol1
+ 1 : rSortParam
.nCol1
);
1761 if (nCol1
< nLastCol
&& !IsSorted(nCol1
, nLastCol
))
1764 pProgress
->SetState( 0, nLastCol
-nCol1
);
1766 std::unique_ptr
<ScSortInfoArray
> pArray( CreateSortInfoArray(
1767 aSortParam
, nCol1
, nLastCol
, bKeepQuery
, bUpdateRefs
));
1769 QuickSort(pArray
.get(), nCol1
, nLastCol
);
1770 SortReorderByColumn(pArray
.get(), rSortParam
.nRow1
, rSortParam
.nRow2
,
1771 rSortParam
.aDataAreaExtras
.mbCellFormats
, pProgress
);
1772 if (rSortParam
.aDataAreaExtras
.anyExtrasWanted() && !pArray
->IsUpdateRefs())
1773 SortReorderAreaExtrasByColumn( pArray
.get(),
1774 rSortParam
.nRow1
, rSortParam
.nRow2
, rSortParam
.aDataAreaExtras
, pProgress
);
1778 // Stored is the first data column without header column.
1779 pUndo
->maSortRange
= ScRange(nCol1
, aSortParam
.nRow1
, nTab
, nLastCol
, aSortParam
.nRow2
, nTab
);
1780 pUndo
->maDataAreaExtras
.mnStartCol
= nCol1
;
1781 pUndo
->maOrderIndices
= pArray
->GetOrderIndices();
1785 DestroySortCollator();
1788 void ScTable::Reorder( const sc::ReorderParam
& rParam
)
1790 if (rParam
.maOrderIndices
.empty())
1793 std::unique_ptr
<ScSortInfoArray
> pArray(CreateSortInfoArray(rParam
));
1799 // Re-play sorting from the known sort indices.
1800 pArray
->ReorderByRow(rParam
.maOrderIndices
);
1801 if (pArray
->IsUpdateRefs())
1802 SortReorderByRowRefUpdate(
1803 pArray
.get(), rParam
.maSortRange
.aStart
.Col(), rParam
.maSortRange
.aEnd
.Col(), nullptr);
1806 SortReorderByRow( pArray
.get(),
1807 rParam
.maSortRange
.aStart
.Col(), rParam
.maSortRange
.aEnd
.Col(), nullptr, false);
1808 if (rParam
.maDataAreaExtras
.anyExtrasWanted())
1809 SortReorderAreaExtrasByRow( pArray
.get(),
1810 rParam
.maSortRange
.aStart
.Col(), rParam
.maSortRange
.aEnd
.Col(),
1811 rParam
.maDataAreaExtras
, nullptr);
1816 // Ordering by column is much simpler. Just set the order indices and we are done.
1817 pArray
->SetOrderIndices(std::vector(rParam
.maOrderIndices
));
1818 SortReorderByColumn(
1819 pArray
.get(), rParam
.maSortRange
.aStart
.Row(), rParam
.maSortRange
.aEnd
.Row(),
1820 rParam
.maDataAreaExtras
.mbCellFormats
, nullptr);
1821 if (rParam
.maDataAreaExtras
.anyExtrasWanted() && !pArray
->IsUpdateRefs())
1822 SortReorderAreaExtrasByColumn( pArray
.get(),
1823 rParam
.maSortRange
.aStart
.Row(), rParam
.maSortRange
.aEnd
.Row(),
1824 rParam
.maDataAreaExtras
, nullptr);
1830 class SubTotalRowFinder
1832 const ScTable
& mrTab
;
1833 const ScSubTotalParam
& mrParam
;
1836 SubTotalRowFinder(const ScTable
& rTab
, const ScSubTotalParam
& rParam
) :
1837 mrTab(rTab
), mrParam(rParam
) {}
1839 bool operator() (size_t nRow
, const ScFormulaCell
* pCell
)
1841 if (!pCell
->IsSubTotal())
1844 SCCOL nStartCol
= mrParam
.nCol1
;
1845 SCCOL nEndCol
= mrParam
.nCol2
;
1847 for (SCCOL nCol
: mrTab
.GetAllocatedColumnsRange(0, nStartCol
- 1))
1849 if (mrTab
.HasData(nCol
, nRow
))
1852 for (SCCOL nCol
: mrTab
.GetAllocatedColumnsRange(nEndCol
+ 1, mrTab
.GetDoc().MaxCol()))
1854 if (mrTab
.HasData(nCol
, nRow
))
1863 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam
& rParam
)
1865 SCCOL nStartCol
= rParam
.nCol1
;
1866 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
1867 SCCOL nEndCol
= ClampToAllocatedColumns(rParam
.nCol2
);
1868 SCROW nEndRow
= rParam
.nRow2
;
1870 for (SCCOL nCol
= nStartCol
; nCol
<= nEndCol
; ++nCol
)
1872 const sc::CellStoreType
& rCells
= aCol
[nCol
].maCells
;
1873 SubTotalRowFinder
aFunc(*this, rParam
);
1874 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
=
1875 sc::FindFormula(rCells
, nStartRow
, nEndRow
, aFunc
);
1876 if (aPos
.first
!= rCells
.end())
1884 struct RemoveSubTotalsHandler
1886 std::set
<SCROW
> aRemoved
;
1888 void operator() (size_t nRow
, const ScFormulaCell
* p
)
1890 if (p
->IsSubTotal())
1891 aRemoved
.insert(nRow
);
1897 void ScTable::RemoveSubTotals( ScSubTotalParam
& rParam
)
1899 SCCOL nStartCol
= rParam
.nCol1
;
1900 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
1901 SCCOL nEndCol
= ClampToAllocatedColumns(rParam
.nCol2
);
1902 SCROW nEndRow
= rParam
.nRow2
; // will change
1904 RemoveSubTotalsHandler aFunc
;
1905 for (SCCOL nCol
= nStartCol
; nCol
<= nEndCol
; ++nCol
)
1907 const sc::CellStoreType
& rCells
= aCol
[nCol
].maCells
;
1908 sc::ParseFormula(rCells
.begin(), rCells
, nStartRow
, nEndRow
, aFunc
);
1911 auto& aRows
= aFunc
.aRemoved
;
1913 std::for_each(aRows
.rbegin(), aRows
.rend(), [this](const SCROW nRow
) {
1914 RemoveRowBreak(nRow
+1, false, true);
1915 rDocument
.DeleteRow(0, nTab
, rDocument
.MaxCol(), nTab
, nRow
, 1);
1918 rParam
.nRow2
-= aRows
.size();
1921 // Delete hard number formats (for result formulas)
1923 static void lcl_RemoveNumberFormat( ScTable
* pTab
, SCCOL nCol
, SCROW nRow
)
1925 const ScPatternAttr
* pPattern
= pTab
->GetPattern( nCol
, nRow
);
1926 if ( pPattern
->GetItemSet().GetItemState( ATTR_VALUE_FORMAT
, false )
1927 == SfxItemState::SET
)
1929 ScPatternAttr
* pNewPattern(new ScPatternAttr( *pPattern
));
1930 SfxItemSet
& rSet
= pNewPattern
->GetItemSet();
1931 rSet
.ClearItem( ATTR_VALUE_FORMAT
);
1932 rSet
.ClearItem( ATTR_LANGUAGE_FORMAT
);
1933 pTab
->SetPattern( nCol
, nRow
, CellAttributeHolder(pNewPattern
, true) );
1941 sal_uInt16 nGroupNo
;
1950 static TranslateId
lcl_GetSubTotalStrId(ScSubTotalFunc id
)
1954 case SUBTOTAL_FUNC_AVE
: return STR_FUN_TEXT_AVG
;
1955 case SUBTOTAL_FUNC_CNT
:
1956 case SUBTOTAL_FUNC_CNT2
: return STR_FUN_TEXT_COUNT
;
1957 case SUBTOTAL_FUNC_MAX
: return STR_FUN_TEXT_MAX
;
1958 case SUBTOTAL_FUNC_MIN
: return STR_FUN_TEXT_MIN
;
1959 case SUBTOTAL_FUNC_PROD
: return STR_FUN_TEXT_PRODUCT
;
1960 case SUBTOTAL_FUNC_STD
:
1961 case SUBTOTAL_FUNC_STDP
: return STR_FUN_TEXT_STDDEV
;
1962 case SUBTOTAL_FUNC_SUM
: return STR_FUN_TEXT_SUM
;
1963 case SUBTOTAL_FUNC_VAR
:
1964 case SUBTOTAL_FUNC_VARP
: return STR_FUN_TEXT_VAR
;
1967 return STR_EMPTYDATA
;
1968 // added to avoid warnings
1973 // Gets the string used for "Grand" results
1974 static TranslateId
lcl_GetGrandSubTotalStrId(ScSubTotalFunc id
)
1978 case SUBTOTAL_FUNC_AVE
: return STR_TABLE_GRAND_AVG
;
1979 case SUBTOTAL_FUNC_CNT
:
1980 case SUBTOTAL_FUNC_CNT2
: return STR_TABLE_GRAND_COUNT
;
1981 case SUBTOTAL_FUNC_MAX
: return STR_TABLE_GRAND_MAX
;
1982 case SUBTOTAL_FUNC_MIN
: return STR_TABLE_GRAND_MIN
;
1983 case SUBTOTAL_FUNC_PROD
: return STR_TABLE_GRAND_PRODUCT
;
1984 case SUBTOTAL_FUNC_STD
:
1985 case SUBTOTAL_FUNC_STDP
: return STR_TABLE_GRAND_STDDEV
;
1986 case SUBTOTAL_FUNC_SUM
: return STR_TABLE_GRAND_SUM
;
1987 case SUBTOTAL_FUNC_VAR
:
1988 case SUBTOTAL_FUNC_VARP
: return STR_TABLE_GRAND_VAR
;
1991 return STR_EMPTYDATA
;
1992 // added to avoid warnings
1997 // new intermediate results
1998 // rParam.nRow2 is changed!
2000 bool ScTable::DoSubTotals( ScSubTotalParam
& rParam
)
2002 SCCOL nStartCol
= rParam
.nCol1
;
2003 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
2004 SCCOL nEndCol
= rParam
.nCol2
;
2005 SCROW nEndRow
= rParam
.nRow2
; // will change
2007 // Remove empty rows at the end
2008 // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
2009 // If sorted, all empty rows are at the end.
2010 SCSIZE nEmpty
= GetEmptyLinesInBlock( nStartCol
, nStartRow
, nEndCol
, nEndRow
, DIR_BOTTOM
);
2013 sal_uInt16 nLevelCount
= 0; // Number of levels
2014 for (const auto& group
: rParam
.aGroups
)
2021 if (nLevelCount
==0) // do nothing
2024 // With (blank) as a separate category, subtotal rows from
2025 // the other columns must always be tested
2026 // (previously only when a column occurred more than once)
2027 bool bTestPrevSub
= ( nLevelCount
> 1 );
2029 OUString aSubString
;
2031 bool bIgnoreCase
= !rParam
.bCaseSens
;
2033 OUString aCompString
[MAXSUBTOTAL
];
2037 ScStyleSheet
* pStyle
= static_cast<ScStyleSheet
*>(rDocument
.GetStyleSheetPool()->Find(
2038 ScResId(STR_STYLENAME_RESULT
), SfxStyleFamily::Para
));
2040 bool bSpaceLeft
= true; // Success when inserting?
2042 // For performance reasons collect formula entries so their
2043 // references don't have to be tested for updates each time a new row is
2046 ::std::vector
< RowEntry
> aRowVector
;
2048 for (sal_uInt16 nLevel
=0; nLevel
<nLevelCount
&& bSpaceLeft
; nLevel
++)
2050 aRowEntry
.nGroupNo
= nLevelCount
- nLevel
- 1;
2052 const auto& group
= rParam
.aGroups
[aRowEntry
.nGroupNo
];
2053 // how many results per level
2054 SCCOL nResCount
= group
.nSubTotals
;
2056 if (nResCount
> 0) // otherwise only sort
2058 SCROW nAboveRows
= rParam
.bSummaryBelow
? nStartRow
: nStartRow
+ nLevel
;
2059 for (sal_uInt16 i
= 0; i
<= aRowEntry
.nGroupNo
; ++i
)
2061 aSubString
= GetString(rParam
.aGroups
[i
].nField
, nAboveRows
);
2063 aCompString
[i
] = ScGlobal::getCharClass().uppercase( aSubString
);
2065 aCompString
[i
] = aSubString
;
2066 } // aSubString stays on the last
2068 bool bBlockVis
= false; // group visible?
2069 aRowEntry
.nSubStartRow
= nAboveRows
;
2070 for (SCROW nRow
=nAboveRows
; nRow
<=nEndRow
+1 && bSpaceLeft
; nRow
++)
2079 for (sal_uInt16 i
= 0; i
<= aRowEntry
.nGroupNo
&& !bChanged
; ++i
)
2081 aString
= GetString(rParam
.aGroups
[i
].nField
, nRow
);
2083 aString
= ScGlobal::getCharClass().uppercase(aString
);
2084 // when sorting, blanks are separate group
2085 // otherwise blank cells are allowed below
2086 bChanged
= ( ( !aString
.isEmpty() || rParam
.bDoSort
) &&
2087 aString
!= aCompString
[i
] );
2089 if ( bChanged
&& bTestPrevSub
)
2091 // No group change on rows that will contain subtotal formulas
2092 bChanged
= std::none_of(aRowVector
.begin(), aRowVector
.end(),
2093 [&nRow
](const RowEntry
& rEntry
) { return rEntry
.nDestRow
== nRow
; });
2098 if (rParam
.bSummaryBelow
)
2100 aRowEntry
.nDestRow
= nRow
;
2101 aRowEntry
.nFuncStart
= aRowEntry
.nSubStartRow
;
2102 aRowEntry
.nFuncEnd
= nRow
- 1;
2106 aRowEntry
.nDestRow
= aRowEntry
.nSubStartRow
;
2107 aRowEntry
.nFuncStart
= aRowEntry
.nSubStartRow
+ 1;
2108 if (nRow
!= nEndRow
+ 1)
2109 aRowEntry
.nFuncEnd
= nRow
- nLevel
;
2111 aRowEntry
.nFuncEnd
= nRow
;
2114 bSpaceLeft
= rDocument
.InsertRow( 0, nTab
, rDocument
.MaxCol(), nTab
,
2115 aRowEntry
.nDestRow
, 1 );
2116 DBShowRow( aRowEntry
.nDestRow
, bBlockVis
);
2117 if ( rParam
.bPagebreak
&& nRow
< rDocument
.MaxRow() &&
2118 aRowEntry
.nSubStartRow
!= nStartRow
&& nLevel
== 0)
2119 SetRowBreak(aRowEntry
.nSubStartRow
, false, true);
2123 for ( auto& rRowEntry
: aRowVector
)
2125 if ( aRowEntry
.nDestRow
<= rRowEntry
.nSubStartRow
)
2126 ++rRowEntry
.nSubStartRow
;
2127 if ( aRowEntry
.nDestRow
<= rRowEntry
.nDestRow
)
2128 ++rRowEntry
.nDestRow
;
2129 if ( aRowEntry
.nDestRow
<= rRowEntry
.nFuncStart
)
2130 ++rRowEntry
.nFuncStart
;
2131 if ( aRowEntry
.nDestRow
<= rRowEntry
.nFuncEnd
)
2132 ++rRowEntry
.nFuncEnd
;
2134 // collect formula positions
2135 aRowVector
.push_back( aRowEntry
);
2137 OUString aOutString
= aSubString
;
2138 if (aOutString
.isEmpty())
2139 aOutString
= ScResId( STR_EMPTYDATA
);
2141 TranslateId pStrId
= STR_TABLE_ERGEBNIS
;
2142 if ( nResCount
== 1 )
2143 pStrId
= lcl_GetSubTotalStrId(group
.func(0));
2144 aOutString
+= ScResId(pStrId
);
2145 SetString(group
.nField
, aRowEntry
.nDestRow
, nTab
, aOutString
);
2146 ApplyStyle(group
.nField
, aRowEntry
.nDestRow
, pStyle
);
2150 aRowEntry
.nSubStartRow
= nRow
;
2151 for (sal_uInt16 i
= 0; i
<= aRowEntry
.nGroupNo
; ++i
)
2153 aSubString
= GetString(rParam
.aGroups
[i
].nField
, nRow
);
2155 aCompString
[i
] = ScGlobal::getCharClass().uppercase( aSubString
);
2157 aCompString
[i
] = aSubString
;
2161 bBlockVis
= !RowFiltered(nRow
);
2166 if (!aRowVector
.empty())
2168 SCROW nGlobalEndRow
= 0;
2169 SCROW nGlobalEndFunc
= 0;
2170 for (auto& rRowEntry
: aRowVector
)
2172 if (!rParam
.bSummaryBelow
)
2174 // if we have Global summary above, we need to shift summary rows down
2175 rRowEntry
.nDestRow
= rRowEntry
.nDestRow
+ nLevelCount
;
2176 rRowEntry
.nFuncEnd
= rRowEntry
.nFuncEnd
+ nLevelCount
;
2177 rRowEntry
.nFuncStart
= rRowEntry
.nFuncStart
+ nLevelCount
- rRowEntry
.nGroupNo
;
2178 rRowEntry
.nSubStartRow
= rRowEntry
.nSubStartRow
+ nLevelCount
;
2181 nGlobalEndRow
= (nGlobalEndRow
< rRowEntry
.nDestRow
) ? rRowEntry
.nDestRow
: nGlobalEndRow
;
2182 nGlobalEndFunc
= (nGlobalEndFunc
< rRowEntry
.nFuncEnd
) ? rRowEntry
.nFuncEnd
: nGlobalEndRow
;
2185 // generate global total
2186 SCROW nGlobalStartRow
= aRowVector
[0].nSubStartRow
;
2187 SCROW nGlobalStartFunc
= aRowVector
[0].nFuncStart
;
2189 for (sal_uInt16 nLevel
= 0; nLevel
<nLevelCount
; nLevel
++)
2191 const sal_uInt16 nGroupNo
= nLevelCount
- nLevel
- 1;
2192 const auto& group
= rParam
.aGroups
[nGroupNo
];
2193 if (!group
.nSubTotals
)
2195 // No subtotal function given for this group => no formula or
2196 // label and do not insert a row.
2200 aRowEntry
.nGroupNo
= nGroupNo
;
2201 if (rParam
.bSummaryBelow
)
2203 // increment end row
2206 // add row entry for formula
2207 aRowEntry
.nSubStartRow
= nGlobalStartRow
;
2208 aRowEntry
.nFuncStart
= nGlobalStartFunc
;
2209 aRowEntry
.nDestRow
= nGlobalEndRow
;
2210 aRowEntry
.nFuncEnd
= nGlobalEndFunc
;
2217 // if we have Global summary we need to shift summary rows down
2218 aRowEntry
.nSubStartRow
= nGlobalStartRow
- nGroupNo
- 1;
2219 aRowEntry
.nFuncStart
= nGlobalStartFunc
- nGroupNo
- 1;
2220 aRowEntry
.nDestRow
= nGlobalStartRow
- nGroupNo
- 1;
2221 aRowEntry
.nFuncEnd
= nGlobalEndFunc
;
2224 bSpaceLeft
= rDocument
.InsertRow(0, nTab
, rDocument
.MaxCol(), nTab
, aRowEntry
.nDestRow
, 1);
2228 aRowVector
.push_back(aRowEntry
);
2230 DBShowRow(aRowEntry
.nDestRow
, true);
2233 OUString label
= ScResId(lcl_GetGrandSubTotalStrId(group
.func(0)));
2234 SetString(group
.nField
, aRowEntry
.nDestRow
, nTab
, label
);
2235 ApplyStyle(group
.nField
, aRowEntry
.nDestRow
, pStyle
);
2240 // now insert the formulas
2241 ScComplexRefData aRef
;
2243 aRef
.Ref1
.SetAbsTab(nTab
);
2244 aRef
.Ref2
.SetAbsTab(nTab
);
2245 for (const auto& rRowEntry
: aRowVector
)
2247 const auto& group
= rParam
.aGroups
[rRowEntry
.nGroupNo
];
2248 SCCOL nResCount
= group
.nSubTotals
;
2249 for ( SCCOL nResult
=0; nResult
< nResCount
; ++nResult
)
2251 aRef
.Ref1
.SetAbsCol(group
.col(nResult
));
2252 aRef
.Ref1
.SetAbsRow(rRowEntry
.nFuncStart
);
2253 aRef
.Ref2
.SetAbsCol(group
.col(nResult
));
2254 aRef
.Ref2
.SetAbsRow(rRowEntry
.nFuncEnd
);
2256 ScTokenArray
aArr(rDocument
);
2257 aArr
.AddOpCode( ocSubTotal
);
2258 aArr
.AddOpCode( ocOpen
);
2259 aArr
.AddDouble( static_cast<double>(group
.func(nResult
)) );
2260 aArr
.AddOpCode( ocSep
);
2261 aArr
.AddDoubleReference( aRef
);
2262 aArr
.AddOpCode( ocClose
);
2263 aArr
.AddOpCode( ocStop
);
2264 ScFormulaCell
* pCell
= new ScFormulaCell(
2265 rDocument
, ScAddress(group
.col(nResult
), rRowEntry
.nDestRow
, nTab
), aArr
);
2266 if ( rParam
.bIncludePattern
)
2267 pCell
->SetNeedNumberFormat(true);
2269 SetFormulaCell(group
.col(nResult
), rRowEntry
.nDestRow
, pCell
);
2270 if (group
.col(nResult
) != group
.nField
)
2272 ApplyStyle(group
.col(nResult
), rRowEntry
.nDestRow
, pStyle
);
2274 lcl_RemoveNumberFormat(this, group
.col(nResult
), rRowEntry
.nDestRow
);
2280 //TODO: according to setting, shift intermediate-sum rows up?
2282 //TODO: create Outlines directly?
2285 DoAutoOutline( nStartCol
, nStartRow
, nEndCol
, nEndRow
);
2287 rParam
.nRow2
= nEndRow
; // new end
2291 void ScTable::TopTenQuery( ScQueryParam
& rParam
)
2293 bool bSortCollatorInitialized
= false;
2294 SCSIZE nEntryCount
= rParam
.GetEntryCount();
2295 SCROW nRow1
= (rParam
.bHasHeader
? rParam
.nRow1
+ 1 : rParam
.nRow1
);
2296 SCSIZE nCount
= static_cast<SCSIZE
>(rParam
.nRow2
- nRow1
+ 1);
2297 for ( SCSIZE i
=0; (i
<nEntryCount
) && (rParam
.GetEntry(i
).bDoQuery
); i
++ )
2299 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
2300 ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
2302 for (ScQueryEntry::Item
& rItem
: rItems
)
2311 ScSortParam
aLocalSortParam(rParam
, static_cast<SCCOL
>(rEntry
.nField
));
2312 aSortParam
= aLocalSortParam
; // used in CreateSortInfoArray, Compare
2313 if (!bSortCollatorInitialized
)
2315 bSortCollatorInitialized
= true;
2316 InitSortCollator(aLocalSortParam
);
2318 std::unique_ptr
<ScSortInfoArray
> pArray(CreateSortInfoArray(aSortParam
, nRow1
, rParam
.nRow2
, bGlobalKeepQuery
, false));
2319 DecoladeRow(pArray
.get(), nRow1
, rParam
.nRow2
);
2320 QuickSort(pArray
.get(), nRow1
, rParam
.nRow2
);
2321 std::unique_ptr
<ScSortInfo
[]> const& ppInfo
= pArray
->GetFirstArray();
2322 SCSIZE nValidCount
= nCount
;
2323 // Don't count note or blank cells, they are sorted to the end
2324 while (nValidCount
> 0 && ppInfo
[nValidCount
- 1].maCell
.isEmpty())
2326 // Don't count Strings, they are between Value and blank
2327 while (nValidCount
> 0 && ppInfo
[nValidCount
- 1].maCell
.hasString())
2329 if (nValidCount
> 0)
2331 if (rItem
.meType
== ScQueryEntry::ByString
)
2332 { // by string ain't going to work
2333 rItem
.meType
= ScQueryEntry::ByValue
;
2334 rItem
.mfVal
= 10; // 10 and 10% respectively
2336 SCSIZE nVal
= (rItem
.mfVal
>= 1 ? static_cast<SCSIZE
>(rItem
.mfVal
) : 1);
2342 rEntry
.eOp
= SC_GREATER_EQUAL
;
2343 if (nVal
> nValidCount
)
2345 nOffset
= nValidCount
- nVal
; // 1 <= nVal <= nValidCount
2350 rEntry
.eOp
= SC_LESS_EQUAL
;
2351 if (nVal
> nValidCount
)
2353 nOffset
= nVal
- 1; // 1 <= nVal <= nValidCount
2358 rEntry
.eOp
= SC_GREATER_EQUAL
;
2361 nOffset
= nValidCount
- (nValidCount
* nVal
/ 100);
2362 if (nOffset
>= nValidCount
)
2363 nOffset
= nValidCount
- 1;
2368 rEntry
.eOp
= SC_LESS_EQUAL
;
2371 nOffset
= (nValidCount
* nVal
/ 100);
2372 if (nOffset
>= nValidCount
)
2373 nOffset
= nValidCount
- 1;
2378 // added to avoid warnings
2381 ScRefCellValue aCell
= ppInfo
[nOffset
].maCell
;
2382 if (aCell
.hasNumeric())
2383 rItem
.mfVal
= aCell
.getValue();
2386 OSL_FAIL("TopTenQuery: pCell no ValueData");
2387 rEntry
.eOp
= SC_GREATER_EQUAL
;
2393 rEntry
.eOp
= SC_GREATER_EQUAL
;
2394 rItem
.meType
= ScQueryEntry::ByValue
;
2401 // added to avoid warnings
2406 if ( bSortCollatorInitialized
)
2407 DestroySortCollator();
2412 bool CanOptimizeQueryStringToNumber( const SvNumberFormatter
* pFormatter
, sal_uInt32 nFormatIndex
, bool& bDateFormat
)
2414 // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
2415 // The problem with this optimization is that the autofilter dialog apparently converts
2416 // the value to text and then converts that back to a number for filtering.
2417 // If that leads to any change of value (such as when time is rounded to seconds),
2418 // even matching values will be filtered out. Therefore query by value only for formats
2419 // where no such change should occur.
2420 if(const SvNumberformat
* pEntry
= pFormatter
->GetEntry(nFormatIndex
))
2422 switch(pEntry
->GetType())
2424 case SvNumFormatType::NUMBER
:
2425 case SvNumFormatType::FRACTION
:
2426 case SvNumFormatType::SCIENTIFIC
:
2428 case SvNumFormatType::DATE
:
2429 case SvNumFormatType::DATETIME
:
2439 class PrepareQueryItem
2441 const ScDocument
& mrDoc
;
2442 const bool mbRoundForFilter
;
2444 explicit PrepareQueryItem(const ScDocument
& rDoc
, bool bRoundForFilter
) :
2445 mrDoc(rDoc
), mbRoundForFilter(bRoundForFilter
) {}
2447 void operator() (ScQueryEntry::Item
& rItem
)
2449 rItem
.mbRoundForFilter
= mbRoundForFilter
;
2451 if (rItem
.meType
!= ScQueryEntry::ByString
&& rItem
.meType
!= ScQueryEntry::ByDate
)
2454 sal_uInt32 nIndex
= 0;
2455 bool bNumber
= mrDoc
.GetFormatTable()->
2456 IsNumberFormat(rItem
.maString
.getString(), nIndex
, rItem
.mfVal
);
2458 // Advanced Filter creates only ByString queries that need to be
2459 // converted to ByValue if appropriate. rItem.mfVal now holds the value
2460 // if bNumber==true.
2462 if (rItem
.meType
== ScQueryEntry::ByString
)
2464 bool bDateFormat
= false;
2465 if (bNumber
&& CanOptimizeQueryStringToNumber( mrDoc
.GetFormatTable(), nIndex
, bDateFormat
))
2466 rItem
.meType
= ScQueryEntry::ByValue
;
2471 // Double-check if the query by date is really appropriate.
2473 if (bNumber
&& ((nIndex
% SV_COUNTRY_LANGUAGE_OFFSET
) != 0))
2475 const SvNumberformat
* pEntry
= mrDoc
.GetFormatTable()->GetEntry(nIndex
);
2478 SvNumFormatType nNumFmtType
= pEntry
->GetType();
2479 if (!(nNumFmtType
& SvNumFormatType::DATE
) || (nNumFmtType
& SvNumFormatType::TIME
))
2480 rItem
.meType
= ScQueryEntry::ByValue
; // not a date only
2482 rItem
.meType
= ScQueryEntry::ByDate
; // date only
2485 rItem
.meType
= ScQueryEntry::ByValue
; // what the ... not a date
2488 rItem
.meType
= ScQueryEntry::ByValue
; // not a date
2492 void lcl_PrepareQuery( const ScDocument
* pDoc
, ScTable
* pTab
, ScQueryParam
& rParam
, bool bRoundForFilter
)
2494 bool bTopTen
= false;
2495 SCSIZE nEntryCount
= rParam
.GetEntryCount();
2497 for ( SCSIZE i
= 0; i
< nEntryCount
; ++i
)
2499 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
2500 if (!rEntry
.bDoQuery
)
2503 ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
2504 std::for_each(rItems
.begin(), rItems
.end(), PrepareQueryItem(*pDoc
, bRoundForFilter
));
2508 switch ( rEntry
.eOp
)
2527 pTab
->TopTenQuery( rParam
);
2533 void ScTable::PrepareQuery( ScQueryParam
& rQueryParam
)
2535 lcl_PrepareQuery(&rDocument
, this, rQueryParam
, false);
2538 SCSIZE
ScTable::Query(const ScQueryParam
& rParamOrg
, bool bKeepSub
)
2540 ScQueryParam
aParam( rParamOrg
);
2541 typedef std::unordered_set
<OUString
> StrSetType
;
2544 bool bStarted
= false;
2545 bool bOldResult
= true;
2546 SCROW nOldStart
= 0;
2551 SCROW nHeader
= aParam
.bHasHeader
? 1 : 0;
2553 lcl_PrepareQuery(&rDocument
, this, aParam
, true);
2555 if (!aParam
.bInplace
)
2557 nOutRow
= aParam
.nDestRow
+ nHeader
;
2559 CopyData( aParam
.nCol1
, aParam
.nRow1
, aParam
.nCol2
, aParam
.nRow1
,
2560 aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
2563 sc::TableColumnBlockPositionSet
blockPos( GetDoc(), nTab
); // cache mdds access
2564 ScQueryEvaluator
queryEvaluator(GetDoc(), *this, aParam
);
2566 SCROW nRealRow2
= aParam
.nRow2
;
2567 for (SCROW j
= aParam
.nRow1
+ nHeader
; j
<= nRealRow2
; ++j
)
2569 bool bResult
; // Filter result
2570 bool bValid
= queryEvaluator
.ValidQuery(j
, nullptr, &blockPos
);
2571 if (!bValid
&& bKeepSub
) // Keep subtotals
2573 for (SCCOL nCol
=aParam
.nCol1
; nCol
<=aParam
.nCol2
&& !bValid
; nCol
++)
2575 ScRefCellValue aCell
= GetCellValue(nCol
, j
);
2576 if (aCell
.getType() != CELLTYPE_FORMULA
)
2579 if (!aCell
.getFormula()->IsSubTotal())
2582 if (RefVisible(aCell
.getFormula()))
2588 if (aParam
.bDuplicate
)
2592 OUStringBuffer aStr
;
2593 for (SCCOL k
=aParam
.nCol1
; k
<= aParam
.nCol2
; k
++)
2595 OUString aCellStr
= GetString(k
, j
);
2596 aStr
.append(aCellStr
+ u
"\x0001");
2599 bResult
= aStrSet
.insert(aStr
.makeStringAndClear()).second
; // unique if inserted.
2605 if (aParam
.bInplace
)
2607 if (bResult
== bOldResult
&& bStarted
)
2612 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
2613 nOldStart
= nOldEnd
= j
;
2614 bOldResult
= bResult
;
2622 CopyData( aParam
.nCol1
,j
, aParam
.nCol2
,j
, aParam
.nDestCol
,nOutRow
,aParam
.nDestTab
);
2623 if( nTab
== aParam
.nDestTab
) // copy to self, changes may invalidate caching position hints
2624 blockPos
.invalidate();
2632 if (aParam
.bInplace
&& bStarted
)
2633 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
2635 if (aParam
.bInplace
)
2641 bool ScTable::CreateExcelQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
2644 std::unique_ptr
<SCCOL
[]> pFields(new SCCOL
[nCol2
-nCol1
+1]);
2647 OSL_ENSURE( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
2648 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
2649 SCROW nDBRow1
= rQueryParam
.nRow1
;
2650 SCCOL nDBCol2
= rQueryParam
.nCol2
;
2651 // First row must be column headers
2652 while (bValid
&& (nCol
<= nCol2
))
2654 OUString aQueryStr
= GetUpperCellString(nCol
, nRow1
);
2655 bool bFound
= false;
2656 SCCOL i
= rQueryParam
.nCol1
;
2657 while (!bFound
&& (i
<= nDBCol2
))
2659 if ( nTab
== nDBTab
)
2660 aCellStr
= GetUpperCellString(i
, nDBRow1
);
2662 aCellStr
= rDocument
.GetUpperCellString(i
, nDBRow1
, nDBTab
);
2663 bFound
= (aCellStr
== aQueryStr
);
2667 pFields
[nCol
- nCol1
] = i
;
2674 SCSIZE nVisible
= 0;
2675 for ( nCol
=nCol1
; nCol
<=ClampToAllocatedColumns(nCol2
); nCol
++ )
2676 nVisible
+= aCol
[nCol
].VisibleCount( nRow1
+1, nRow2
);
2678 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
2680 OSL_FAIL("too many filter criteria");
2684 SCSIZE nNewEntries
= nVisible
;
2685 rQueryParam
.Resize( nNewEntries
);
2688 SCROW nRow
= nRow1
+ 1;
2689 svl::SharedStringPool
& rPool
= rDocument
.GetSharedStringPool();
2690 while (nRow
<= nRow2
)
2693 while (nCol
<= nCol2
)
2695 aCellStr
= GetInputString( nCol
, nRow
);
2696 if (!aCellStr
.isEmpty())
2698 if (nIndex
< nNewEntries
)
2700 rQueryParam
.GetEntry(nIndex
).nField
= pFields
[nCol
- nCol1
];
2701 rQueryParam
.FillInExcelSyntax(rPool
, aCellStr
, nIndex
, nullptr);
2703 if (nIndex
< nNewEntries
)
2704 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_AND
;
2712 if (nIndex
< nNewEntries
)
2713 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_OR
;
2719 bool ScTable::CreateStarQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
2721 // A valid StarQuery must be at least 4 columns wide. To be precise it
2722 // should be exactly 4 columns ...
2723 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
2724 // column Excel style query range immediately left to itself would result
2725 // in a circular reference when the field name or operator or value (first
2726 // to third query range column) is obtained (#i58354#). Furthermore, if the
2727 // range wasn't sufficiently specified data changes wouldn't flag formula
2728 // cells for recalculation.
2729 if (nCol2
- nCol1
< 3)
2736 OSL_ENSURE( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
2737 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
2738 SCROW nDBRow1
= rQueryParam
.nRow1
;
2739 SCCOL nDBCol2
= rQueryParam
.nCol2
;
2741 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRow2
-nRow1
+1);
2742 rQueryParam
.Resize( nNewEntries
);
2743 svl::SharedStringPool
& rPool
= rDocument
.GetSharedStringPool();
2747 ScQueryEntry
& rEntry
= rQueryParam
.GetEntry(nIndex
);
2750 // First column AND/OR
2753 aCellStr
= GetUpperCellString(nCol1
, nRow
);
2754 if ( aCellStr
== ScResId(STR_TABLE_AND
) )
2756 rEntry
.eConnect
= SC_AND
;
2759 else if ( aCellStr
== ScResId(STR_TABLE_OR
) )
2761 rEntry
.eConnect
= SC_OR
;
2765 // Second column field name
2766 if ((nIndex
< 1) || bValid
)
2768 bool bFound
= false;
2769 aCellStr
= GetUpperCellString(nCol1
+ 1, nRow
);
2770 for (SCCOL i
=rQueryParam
.nCol1
; (i
<= nDBCol2
) && (!bFound
); i
++)
2773 if ( nTab
== nDBTab
)
2774 aFieldStr
= GetUpperCellString(i
, nDBRow1
);
2776 aFieldStr
= rDocument
.GetUpperCellString(i
, nDBRow1
, nDBTab
);
2777 bFound
= (aCellStr
== aFieldStr
);
2787 // Third column operator =<>...
2790 aCellStr
= GetUpperCellString(nCol1
+ 2, nRow
);
2791 if (aCellStr
.startsWith("<"))
2793 if (aCellStr
[1] == '>')
2794 rEntry
.eOp
= SC_NOT_EQUAL
;
2795 else if (aCellStr
[1] == '=')
2796 rEntry
.eOp
= SC_LESS_EQUAL
;
2798 rEntry
.eOp
= SC_LESS
;
2800 else if (aCellStr
.startsWith(">"))
2802 if (aCellStr
[1] == '=')
2803 rEntry
.eOp
= SC_GREATER_EQUAL
;
2805 rEntry
.eOp
= SC_GREATER
;
2807 else if (aCellStr
.startsWith("="))
2808 rEntry
.eOp
= SC_EQUAL
;
2811 // Fourth column values
2814 OUString aStr
= GetString(nCol1
+ 3, nRow
);
2815 rEntry
.GetQueryItem().maString
= rPool
.intern(aStr
);
2816 rEntry
.bDoQuery
= true;
2821 while (bValid
&& (nRow
<= nRow2
) /* && (nIndex < MAXQUERY) */ );
2825 bool ScTable::CreateQueryParam(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
2828 PutInOrder(nCol1
, nCol2
);
2829 PutInOrder(nRow1
, nRow2
);
2831 nCount
= rQueryParam
.GetEntryCount();
2832 for (i
=0; i
< nCount
; i
++)
2833 rQueryParam
.GetEntry(i
).Clear();
2835 // Standard query table
2836 bool bValid
= CreateStarQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
2837 // Excel Query table
2839 bValid
= CreateExcelQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
2841 SvNumberFormatter
* pFormatter
= rDocument
.GetFormatTable();
2842 nCount
= rQueryParam
.GetEntryCount();
2845 // query type must be set
2846 for (i
=0; i
< nCount
; i
++)
2848 ScQueryEntry::Item
& rItem
= rQueryParam
.GetEntry(i
).GetQueryItem();
2849 sal_uInt32 nIndex
= 0;
2850 bool bNumber
= pFormatter
->IsNumberFormat(
2851 rItem
.maString
.getString(), nIndex
, rItem
.mfVal
);
2852 bool bDateFormat
= false;
2853 rItem
.meType
= bNumber
&& CanOptimizeQueryStringToNumber( pFormatter
, nIndex
, bDateFormat
)
2854 ? ScQueryEntry::ByValue
: (bDateFormat
? ScQueryEntry::ByDate
: ScQueryEntry::ByString
);
2859 for (i
=0; i
< nCount
; i
++)
2860 rQueryParam
.GetEntry(i
).Clear();
2865 bool ScTable::HasColHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
) const
2867 if (nStartRow
== nEndRow
)
2868 // Assume only data.
2869 /* XXX NOTE: previous behavior still checked this one row and could
2870 * evaluate it has header row, but that doesn't make much sense. */
2873 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
2875 CellType eType
= GetCellType( nCol
, nStartRow
);
2876 // Any non-text cell in first row => not headers.
2877 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
2881 // First row all text cells, any non-text cell in second row => headers.
2882 SCROW nTestRow
= nStartRow
+ 1;
2883 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
2885 CellType eType
= GetCellType( nCol
, nTestRow
);
2886 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
2890 // Also second row all text cells => first row not headers.
2894 bool ScTable::HasRowHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
) const
2896 if (nStartCol
== nEndCol
)
2897 // Assume only data.
2898 /* XXX NOTE: previous behavior still checked this one column and could
2899 * evaluate it has header column, but that doesn't make much sense. */
2902 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
2904 CellType eType
= GetCellType( nStartCol
, nRow
);
2905 // Any non-text cell in first column => not headers.
2906 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
2910 // First column all text cells, any non-text cell in second column => headers.
2911 SCCOL nTestCol
= nStartCol
+ 1;
2912 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
2914 CellType eType
= GetCellType( nRow
, nTestCol
);
2915 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
2919 // Also second column all text cells => first column not headers.
2923 void ScTable::GetFilterEntries( SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, ScFilterEntries
& rFilterEntries
, bool bFiltering
)
2925 if (nCol
>= aCol
.size())
2928 sc::ColumnBlockConstPosition aBlockPos
;
2929 aCol
[nCol
].InitBlockPosition(aBlockPos
);
2930 aCol
[nCol
].GetFilterEntries(aBlockPos
, nRow1
, nRow2
, rFilterEntries
, bFiltering
, false /*bFilteredRow*/);
2932 SCROW nLastRow
= aBlockPos
.miCellPos
->position
;
2933 if (nLastRow
< nRow2
)
2935 aCol
[nCol
].GetBackColorFilterEntries(nLastRow
, nRow2
, rFilterEntries
);
2939 void ScTable::GetFilteredFilterEntries(
2940 SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, const ScQueryParam
& rParam
, ScFilterEntries
& rFilterEntries
, bool bFiltering
)
2942 if (nCol
>= aCol
.size())
2945 sc::ColumnBlockConstPosition aBlockPos
;
2946 aCol
[nCol
].InitBlockPosition(aBlockPos
);
2948 // remove the entry for this column from the query parameter
2949 ScQueryParam
aParam( rParam
);
2950 aParam
.RemoveEntryByField(nCol
);
2952 lcl_PrepareQuery(&rDocument
, this, aParam
, true);
2953 ScQueryEvaluator
queryEvaluator(GetDoc(), *this, aParam
);
2954 for ( SCROW j
= nRow1
; j
<= nRow2
; ++j
)
2956 if (queryEvaluator
.ValidQuery(j
))
2958 aCol
[nCol
].GetFilterEntries(aBlockPos
, j
, j
, rFilterEntries
, bFiltering
, false/*bFilteredRow*/);
2962 aCol
[nCol
].GetFilterEntries(aBlockPos
, j
, j
, rFilterEntries
, bFiltering
, true/*bFilteredRow*/);
2967 bool ScTable::GetDataEntries(SCCOL nCol
, SCROW nRow
, std::set
<ScTypedStrData
>& rStrings
)
2969 if (!ValidCol(nCol
) || nCol
>= GetAllocatedColumnsCount())
2971 return aCol
[nCol
].GetDataEntries( nRow
, rStrings
);
2974 sal_uInt64
ScTable::GetCellCount() const
2976 sal_uInt64 nCellCount
= 0;
2978 for ( SCCOL nCol
=0; nCol
< aCol
.size(); nCol
++ )
2979 nCellCount
+= aCol
[nCol
].GetCellCount();
2984 sal_uInt64
ScTable::GetWeightedCount() const
2986 sal_uInt64 nCellCount
= 0;
2988 for ( SCCOL nCol
=0; nCol
< aCol
.size(); nCol
++ )
2989 nCellCount
+= aCol
[nCol
].GetWeightedCount();
2994 sal_uInt64
ScTable::GetWeightedCount(SCROW nStartRow
, SCROW nEndRow
) const
2996 sal_uInt64 nCellCount
= 0;
2998 for ( SCCOL nCol
=0; nCol
< aCol
.size(); nCol
++ )
2999 nCellCount
+= aCol
[nCol
].GetWeightedCount(nStartRow
, nEndRow
);
3004 sal_uInt64
ScTable::GetCodeCount() const
3006 sal_uInt64 nCodeCount
= 0;
3008 for ( SCCOL nCol
=0; nCol
< aCol
.size(); nCol
++ )
3009 if ( aCol
[nCol
].GetCellCount() )
3010 nCodeCount
+= aCol
[nCol
].GetCodeCount();
3015 sal_Int32
ScTable::GetMaxStringLen( SCCOL nCol
, SCROW nRowStart
,
3016 SCROW nRowEnd
, rtl_TextEncoding eCharSet
) const
3018 if ( IsColValid( nCol
) )
3019 return aCol
[nCol
].GetMaxStringLen( nRowStart
, nRowEnd
, eCharSet
);
3024 sal_Int32
ScTable::GetMaxNumberStringLen(
3025 sal_uInt16
& nPrecision
, SCCOL nCol
, SCROW nRowStart
, SCROW nRowEnd
) const
3027 if ( IsColValid( nCol
) )
3028 return aCol
[nCol
].GetMaxNumberStringLen( nPrecision
, nRowStart
, nRowEnd
);
3033 void ScTable::UpdateSelectionFunction( ScFunctionData
& rData
, const ScMarkData
& rMark
)
3035 ScRangeList aRanges
= rMark
.GetMarkedRangesForTab( nTab
);
3036 ScRange
aMarkArea( ScAddress::UNINITIALIZED
);
3037 if (rMark
.IsMultiMarked())
3038 aMarkArea
= rMark
.GetMultiMarkArea();
3039 else if (rMark
.IsMarked())
3040 aMarkArea
= rMark
.GetMarkArea();
3043 assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
3044 aMarkArea
.aStart
.SetCol(0);
3045 aMarkArea
.aEnd
.SetCol(rDocument
.MaxCol());
3047 const SCCOL nStartCol
= aMarkArea
.aStart
.Col();
3048 const SCCOL nEndCol
= ClampToAllocatedColumns(aMarkArea
.aEnd
.Col());
3049 for (SCCOL nCol
= nStartCol
; nCol
<= nEndCol
&& !rData
.getError(); ++nCol
)
3051 if (mpColFlags
&& ColHidden(nCol
))
3054 aCol
[nCol
].UpdateSelectionFunction(aRanges
, rData
, *mpHiddenRows
);
3058 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */