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 <rtl/math.hxx>
21 #include <comphelper/random.hxx>
22 #include <unotools/textsearch.hxx>
23 #include <svl/zforlist.hxx>
24 #include <svl/zformat.hxx>
25 #include <unotools/charclass.hxx>
26 #include <unotools/collatorwrapper.hxx>
27 #include <com/sun/star/i18n/CollatorOptions.hpp>
29 #include <unotools/transliterationwrapper.hxx>
32 #include "scitems.hxx"
34 #include "formulacell.hxx"
35 #include "document.hxx"
36 #include "globstr.hrc"
38 #include "stlpool.hxx"
39 #include "compiler.hxx"
40 #include "patattr.hxx"
41 #include "subtotal.hxx"
42 #include "docoptio.hxx"
43 #include "markdata.hxx"
44 #include "rangelst.hxx"
45 #include "attarray.hxx"
46 #include "userlist.hxx"
47 #include "progress.hxx"
48 #include "cellform.hxx"
50 #include "queryparam.hxx"
51 #include "queryentry.hxx"
52 #include "segmenttree.hxx"
53 #include "subtotalparam.hxx"
54 #include "docpool.hxx"
55 #include "cellvalue.hxx"
56 #include "tokenarray.hxx"
57 #include "mtvcellfunc.hxx"
58 #include "columnspanset.hxx"
59 #include <fstalgorithm.hxx>
60 #include <listenercontext.hxx>
61 #include <sharedformula.hxx>
62 #include <stlsheet.hxx>
63 #include <refhint.hxx>
64 #include <listenerquery.hxx>
65 #include <bcaslot.hxx>
66 #include <reordermap.hxx>
68 #include <svl/sharedstringpool.hxx>
70 #include <unordered_set>
72 #include <boost/checked_delete.hpp>
73 #include <boost/scoped_ptr.hpp>
74 #include <boost/scoped_array.hpp>
75 #include <boost/noncopyable.hpp>
76 #include <boost/ptr_container/ptr_vector.hpp>
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
95 String after the last number token. This may still contain number strings.
98 Number converted from the middle number string
100 @return Returns TRUE if a numeral element is found in a given string, or
101 FALSE if no numeral element is found.
103 bool SplitString( const OUString
&sWhole
,
104 OUString
&sPrefix
, OUString
&sSuffix
, double &fNum
)
106 i18n::LocaleDataItem aLocaleItem
= ScGlobal::pLocaleData
->getLocaleItem();
108 // Get prefix element
109 OUString sEmpty
, sUser
= "-";
110 ParseResult aPRPre
= ScGlobal::pCharClass
->parsePredefinedToken(
111 KParseType::IDENTNAME
, sWhole
, 0,
112 KParseTokens::ANY_LETTER
, sUser
, KParseTokens::ANY_LETTER
, sUser
);
113 sPrefix
= sWhole
.copy( 0, aPRPre
.EndPos
);
115 // Return FALSE if no numeral element is found
116 if ( aPRPre
.EndPos
== sWhole
.getLength() )
119 // Get numeral element
120 sUser
= aLocaleItem
.decimalSeparator
;
121 ParseResult aPRNum
= ScGlobal::pCharClass
->parsePredefinedToken(
122 KParseType::ANY_NUMBER
, sWhole
, aPRPre
.EndPos
,
123 KParseTokens::ANY_NUMBER
, sEmpty
, KParseTokens::ANY_NUMBER
, sUser
);
125 if ( aPRNum
.EndPos
== aPRPre
.EndPos
)
129 sSuffix
= sWhole
.copy( aPRNum
.EndPos
);
134 /** Naturally compares two given strings.
136 This is the main function that should be called externally. It returns
137 either 1, 0, or -1 depending on the comparison result of given two strings.
146 Boolean value for case sensitivity
149 Pointer to user defined sort list
152 Pointer to collator wrapper for normal string comparison
154 @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
157 short Compare( const OUString
&sInput1
, const OUString
&sInput2
,
158 const bool bCaseSens
, const ScUserListData
* pData
, const CollatorWrapper
*pCW
)
160 OUString
sStr1( sInput1
), sStr2( sInput2
), sPre1
, sSuf1
, sPre2
, sSuf2
;
165 bool bNumFound1
= SplitString( sStr1
, sPre1
, sSuf1
, nNum1
);
166 bool bNumFound2
= SplitString( sStr2
, sPre2
, sSuf2
, nNum2
);
168 short nPreRes
; // Prefix comparison result
173 if ( !bNumFound1
|| !bNumFound2
)
174 return static_cast<short>(pData
->Compare( sStr1
, sStr2
));
176 nPreRes
= pData
->Compare( sPre1
, sPre2
);
180 if ( !bNumFound1
|| !bNumFound2
)
181 return static_cast<short>(pData
->ICompare( sStr1
, sStr2
));
183 nPreRes
= pData
->ICompare( sPre1
, sPre2
);
188 if ( !bNumFound1
|| !bNumFound2
)
189 return static_cast<short>(pCW
->compareString( sStr1
, sStr2
));
191 nPreRes
= static_cast<short>(pCW
->compareString( sPre1
, sPre2
));
194 // Prefix strings differ. Return immediately.
195 if ( nPreRes
!= 0 ) return nPreRes
;
197 if ( nNum1
!= nNum2
)
199 if ( nNum1
< nNum2
) return -1;
200 return (nNum1
> nNum2
) ? 1 : 0;
203 // The prefix and the first numerical elements are equal, but the suffix
204 // strings may still differ. Stay in the loop.
216 // STATIC DATA -----------------------------------------------------------
220 ScRefCellValue maCell
;
222 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo
);
224 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo
)
226 // END OF STATIC DATA -----------------------------------------------------
228 class ScSortInfoArray
: boost::noncopyable
234 ScRefCellValue maCell
;
235 const sc::CellTextAttr
* mpAttr
;
236 const SvtBroadcaster
* mpBroadcaster
;
237 const ScPostIt
* mpNote
;
238 const ScPatternAttr
* mpPattern
;
240 Cell() : mpAttr(NULL
), mpBroadcaster(NULL
), mpNote(NULL
), mpPattern(NULL
) {}
245 std::vector
<Cell
> maCells
;
250 Row( size_t nColSize
) : maCells(nColSize
, Cell()), mbHidden(false), mbFiltered(false) {}
253 typedef std::vector
<Row
*> RowsType
;
256 boost::scoped_ptr
<RowsType
> mpRows
; /// row-wise data table for sort by row operation.
258 ScSortInfo
*** pppInfo
;
261 SCCOLROW mnLastIndex
; /// index of last non-empty cell position.
262 sal_uInt16 nUsedSorts
;
264 std::vector
<SCCOLROW
> maOrderIndices
;
269 ScSortInfoArray( sal_uInt16 nSorts
, SCCOLROW nInd1
, SCCOLROW nInd2
) :
271 nCount( nInd2
- nInd1
+ 1 ), nStart( nInd1
),
279 pppInfo
= new ScSortInfo
**[nUsedSorts
];
280 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
282 ScSortInfo
** ppInfo
= new ScSortInfo
* [nCount
];
283 for ( SCSIZE j
= 0; j
< nCount
; j
++ )
284 ppInfo
[j
] = new ScSortInfo
;
285 pppInfo
[nSort
] = ppInfo
;
289 for (size_t i
= 0; i
< nCount
; ++i
)
290 maOrderIndices
.push_back(i
+nStart
);
297 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
299 ScSortInfo
** ppInfo
= pppInfo
[nSort
];
300 for ( SCSIZE j
= 0; j
< nCount
; j
++ )
308 std::for_each(mpRows
->begin(), mpRows
->end(), boost::checked_deleter
<Row
>());
311 void SetKeepQuery( bool b
) { mbKeepQuery
= b
; }
313 bool IsKeepQuery() const { return mbKeepQuery
; }
315 void SetUpdateRefs( bool b
) { mbUpdateRefs
= b
; }
317 bool IsUpdateRefs() const { return mbUpdateRefs
; }
320 * Call this only during normal sorting, not from reordering.
322 ScSortInfo
** GetFirstArray() const
329 * Call this only during normal sorting, not from reordering.
331 ScSortInfo
* Get( sal_uInt16 nSort
, SCCOLROW nInd
)
334 return (pppInfo
[nSort
])[ nInd
- nStart
];
338 * Call this only during normal sorting, not from reordering.
340 void Swap( SCCOLROW nInd1
, SCCOLROW nInd2
)
343 SCSIZE n1
= static_cast<SCSIZE
>(nInd1
- nStart
);
344 SCSIZE n2
= static_cast<SCSIZE
>(nInd2
- nStart
);
345 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
347 ScSortInfo
** ppInfo
= pppInfo
[nSort
];
348 ScSortInfo
* pTmp
= ppInfo
[n1
];
349 ppInfo
[n1
] = ppInfo
[n2
];
353 std::swap(maOrderIndices
[n1
], maOrderIndices
[n2
]);
357 // Swap rows in data table.
358 RowsType
& rRows
= *mpRows
;
359 std::swap(rRows
[n1
], rRows
[n2
]);
363 void SetOrderIndices( const std::vector
<SCCOLROW
>& rIndices
)
365 maOrderIndices
= rIndices
;
369 * @param rIndices indices are actual row positions on the sheet, not an
370 * offset from the top row.
372 void ReorderByRow( const std::vector
<SCCOLROW
>& rIndices
)
377 RowsType
& rRows
= *mpRows
;
379 std::vector
<SCCOLROW
> aOrderIndices2
;
380 aOrderIndices2
.reserve(rIndices
.size());
383 aRows2
.reserve(rRows
.size());
385 std::vector
<SCCOLROW
>::const_iterator it
= rIndices
.begin(), itEnd
= rIndices
.end();
386 for (; it
!= itEnd
; ++it
)
388 size_t nPos
= *it
- nStart
; // switch to an offset to top row.
389 aRows2
.push_back(rRows
[nPos
]);
390 aOrderIndices2
.push_back(maOrderIndices
[nPos
]);
394 maOrderIndices
.swap(aOrderIndices2
);
397 sal_uInt16
GetUsedSorts() const { return nUsedSorts
; }
399 SCCOLROW
GetStart() const { return nStart
; }
400 SCCOLROW
GetLast() const { return mnLastIndex
; }
402 const std::vector
<SCCOLROW
>& GetOrderIndices() const { return maOrderIndices
; }
404 RowsType
& InitDataRows( size_t nRowSize
, size_t nColSize
)
406 mpRows
.reset(new RowsType
);
407 mpRows
->reserve(nRowSize
);
408 for (size_t i
= 0; i
< nRowSize
; ++i
)
409 mpRows
->push_back(new Row(nColSize
));
414 RowsType
* GetDataRows()
423 ScSortInfoArray
& rArray
, ScTable
& rTab
, ScColumn
* pCols
,
424 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
425 bool bPattern
, bool bHiddenFiltered
)
427 // Fill row-wise data table.
428 ScSortInfoArray::RowsType
& rRows
= rArray
.InitDataRows(nRow2
-nRow1
+1, nCol2
-nCol1
+1);
430 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
432 ScColumn
& rCol
= pCols
[nCol
];
434 // Skip reordering of cell formats if the whole span is on the same pattern entry.
435 bool bUniformPattern
= rCol
.GetPatternCount(nRow1
, nRow2
) < 2u;
437 sc::ColumnBlockConstPosition aBlockPos
;
438 rCol
.InitBlockPosition(aBlockPos
);
439 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
441 ScSortInfoArray::Row
& rRow
= *rRows
[nRow
-nRow1
];
442 ScSortInfoArray::Cell
& rCell
= rRow
.maCells
[nCol
-nCol1
];
444 rCell
.maCell
= rCol
.GetCellValue(aBlockPos
, nRow
);
445 rCell
.mpAttr
= rCol
.GetCellTextAttr(aBlockPos
, nRow
);
446 rCell
.mpBroadcaster
= rCol
.GetBroadcaster(aBlockPos
, nRow
);
447 rCell
.mpNote
= rCol
.GetCellNote(aBlockPos
, nRow
);
449 if (!bUniformPattern
&& bPattern
)
450 rCell
.mpPattern
= rCol
.GetPattern(nRow
);
456 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
458 ScSortInfoArray::Row
& rRow
= *rRows
[nRow
-nRow1
];
459 rRow
.mbHidden
= rTab
.RowHidden(nRow
);
460 rRow
.mbFiltered
= rTab
.RowFiltered(nRow
);
467 ScSortInfoArray
* ScTable::CreateSortInfoArray( const sc::ReorderParam
& rParam
)
469 ScSortInfoArray
* pArray
= NULL
;
473 // Create a sort info array with just the data table.
474 SCROW nRow1
= rParam
.maSortRange
.aStart
.Row();
475 SCROW nRow2
= rParam
.maSortRange
.aEnd
.Row();
476 SCCOL nCol1
= rParam
.maSortRange
.aStart
.Col();
477 SCCOL nCol2
= rParam
.maSortRange
.aEnd
.Col();
479 pArray
= new ScSortInfoArray(0, nRow1
, nRow2
);
480 pArray
->SetKeepQuery(rParam
.mbHiddenFiltered
);
481 pArray
->SetUpdateRefs(rParam
.mbUpdateRefs
);
484 *pArray
, *this, aCol
, nCol1
, nRow1
, nCol2
, nRow2
,
485 rParam
.mbPattern
, rParam
.mbHiddenFiltered
);
489 SCCOLROW nCol1
= rParam
.maSortRange
.aStart
.Col();
490 SCCOLROW nCol2
= rParam
.maSortRange
.aEnd
.Col();
492 pArray
= new ScSortInfoArray(0, nCol1
, nCol2
);
493 pArray
->SetKeepQuery(rParam
.mbHiddenFiltered
);
494 pArray
->SetUpdateRefs(rParam
.mbUpdateRefs
);
500 ScSortInfoArray
* ScTable::CreateSortInfoArray(
501 const ScSortParam
& rSortParam
, SCCOLROW nInd1
, SCCOLROW nInd2
,
502 bool bKeepQuery
, bool bUpdateRefs
)
504 sal_uInt16 nUsedSorts
= 1;
505 while ( nUsedSorts
< rSortParam
.GetSortKeyCount() && rSortParam
.maKeyState
[nUsedSorts
].bDoSort
)
507 ScSortInfoArray
* pArray
= new ScSortInfoArray( nUsedSorts
, nInd1
, nInd2
);
508 pArray
->SetKeepQuery(bKeepQuery
);
509 pArray
->SetUpdateRefs(bUpdateRefs
);
511 if ( rSortParam
.bByRow
)
513 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
515 SCCOL nCol
= static_cast<SCCOL
>(rSortParam
.maKeyState
[nSort
].nField
);
516 ScColumn
* pCol
= &aCol
[nCol
];
517 sc::ColumnBlockConstPosition aBlockPos
;
518 pCol
->InitBlockPosition(aBlockPos
);
519 for ( SCROW nRow
= nInd1
; nRow
<= nInd2
; nRow
++ )
521 ScSortInfo
* pInfo
= pArray
->Get( nSort
, nRow
);
522 pInfo
->maCell
= pCol
->GetCellValue(aBlockPos
, nRow
);
528 *pArray
, *this, aCol
, rSortParam
.nCol1
, nInd1
, rSortParam
.nCol2
, nInd2
,
529 rSortParam
.bIncludePattern
, bKeepQuery
);
533 for ( sal_uInt16 nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
535 SCROW nRow
= rSortParam
.maKeyState
[nSort
].nField
;
536 for ( SCCOL nCol
= static_cast<SCCOL
>(nInd1
);
537 nCol
<= static_cast<SCCOL
>(nInd2
); nCol
++ )
539 ScSortInfo
* pInfo
= pArray
->Get( nSort
, nCol
);
540 pInfo
->maCell
= GetCellValue(nCol
, nRow
);
550 struct SortedColumn
: boost::noncopyable
552 typedef mdds::flat_segment_tree
<SCROW
, const ScPatternAttr
*> PatRangeType
;
554 sc::CellStoreType maCells
;
555 sc::CellTextAttrStoreType maCellTextAttrs
;
556 sc::BroadcasterStoreType maBroadcasters
;
557 sc::CellNoteStoreType maCellNotes
;
559 PatRangeType maPatterns
;
560 PatRangeType::const_iterator miPatternPos
;
562 SortedColumn( size_t nTopEmptyRows
) :
563 maCells(nTopEmptyRows
),
564 maCellTextAttrs(nTopEmptyRows
),
565 maBroadcasters(nTopEmptyRows
),
566 maCellNotes(nTopEmptyRows
),
567 maPatterns(0, MAXROWCOUNT
, NULL
),
568 miPatternPos(maPatterns
.begin()) {}
570 void setPattern( SCROW nRow
, const ScPatternAttr
* pPat
)
572 miPatternPos
= maPatterns
.insert(miPatternPos
, nRow
, nRow
+1, pPat
).first
;
576 struct SortedRowFlags
578 typedef mdds::flat_segment_tree
<SCROW
,bool> FlagsType
;
580 FlagsType maRowsHidden
;
581 FlagsType maRowsFiltered
;
582 FlagsType::const_iterator miPosHidden
;
583 FlagsType::const_iterator miPosFiltered
;
586 maRowsHidden(0, MAXROWCOUNT
, false),
587 maRowsFiltered(0, MAXROWCOUNT
, false),
588 miPosHidden(maRowsHidden
.begin()),
589 miPosFiltered(maRowsFiltered
.begin()) {}
591 void setRowHidden( SCROW nRow
, bool b
)
593 miPosHidden
= maRowsHidden
.insert(miPosHidden
, nRow
, nRow
+1, b
).first
;
596 void setRowFiltered( SCROW nRow
, bool b
)
598 miPosFiltered
= maRowsFiltered
.insert(miPosFiltered
, nRow
, nRow
+1, b
).first
;
601 void swap( SortedRowFlags
& r
)
603 maRowsHidden
.swap(r
.maRowsHidden
);
604 maRowsFiltered
.swap(r
.maRowsFiltered
);
606 // Just reset the position hints.
607 miPosHidden
= maRowsHidden
.begin();
608 miPosFiltered
= maRowsFiltered
.begin();
616 const ScPatternAttr
* mpPattern
;
618 PatternSpan( SCROW nRow1
, SCROW nRow2
, const ScPatternAttr
* pPat
) :
619 mnRow1(nRow1
), mnRow2(nRow2
), mpPattern(pPat
) {}
624 bool ScTable::IsSortCollatorGlobal() const
626 return pSortCollator
== ScGlobal::GetCollator() ||
627 pSortCollator
== ScGlobal::GetCaseCollator();
630 void ScTable::InitSortCollator( const ScSortParam
& rPar
)
632 if ( !rPar
.aCollatorLocale
.Language
.isEmpty() )
634 if ( !pSortCollator
|| IsSortCollatorGlobal() )
635 pSortCollator
= new CollatorWrapper( comphelper::getProcessComponentContext() );
636 pSortCollator
->loadCollatorAlgorithm( rPar
.aCollatorAlgorithm
,
637 rPar
.aCollatorLocale
, (rPar
.bCaseSens
? 0 : SC_COLLATOR_IGNORES
) );
641 DestroySortCollator();
642 pSortCollator
= (rPar
.bCaseSens
? ScGlobal::GetCaseCollator() :
643 ScGlobal::GetCollator());
647 void ScTable::DestroySortCollator()
651 if ( !IsSortCollatorGlobal() )
652 delete pSortCollator
;
653 pSortCollator
= NULL
;
659 template<typename _Hint
, typename _ReorderMap
, typename _Index
>
660 class ReorderNotifier
: std::unary_function
<SvtListener
*, void>
664 ReorderNotifier( const _ReorderMap
& rMap
, SCTAB nTab
, _Index nPos1
, _Index nPos2
) :
665 maHint(rMap
, nTab
, nPos1
, nPos2
) {}
667 void operator() ( SvtListener
* p
)
673 typedef ReorderNotifier
<sc::RefColReorderHint
, sc::ColRowReorderMapType
, SCCOL
> ColReorderNotifier
;
674 typedef ReorderNotifier
<sc::RefRowReorderHint
, sc::ColRowReorderMapType
, SCROW
> RowReorderNotifier
;
676 class StartListeningNotifier
: std::unary_function
<SvtListener
*, void>
678 sc::RefStartListeningHint maHint
;
680 StartListeningNotifier() {}
682 void operator() ( SvtListener
* p
)
688 class StopListeningNotifier
: std::unary_function
<SvtListener
*, void>
690 sc::RefStopListeningHint maHint
;
692 StopListeningNotifier() {}
694 void operator() ( SvtListener
* p
)
700 class FormulaGroupPosCollector
: std::unary_function
<SvtListener
*, void>
702 sc::RefQueryFormulaGroup
& mrQuery
;
705 FormulaGroupPosCollector( sc::RefQueryFormulaGroup
& rQuery
) : mrQuery(rQuery
) {}
707 void operator() ( SvtListener
* p
)
713 void fillSortedColumnArray(
714 boost::ptr_vector
<SortedColumn
>& rSortedCols
,
715 SortedRowFlags
& rRowFlags
,
716 std::vector
<SvtListener
*>& rCellListeners
,
717 ScSortInfoArray
* pArray
, SCTAB nTab
, SCCOL nCol1
, SCCOL nCol2
, ScProgress
* pProgress
)
719 SCROW nRow1
= pArray
->GetStart();
720 ScSortInfoArray::RowsType
* pRows
= pArray
->GetDataRows();
722 size_t nColCount
= nCol2
- nCol1
+ 1;
723 boost::ptr_vector
<SortedColumn
> aSortedCols
; // storage for copied cells.
724 SortedRowFlags aRowFlags
;
725 aSortedCols
.reserve(nColCount
);
726 for (size_t i
= 0; i
< nColCount
; ++i
)
728 // In the sorted column container, element positions and row
729 // positions must match, else formula cells may mis-behave during
731 aSortedCols
.push_back(new SortedColumn(nRow1
));
734 for (size_t i
= 0; i
< pRows
->size(); ++i
)
736 ScSortInfoArray::Row
* pRow
= (*pRows
)[i
];
737 for (size_t j
= 0; j
< pRow
->maCells
.size(); ++j
)
739 ScAddress
aCellPos(nCol1
+ j
, nRow1
+ i
, nTab
);
741 ScSortInfoArray::Cell
& rCell
= pRow
->maCells
[j
];
743 sc::CellStoreType
& rCellStore
= aSortedCols
.at(j
).maCells
;
744 switch (rCell
.maCell
.meType
)
746 case CELLTYPE_STRING
:
747 assert(rCell
.mpAttr
);
748 rCellStore
.push_back(*rCell
.maCell
.mpString
);
751 assert(rCell
.mpAttr
);
752 rCellStore
.push_back(rCell
.maCell
.mfValue
);
755 assert(rCell
.mpAttr
);
756 rCellStore
.push_back(rCell
.maCell
.mpEditText
->Clone());
758 case CELLTYPE_FORMULA
:
760 assert(rCell
.mpAttr
);
761 ScAddress aOldPos
= rCell
.maCell
.mpFormula
->aPos
;
763 ScFormulaCell
* pNew
= rCell
.maCell
.mpFormula
->Clone( aCellPos
, SC_CLONECELL_DEFAULT
);
764 if (pArray
->IsUpdateRefs())
766 pNew
->CopyAllBroadcasters(*rCell
.maCell
.mpFormula
);
767 pNew
->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos
, aCellPos
);
771 pNew
->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos
, aCellPos
);
774 if (!rCellListeners
.empty())
776 // Original source cells will be deleted during
777 // sc::CellStoreType::transfer(), SvtListener is a base
778 // class, so we need to replace it.
779 auto it( ::std::find( rCellListeners
.begin(), rCellListeners
.end(), rCell
.maCell
.mpFormula
));
780 if (it
!= rCellListeners
.end())
784 rCellStore
.push_back(pNew
);
788 assert(!rCell
.mpAttr
);
789 rCellStore
.push_back_empty();
792 sc::CellTextAttrStoreType
& rAttrStore
= aSortedCols
.at(j
).maCellTextAttrs
;
794 rAttrStore
.push_back(*rCell
.mpAttr
);
796 rAttrStore
.push_back_empty();
798 if (pArray
->IsUpdateRefs())
800 // At this point each broadcaster instance is managed by 2
801 // containers. We will release those in the original storage
802 // below before transferring them to the document.
803 sc::BroadcasterStoreType
& rBCStore
= aSortedCols
.at(j
).maBroadcasters
;
804 if (rCell
.mpBroadcaster
)
805 // A const pointer would be implicitly converted to a bool type.
806 rBCStore
.push_back(const_cast<SvtBroadcaster
*>(rCell
.mpBroadcaster
));
808 rBCStore
.push_back_empty();
811 // The same with cell note instances ...
812 sc::CellNoteStoreType
& rNoteStore
= aSortedCols
.at(j
).maCellNotes
;
814 rNoteStore
.push_back(const_cast<ScPostIt
*>(rCell
.mpNote
));
816 rNoteStore
.push_back_empty();
819 aSortedCols
.at(j
).setPattern(aCellPos
.Row(), rCell
.mpPattern
);
822 if (pArray
->IsKeepQuery())
824 // Hidden and filtered flags are first converted to segments.
825 SCROW nRow
= nRow1
+ i
;
826 aRowFlags
.setRowHidden(nRow
, pRow
->mbHidden
);
827 aRowFlags
.setRowFiltered(nRow
, pRow
->mbFiltered
);
831 pProgress
->SetStateOnPercent(i
);
834 rSortedCols
.swap(aSortedCols
);
835 rRowFlags
.swap(aRowFlags
);
838 void expandRowRange( ScRange
& rRange
, SCROW nTop
, SCROW nBottom
)
840 if (nTop
< rRange
.aStart
.Row())
841 rRange
.aStart
.SetRow(nTop
);
843 if (rRange
.aEnd
.Row() < nBottom
)
844 rRange
.aEnd
.SetRow(nBottom
);
847 class FormulaCellCollectAction
: public sc::ColumnSpanSet::ColumnAction
849 std::vector
<ScFormulaCell
*>& mrCells
;
853 FormulaCellCollectAction( std::vector
<ScFormulaCell
*>& rCells
) :
854 mrCells(rCells
), mpCol(NULL
) {}
856 virtual void startColumn( ScColumn
* pCol
) SAL_OVERRIDE
861 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) SAL_OVERRIDE
868 mpCol
->CollectFormulaCells(mrCells
, nRow1
, nRow2
);
872 class ListenerStartAction
: public sc::ColumnSpanSet::ColumnAction
876 boost::shared_ptr
<sc::ColumnBlockPositionSet
> mpPosSet
;
877 sc::StartListeningContext maStartCxt
;
878 sc::EndListeningContext maEndCxt
;
881 ListenerStartAction( ScDocument
& rDoc
) :
883 mpPosSet(new sc::ColumnBlockPositionSet(rDoc
)),
884 maStartCxt(rDoc
, mpPosSet
),
885 maEndCxt(rDoc
, mpPosSet
) {}
887 virtual void startColumn( ScColumn
* pCol
) SAL_OVERRIDE
892 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) SAL_OVERRIDE
899 mpCol
->StartListeningFormulaCells(maStartCxt
, maEndCxt
, nRow1
, nRow2
);
905 void ScTable::SortReorderByColumn(
906 ScSortInfoArray
* pArray
, SCROW nRow1
, SCROW nRow2
, bool bPattern
, ScProgress
* pProgress
)
908 SCCOLROW nStart
= pArray
->GetStart();
909 SCCOLROW nLast
= pArray
->GetLast();
911 std::vector
<SCCOLROW
> aIndices
= pArray
->GetOrderIndices();
912 size_t nCount
= aIndices
.size();
914 // Cut formula grouping at row and reference boundaries before the reordering.
915 ScRange
aSortRange(nStart
, nRow1
, nTab
, nLast
, nRow2
, nTab
);
916 for (SCCOL nCol
= nStart
; nCol
<= nLast
; ++nCol
)
917 aCol
[nCol
].SplitFormulaGroupByRelativeRef(aSortRange
);
919 // Collect all listeners of cell broadcasters of sorted range.
920 std::vector
<SvtListener
*> aCellListeners
;
922 if (!pArray
->IsUpdateRefs())
924 // Collect listeners of cell broadcasters.
925 for (SCCOL nCol
= nStart
; nCol
<= nLast
; ++nCol
)
926 aCol
[nCol
].CollectListeners(aCellListeners
, nRow1
, nRow2
);
928 // Remove any duplicate listener entries. We must ensure that we
929 // notify each unique listener only once.
930 std::sort(aCellListeners
.begin(), aCellListeners
.end());
931 aCellListeners
.erase(std::unique(aCellListeners
.begin(), aCellListeners
.end()), aCellListeners
.end());
933 // Notify the cells' listeners to stop listening.
934 /* TODO: for performance this could be enhanced to stop and later
935 * restart only listening to within the reordered range and keep
936 * listening to everything outside untouched. */
937 StopListeningNotifier aFunc
;
938 std::for_each(aCellListeners
.begin(), aCellListeners
.end(), aFunc
);
941 // table to keep track of column index to position in the index table.
942 std::vector
<SCCOLROW
> aPosTable(nCount
);
943 for (size_t i
= 0; i
< nCount
; ++i
)
944 aPosTable
[aIndices
[i
]-nStart
] = i
;
946 SCCOLROW nDest
= nStart
;
947 for (size_t i
= 0; i
< nCount
; ++i
, ++nDest
)
949 SCCOLROW nSrc
= aIndices
[i
];
952 aCol
[nDest
].Swap(aCol
[nSrc
], nRow1
, nRow2
, bPattern
);
954 // Update the position of the index that was originally equal to nDest.
955 size_t nPos
= aPosTable
[nDest
-nStart
];
956 aIndices
[nPos
] = nSrc
;
957 aPosTable
[nSrc
-nStart
] = nPos
;
961 pProgress
->SetStateOnPercent(i
);
964 // Reset formula cell positions which became out-of-sync after column reordering.
965 bool bUpdateRefs
= pArray
->IsUpdateRefs();
966 for (SCCOL nCol
= nStart
; nCol
<= nLast
; ++nCol
)
967 aCol
[nCol
].ResetFormulaCellPositions(nRow1
, nRow2
, bUpdateRefs
);
969 if (pArray
->IsUpdateRefs())
971 // Set up column reorder map (for later broadcasting of reference updates).
972 sc::ColRowReorderMapType aColMap
;
973 const std::vector
<SCCOLROW
>& rOldIndices
= pArray
->GetOrderIndices();
974 for (size_t i
= 0, n
= rOldIndices
.size(); i
< n
; ++i
)
976 SCCOL nNew
= i
+ nStart
;
977 SCCOL nOld
= rOldIndices
[i
];
978 aColMap
.insert(sc::ColRowReorderMapType::value_type(nOld
, nNew
));
981 // Collect all listeners within sorted range ahead of time.
982 std::vector
<SvtListener
*> aListeners
;
984 for (SCCOL nCol
= nStart
; nCol
<= nLast
; ++nCol
)
985 aCol
[nCol
].CollectListeners(aListeners
, nRow1
, nRow2
);
987 // Get all area listeners that listen on one column within the range
988 // and end their listening.
989 ScRange
aMoveRange( nStart
, nRow1
, nTab
, nLast
, nRow2
, nTab
);
990 std::vector
<sc::AreaListener
> aAreaListeners
= pDocument
->GetBASM()->GetAllListeners(
991 aMoveRange
, sc::OneColumnInsideArea
);
993 std::vector
<sc::AreaListener
>::iterator it
= aAreaListeners
.begin(), itEnd
= aAreaListeners
.end();
994 for (; it
!= itEnd
; ++it
)
996 pDocument
->EndListeningArea(it
->maArea
, it
->mbGroupListening
, it
->mpListener
);
997 aListeners
.push_back( it
->mpListener
);
1001 // Remove any duplicate listener entries and notify all listeners
1002 // afterward. We must ensure that we notify each unique listener only
1004 std::sort(aListeners
.begin(), aListeners
.end());
1005 aListeners
.erase(std::unique(aListeners
.begin(), aListeners
.end()), aListeners
.end());
1006 ColReorderNotifier
aFunc(aColMap
, nTab
, nRow1
, nRow2
);
1007 std::for_each(aListeners
.begin(), aListeners
.end(), aFunc
);
1009 // Re-start area listeners on the reordered columns.
1011 std::vector
<sc::AreaListener
>::iterator it
= aAreaListeners
.begin(), itEnd
= aAreaListeners
.end();
1012 for (; it
!= itEnd
; ++it
)
1014 ScRange aNewRange
= it
->maArea
;
1015 sc::ColRowReorderMapType::const_iterator itCol
= aColMap
.find( aNewRange
.aStart
.Col());
1016 if (itCol
!= aColMap
.end())
1018 aNewRange
.aStart
.SetCol( itCol
->second
);
1019 aNewRange
.aEnd
.SetCol( itCol
->second
);
1021 pDocument
->StartListeningArea(aNewRange
, it
->mbGroupListening
, it
->mpListener
);
1025 else // !(pArray->IsUpdateRefs())
1027 // Notify the cells' listeners to (re-)start listening.
1028 StartListeningNotifier aFunc
;
1029 std::for_each(aCellListeners
.begin(), aCellListeners
.end(), aFunc
);
1032 // Re-join formulas at row boundaries now that all the references have
1033 // been adjusted for column reordering.
1034 for (SCCOL nCol
= nStart
; nCol
<= nLast
; ++nCol
)
1036 sc::CellStoreType
& rCells
= aCol
[nCol
].maCells
;
1037 sc::CellStoreType::position_type aPos
= rCells
.position(nRow1
);
1038 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos
);
1041 aPos
= rCells
.position(aPos
.first
, nRow2
+1);
1042 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos
);
1047 void ScTable::SortReorderByRow(
1048 ScSortInfoArray
* pArray
, SCCOL nCol1
, SCCOL nCol2
, ScProgress
* pProgress
)
1050 assert(!pArray
->IsUpdateRefs());
1055 SCROW nRow1
= pArray
->GetStart();
1056 SCROW nRow2
= pArray
->GetLast();
1058 // Collect all listeners of cell broadcasters of sorted range.
1059 std::vector
<SvtListener
*> aCellListeners
;
1061 // When the update ref mode is disabled, we need to detach all formula
1062 // cells in the sorted range before reordering, and re-start them
1065 sc::EndListeningContext
aCxt(*pDocument
);
1066 DetachFormulaCells(aCxt
, nCol1
, nRow1
, nCol2
, nRow2
);
1069 // Collect listeners of cell broadcasters.
1070 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1071 aCol
[nCol
].CollectListeners(aCellListeners
, nRow1
, nRow2
);
1073 // Remove any duplicate listener entries. We must ensure that we notify
1074 // each unique listener only once.
1075 std::sort(aCellListeners
.begin(), aCellListeners
.end());
1076 aCellListeners
.erase(std::unique(aCellListeners
.begin(), aCellListeners
.end()), aCellListeners
.end());
1078 // Notify the cells' listeners to stop listening.
1079 /* TODO: for performance this could be enhanced to stop and later
1080 * restart only listening to within the reordered range and keep
1081 * listening to everything outside untouched. */
1083 StopListeningNotifier aFunc
;
1084 std::for_each(aCellListeners
.begin(), aCellListeners
.end(), aFunc
);
1087 // Split formula groups at the sort range boundaries (if applicable).
1088 std::vector
<SCROW
> aRowBounds
;
1089 aRowBounds
.reserve(2);
1090 aRowBounds
.push_back(nRow1
);
1091 aRowBounds
.push_back(nRow2
+1);
1092 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1093 SplitFormulaGroups(nCol
, aRowBounds
);
1095 // Cells in the data rows only reference values in the document. Make
1096 // a copy before updating the document.
1097 boost::ptr_vector
<SortedColumn
> aSortedCols
; // storage for copied cells.
1098 SortedRowFlags aRowFlags
;
1099 fillSortedColumnArray(aSortedCols
, aRowFlags
, aCellListeners
, pArray
, nTab
, nCol1
, nCol2
, pProgress
);
1101 for (size_t i
= 0, n
= aSortedCols
.size(); i
< n
; ++i
)
1103 SCCOL nThisCol
= i
+ nCol1
;
1106 sc::CellStoreType
& rDest
= aCol
[nThisCol
].maCells
;
1107 sc::CellStoreType
& rSrc
= aSortedCols
[i
].maCells
;
1108 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1112 sc::CellTextAttrStoreType
& rDest
= aCol
[nThisCol
].maCellTextAttrs
;
1113 sc::CellTextAttrStoreType
& rSrc
= aSortedCols
[i
].maCellTextAttrs
;
1114 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1118 sc::CellNoteStoreType
& rSrc
= aSortedCols
[i
].maCellNotes
;
1119 sc::CellNoteStoreType
& rDest
= aCol
[nThisCol
].maCellNotes
;
1121 // Do the same as broadcaster storage transfer (to prevent double deletion).
1122 rDest
.release_range(nRow1
, nRow2
);
1123 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1124 aCol
[nThisCol
].UpdateNoteCaptions(nRow1
, nRow2
);
1128 // Get all row spans where the pattern is not NULL.
1129 std::vector
<PatternSpan
> aSpans
=
1130 sc::toSpanArrayWithValue
<SCROW
,const ScPatternAttr
*,PatternSpan
>(
1131 aSortedCols
[i
].maPatterns
);
1133 std::vector
<PatternSpan
>::iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
1134 for (; it
!= itEnd
; ++it
)
1136 assert(it
->mpPattern
); // should never be NULL.
1137 pDocument
->GetPool()->Put(*it
->mpPattern
);
1140 for (it
= aSpans
.begin(); it
!= itEnd
; ++it
)
1142 aCol
[nThisCol
].SetPatternArea(it
->mnRow1
, it
->mnRow2
, *it
->mpPattern
, true);
1143 pDocument
->GetPool()->Remove(*it
->mpPattern
);
1147 aCol
[nThisCol
].CellStorageModified();
1150 if (pArray
->IsKeepQuery())
1152 aRowFlags
.maRowsHidden
.build_tree();
1153 aRowFlags
.maRowsFiltered
.build_tree();
1155 // Remove all flags in the range first.
1156 SetRowHidden(nRow1
, nRow2
, false);
1157 SetRowFiltered(nRow1
, nRow2
, false);
1159 std::vector
<sc::RowSpan
> aSpans
=
1160 sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsHidden
, nRow1
);
1162 std::vector
<sc::RowSpan
>::const_iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
1163 for (; it
!= itEnd
; ++it
)
1164 SetRowHidden(it
->mnRow1
, it
->mnRow2
, true);
1166 aSpans
= sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsFiltered
, nRow1
);
1168 it
= aSpans
.begin(), itEnd
= aSpans
.end();
1169 for (; it
!= itEnd
; ++it
)
1170 SetRowFiltered(it
->mnRow1
, it
->mnRow2
, true);
1173 // Notify the cells' listeners to (re-)start listening.
1175 StartListeningNotifier aFunc
;
1176 std::for_each(aCellListeners
.begin(), aCellListeners
.end(), aFunc
);
1179 // Re-group columns in the sorted range too.
1180 for (SCCOL i
= nCol1
; i
<= nCol2
; ++i
)
1181 aCol
[i
].RegroupFormulaCells();
1184 sc::StartListeningContext
aCxt(*pDocument
);
1185 AttachFormulaCells(aCxt
, nCol1
, nRow1
, nCol2
, nRow2
);
1189 void ScTable::SortReorderByRowRefUpdate(
1190 ScSortInfoArray
* pArray
, SCCOL nCol1
, SCCOL nCol2
, ScProgress
* pProgress
)
1192 assert(pArray
->IsUpdateRefs());
1197 SCROW nRow1
= pArray
->GetStart();
1198 SCROW nRow2
= pArray
->GetLast();
1200 ScRange
aMoveRange( nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
);
1201 sc::ColumnSpanSet
aGrpListenerRanges(false);
1204 // Get the range of formula group listeners within sorted range (if any).
1205 sc::QueryRange aQuery
;
1207 ScBroadcastAreaSlotMachine
* pBASM
= pDocument
->GetBASM();
1208 std::vector
<sc::AreaListener
> aGrpListeners
=
1209 pBASM
->GetAllListeners(
1210 aMoveRange
, sc::AreaInsideOrOverlap
, sc::ListenerGroup
);
1213 std::vector
<sc::AreaListener
>::iterator it
= aGrpListeners
.begin(), itEnd
= aGrpListeners
.end();
1214 for (; it
!= itEnd
; ++it
)
1216 assert(it
->mbGroupListening
);
1217 SvtListener
* pGrpLis
= it
->mpListener
;
1218 pGrpLis
->Query(aQuery
);
1219 pDocument
->EndListeningArea(it
->maArea
, it
->mbGroupListening
, pGrpLis
);
1224 aQuery
.swapRanges(aTmp
);
1226 // If the range is within the sorted range, we need to expand its rows
1227 // to the top and bottom of the sorted range, since the formula cells
1228 // could be anywhere in the sorted range after reordering.
1229 for (size_t i
= 0, n
= aTmp
.size(); i
< n
; ++i
)
1231 ScRange aRange
= *aTmp
[i
];
1232 if (!aMoveRange
.Intersects(aRange
))
1234 // Doesn't overlap with the sorted range at all.
1235 aGrpListenerRanges
.set(aRange
, true);
1239 if (aMoveRange
.aStart
.Col() <= aRange
.aStart
.Col() && aRange
.aEnd
.Col() <= aMoveRange
.aEnd
.Col())
1241 // Its column range is within the column range of the sorted range.
1242 expandRowRange(aRange
, aMoveRange
.aStart
.Row(), aMoveRange
.aEnd
.Row());
1243 aGrpListenerRanges
.set(aRange
, true);
1247 // It intersects with the sorted range, but its column range is
1248 // not within the column range of the sorted range. Split it into
1250 ScRange aR1
= aRange
;
1251 ScRange aR2
= aRange
;
1252 if (aRange
.aStart
.Col() < aMoveRange
.aStart
.Col())
1254 // Left half is outside the sorted range while the right half is inside.
1255 aR1
.aEnd
.SetCol(aMoveRange
.aStart
.Col()-1);
1256 aR2
.aStart
.SetCol(aMoveRange
.aStart
.Col());
1257 expandRowRange(aR2
, aMoveRange
.aStart
.Row(), aMoveRange
.aEnd
.Row());
1261 // Left half is inside the sorted range while the right half is outside.
1262 aR1
.aEnd
.SetCol(aMoveRange
.aEnd
.Col()-1);
1263 aR2
.aStart
.SetCol(aMoveRange
.aEnd
.Col());
1264 expandRowRange(aR1
, aMoveRange
.aStart
.Row(), aMoveRange
.aEnd
.Row());
1267 aGrpListenerRanges
.set(aR1
, true);
1268 aGrpListenerRanges
.set(aR2
, true);
1272 // Split formula groups at the sort range boundaries (if applicable).
1273 std::vector
<SCROW
> aRowBounds
;
1274 aRowBounds
.reserve(2);
1275 aRowBounds
.push_back(nRow1
);
1276 aRowBounds
.push_back(nRow2
+1);
1277 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1278 SplitFormulaGroups(nCol
, aRowBounds
);
1280 // Cells in the data rows only reference values in the document. Make
1281 // a copy before updating the document.
1282 boost::ptr_vector
<SortedColumn
> aSortedCols
; // storage for copied cells.
1283 SortedRowFlags aRowFlags
;
1284 std::vector
<SvtListener
*> aListenersDummy
;
1285 fillSortedColumnArray(aSortedCols
, aRowFlags
, aListenersDummy
, pArray
, nTab
, nCol1
, nCol2
, pProgress
);
1287 for (size_t i
= 0, n
= aSortedCols
.size(); i
< n
; ++i
)
1289 SCCOL nThisCol
= i
+ nCol1
;
1292 sc::CellStoreType
& rDest
= aCol
[nThisCol
].maCells
;
1293 sc::CellStoreType
& rSrc
= aSortedCols
[i
].maCells
;
1294 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1298 sc::CellTextAttrStoreType
& rDest
= aCol
[nThisCol
].maCellTextAttrs
;
1299 sc::CellTextAttrStoreType
& rSrc
= aSortedCols
[i
].maCellTextAttrs
;
1300 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1304 sc::BroadcasterStoreType
& rSrc
= aSortedCols
[i
].maBroadcasters
;
1305 sc::BroadcasterStoreType
& rDest
= aCol
[nThisCol
].maBroadcasters
;
1307 // Release current broadcasters first, to prevent them from getting deleted.
1308 rDest
.release_range(nRow1
, nRow2
);
1310 // Transfer sorted broadcaster segment to the document.
1311 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1315 sc::CellNoteStoreType
& rSrc
= aSortedCols
[i
].maCellNotes
;
1316 sc::CellNoteStoreType
& rDest
= aCol
[nThisCol
].maCellNotes
;
1318 // Do the same as broadcaster storage transfer (to prevent double deletion).
1319 rDest
.release_range(nRow1
, nRow2
);
1320 rSrc
.transfer(nRow1
, nRow2
, rDest
, nRow1
);
1321 aCol
[nThisCol
].UpdateNoteCaptions(nRow1
, nRow2
);
1325 // Get all row spans where the pattern is not NULL.
1326 std::vector
<PatternSpan
> aSpans
=
1327 sc::toSpanArrayWithValue
<SCROW
,const ScPatternAttr
*,PatternSpan
>(
1328 aSortedCols
[i
].maPatterns
);
1330 std::vector
<PatternSpan
>::iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
1331 for (; it
!= itEnd
; ++it
)
1333 assert(it
->mpPattern
); // should never be NULL.
1334 pDocument
->GetPool()->Put(*it
->mpPattern
);
1337 for (it
= aSpans
.begin(); it
!= itEnd
; ++it
)
1339 aCol
[nThisCol
].SetPatternArea(it
->mnRow1
, it
->mnRow2
, *it
->mpPattern
, true);
1340 pDocument
->GetPool()->Remove(*it
->mpPattern
);
1344 aCol
[nThisCol
].CellStorageModified();
1347 if (pArray
->IsKeepQuery())
1349 aRowFlags
.maRowsHidden
.build_tree();
1350 aRowFlags
.maRowsFiltered
.build_tree();
1352 // Remove all flags in the range first.
1353 SetRowHidden(nRow1
, nRow2
, false);
1354 SetRowFiltered(nRow1
, nRow2
, false);
1356 std::vector
<sc::RowSpan
> aSpans
=
1357 sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsHidden
, nRow1
);
1359 std::vector
<sc::RowSpan
>::const_iterator it
= aSpans
.begin(), itEnd
= aSpans
.end();
1360 for (; it
!= itEnd
; ++it
)
1361 SetRowHidden(it
->mnRow1
, it
->mnRow2
, true);
1363 aSpans
= sc::toSpanArray
<SCROW
,sc::RowSpan
>(aRowFlags
.maRowsFiltered
, nRow1
);
1365 it
= aSpans
.begin(), itEnd
= aSpans
.end();
1366 for (; it
!= itEnd
; ++it
)
1367 SetRowFiltered(it
->mnRow1
, it
->mnRow2
, true);
1370 // Set up row reorder map (for later broadcasting of reference updates).
1371 sc::ColRowReorderMapType aRowMap
;
1372 const std::vector
<SCCOLROW
>& rOldIndices
= pArray
->GetOrderIndices();
1373 for (size_t i
= 0, n
= rOldIndices
.size(); i
< n
; ++i
)
1375 SCROW nNew
= i
+ nRow1
;
1376 SCROW nOld
= rOldIndices
[i
];
1377 aRowMap
.insert(sc::ColRowReorderMapType::value_type(nOld
, nNew
));
1380 // Collect all listeners within sorted range ahead of time.
1381 std::vector
<SvtListener
*> aListeners
;
1383 // Collect listeners of cell broadcasters.
1384 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
1385 aCol
[nCol
].CollectListeners(aListeners
, nRow1
, nRow2
);
1387 // Get all area listeners that listen on one row within the range and end
1389 std::vector
<sc::AreaListener
> aAreaListeners
= pDocument
->GetBASM()->GetAllListeners(
1390 aMoveRange
, sc::OneRowInsideArea
);
1392 std::vector
<sc::AreaListener
>::iterator it
= aAreaListeners
.begin(), itEnd
= aAreaListeners
.end();
1393 for (; it
!= itEnd
; ++it
)
1395 pDocument
->EndListeningArea(it
->maArea
, it
->mbGroupListening
, it
->mpListener
);
1396 aListeners
.push_back( it
->mpListener
);
1401 // Get all formula cells from the former group area listener ranges.
1403 std::vector
<ScFormulaCell
*> aFCells
;
1404 FormulaCellCollectAction
aAction(aFCells
);
1405 aGrpListenerRanges
.executeColumnAction(*pDocument
, aAction
);
1407 std::copy(aFCells
.begin(), aFCells
.end(), std::back_inserter(aListeners
));
1410 // Remove any duplicate listener entries. We must ensure that we notify
1411 // each unique listener only once.
1412 std::sort(aListeners
.begin(), aListeners
.end());
1413 aListeners
.erase(std::unique(aListeners
.begin(), aListeners
.end()), aListeners
.end());
1415 // Collect positions of all shared formula cells outside the sorted range,
1416 // and make them unshared before notifying them.
1417 sc::RefQueryFormulaGroup aFormulaGroupPos
;
1418 aFormulaGroupPos
.setSkipRange(ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
));
1420 std::for_each(aListeners
.begin(), aListeners
.end(), FormulaGroupPosCollector(aFormulaGroupPos
));
1421 const sc::RefQueryFormulaGroup::TabsType
& rGroupTabs
= aFormulaGroupPos
.getAllPositions();
1422 sc::RefQueryFormulaGroup::TabsType::const_iterator itGroupTab
= rGroupTabs
.begin(), itGroupTabEnd
= rGroupTabs
.end();
1423 for (; itGroupTab
!= itGroupTabEnd
; ++itGroupTab
)
1425 const sc::RefQueryFormulaGroup::ColsType
& rCols
= itGroupTab
->second
;
1426 sc::RefQueryFormulaGroup::ColsType::const_iterator itCol
= rCols
.begin(), itColEnd
= rCols
.end();
1427 for (; itCol
!= itColEnd
; ++itCol
)
1429 const sc::RefQueryFormulaGroup::ColType
& rCol
= itCol
->second
;
1430 std::vector
<SCROW
> aBounds(rCol
);
1431 pDocument
->UnshareFormulaCells(itGroupTab
->first
, itCol
->first
, aBounds
);
1435 // Notify the listeners to update their references.
1436 RowReorderNotifier
aFunc(aRowMap
, nTab
, nCol1
, nCol2
);
1437 std::for_each(aListeners
.begin(), aListeners
.end(), aFunc
);
1439 // Re-group formulas in affected columns.
1440 for (itGroupTab
= rGroupTabs
.begin(); itGroupTab
!= itGroupTabEnd
; ++itGroupTab
)
1442 const sc::RefQueryFormulaGroup::ColsType
& rCols
= itGroupTab
->second
;
1443 sc::RefQueryFormulaGroup::ColsType::const_iterator itCol
= rCols
.begin(), itColEnd
= rCols
.end();
1444 for (; itCol
!= itColEnd
; ++itCol
)
1445 pDocument
->RegroupFormulaCells(itGroupTab
->first
, itCol
->first
);
1448 // Re-start area listeners on the reordered rows.
1450 std::vector
<sc::AreaListener
>::iterator it
= aAreaListeners
.begin(), itEnd
= aAreaListeners
.end();
1451 for (; it
!= itEnd
; ++it
)
1453 ScRange aNewRange
= it
->maArea
;
1454 sc::ColRowReorderMapType::const_iterator itRow
= aRowMap
.find( aNewRange
.aStart
.Row());
1455 if (itRow
!= aRowMap
.end())
1457 aNewRange
.aStart
.SetRow( itRow
->second
);
1458 aNewRange
.aEnd
.SetRow( itRow
->second
);
1460 pDocument
->StartListeningArea(aNewRange
, it
->mbGroupListening
, it
->mpListener
);
1464 // Re-group columns in the sorted range too.
1465 for (SCCOL i
= nCol1
; i
<= nCol2
; ++i
)
1466 aCol
[i
].RegroupFormulaCells();
1469 // Re-start area listeners on the old group listener ranges.
1470 ListenerStartAction
aAction(*pDocument
);
1471 aGrpListenerRanges
.executeColumnAction(*pDocument
, aAction
);
1475 short ScTable::CompareCell(
1477 ScRefCellValue
& rCell1
, SCCOL nCell1Col
, SCROW nCell1Row
,
1478 ScRefCellValue
& rCell2
, SCCOL nCell2Col
, SCROW nCell2Row
) const
1482 CellType eType1
= rCell1
.meType
, eType2
= rCell2
.meType
;
1484 if (!rCell1
.isEmpty())
1486 if (!rCell2
.isEmpty())
1488 bool bStr1
= ( eType1
!= CELLTYPE_VALUE
);
1489 if (eType1
== CELLTYPE_FORMULA
&& rCell1
.mpFormula
->IsValue())
1491 bool bStr2
= ( eType2
!= CELLTYPE_VALUE
);
1492 if (eType2
== CELLTYPE_FORMULA
&& rCell2
.mpFormula
->IsValue())
1495 if ( bStr1
&& bStr2
) // nur Strings untereinander als String vergleichen!
1499 if (eType1
== CELLTYPE_STRING
)
1500 aStr1
= rCell1
.mpString
->getString();
1502 GetString(nCell1Col
, nCell1Row
, aStr1
);
1503 if (eType2
== CELLTYPE_STRING
)
1504 aStr2
= rCell2
.mpString
->getString();
1506 GetString(nCell2Col
, nCell2Row
, aStr2
);
1508 bool bUserDef
= aSortParam
.bUserDef
; // custom sort order
1509 bool bNaturalSort
= aSortParam
.bNaturalSort
; // natural sort
1510 bool bCaseSens
= aSortParam
.bCaseSens
; // case sensitivity
1514 ScUserList
* pList
= ScGlobal::GetUserList();
1515 const ScUserListData
* pData
= (*pList
)[aSortParam
.nUserIndex
];
1520 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, pData
, pSortCollator
);
1524 nRes
= sal::static_int_cast
<short>( pData
->Compare(aStr1
, aStr2
) );
1526 nRes
= sal::static_int_cast
<short>( pData
->ICompare(aStr1
, aStr2
) );
1536 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, NULL
, pSortCollator
);
1538 nRes
= static_cast<short>( pSortCollator
->compareString( aStr1
, aStr2
) );
1541 else if ( bStr1
) // String <-> Number
1542 nRes
= 1; // Number in front
1543 else if ( bStr2
) // Number <-> String
1544 nRes
= -1; // Number in front
1545 else // Mixed numbers
1547 double nVal1
= rCell1
.getValue();
1548 double nVal2
= rCell2
.getValue();
1551 else if (nVal1
> nVal2
)
1554 if ( !aSortParam
.maKeyState
[nSort
].bAscending
)
1562 if (!rCell2
.isEmpty())
1565 nRes
= 0; // both empty
1570 short ScTable::Compare( ScSortInfoArray
* pArray
, SCCOLROW nIndex1
, SCCOLROW nIndex2
) const
1573 sal_uInt16 nSort
= 0;
1576 ScSortInfo
* pInfo1
= pArray
->Get( nSort
, nIndex1
);
1577 ScSortInfo
* pInfo2
= pArray
->Get( nSort
, nIndex2
);
1578 if ( aSortParam
.bByRow
)
1579 nRes
= CompareCell( nSort
,
1580 pInfo1
->maCell
, static_cast<SCCOL
>(aSortParam
.maKeyState
[nSort
].nField
), pInfo1
->nOrg
,
1581 pInfo2
->maCell
, static_cast<SCCOL
>(aSortParam
.maKeyState
[nSort
].nField
), pInfo2
->nOrg
);
1583 nRes
= CompareCell( nSort
,
1584 pInfo1
->maCell
, static_cast<SCCOL
>(pInfo1
->nOrg
), aSortParam
.maKeyState
[nSort
].nField
,
1585 pInfo2
->maCell
, static_cast<SCCOL
>(pInfo2
->nOrg
), aSortParam
.maKeyState
[nSort
].nField
);
1586 } while ( nRes
== 0 && ++nSort
< pArray
->GetUsedSorts() );
1589 ScSortInfo
* pInfo1
= pArray
->Get( 0, nIndex1
);
1590 ScSortInfo
* pInfo2
= pArray
->Get( 0, nIndex2
);
1591 if( pInfo1
->nOrg
< pInfo2
->nOrg
)
1593 else if( pInfo1
->nOrg
> pInfo2
->nOrg
)
1599 void ScTable::QuickSort( ScSortInfoArray
* pArray
, SCsCOLROW nLo
, SCsCOLROW nHi
)
1601 if ((nHi
- nLo
) == 1)
1603 if (Compare(pArray
, nLo
, nHi
) > 0)
1604 pArray
->Swap( nLo
, nHi
);
1612 while ((ni
<= nHi
) && (Compare(pArray
, ni
, nLo
)) < 0)
1614 while ((nj
>= nLo
) && (Compare(pArray
, nLo
, nj
)) < 0)
1619 pArray
->Swap( ni
, nj
);
1624 if ((nj
- nLo
) < (nHi
- ni
))
1627 QuickSort(pArray
, nLo
, nj
);
1629 QuickSort(pArray
, ni
, nHi
);
1634 QuickSort(pArray
, ni
, nHi
);
1636 QuickSort(pArray
, nLo
, nj
);
1641 short ScTable::Compare(SCCOLROW nIndex1
, SCCOLROW nIndex2
) const
1644 sal_uInt16 nSort
= 0;
1645 const sal_uInt32 nMaxSorts
= aSortParam
.GetSortKeyCount();
1646 if (aSortParam
.bByRow
)
1650 SCCOL nCol
= static_cast<SCCOL
>(aSortParam
.maKeyState
[nSort
].nField
);
1651 ScRefCellValue aCell1
= aCol
[nCol
].GetCellValue(nIndex1
);
1652 ScRefCellValue aCell2
= aCol
[nCol
].GetCellValue(nIndex2
);
1653 nRes
= CompareCell(nSort
, aCell1
, nCol
, nIndex1
, aCell2
, nCol
, nIndex2
);
1654 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.maKeyState
[nSort
].bDoSort
);
1660 SCROW nRow
= aSortParam
.maKeyState
[nSort
].nField
;
1661 ScRefCellValue aCell1
= aCol
[nIndex1
].GetCellValue(nRow
);
1662 ScRefCellValue aCell2
= aCol
[nIndex2
].GetCellValue(nRow
);
1663 nRes
= CompareCell( nSort
, aCell1
, static_cast<SCCOL
>(nIndex1
),
1664 nRow
, aCell2
, static_cast<SCCOL
>(nIndex2
), nRow
);
1665 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.maKeyState
[nSort
].bDoSort
);
1670 bool ScTable::IsSorted( SCCOLROW nStart
, SCCOLROW nEnd
) const // over aSortParam
1672 for (SCCOLROW i
=nStart
; i
<nEnd
; i
++)
1674 if (Compare( i
, i
+1 ) > 0)
1680 void ScTable::DecoladeRow( ScSortInfoArray
* pArray
, SCROW nRow1
, SCROW nRow2
)
1683 int nMax
= nRow2
- nRow1
;
1684 for (SCROW i
= nRow1
; (i
+ 4) <= nRow2
; i
+= 4)
1686 nRow
= comphelper::rng::uniform_int_distribution(0, nMax
-1);
1687 pArray
->Swap(i
, nRow1
+ nRow
);
1692 const ScSortParam
& rSortParam
, bool bKeepQuery
, bool bUpdateRefs
,
1693 ScProgress
* pProgress
, sc::ReorderParam
* pUndo
)
1695 InitSortCollator( rSortParam
);
1696 bGlobalKeepQuery
= bKeepQuery
;
1700 // Copy over the basic sort parameters.
1701 pUndo
->mbByRow
= rSortParam
.bByRow
;
1702 pUndo
->mbPattern
= rSortParam
.bIncludePattern
;
1703 pUndo
->mbHiddenFiltered
= bKeepQuery
;
1704 pUndo
->mbUpdateRefs
= bUpdateRefs
;
1705 pUndo
->mbHasHeaders
= rSortParam
.bHasHeader
;
1708 // It is assumed that the data area has already been trimmed as necessary.
1710 aSortParam
= rSortParam
; // must be assigned before calling IsSorted()
1711 if (rSortParam
.bByRow
)
1713 SCROW nLastRow
= rSortParam
.nRow2
;
1714 SCROW nRow1
= (rSortParam
.bHasHeader
? rSortParam
.nRow1
+ 1 : rSortParam
.nRow1
);
1715 if (nRow1
< nLastRow
&& !IsSorted(nRow1
, nLastRow
))
1718 pProgress
->SetState( 0, nLastRow
-nRow1
);
1720 boost::scoped_ptr
<ScSortInfoArray
> pArray(CreateSortInfoArray(aSortParam
, nRow1
, nLastRow
, bKeepQuery
, bUpdateRefs
));
1722 if ( nLastRow
- nRow1
> 255 )
1723 DecoladeRow(pArray
.get(), nRow1
, nLastRow
);
1725 QuickSort(pArray
.get(), nRow1
, nLastRow
);
1726 if (pArray
->IsUpdateRefs())
1727 SortReorderByRowRefUpdate(pArray
.get(), aSortParam
.nCol1
, aSortParam
.nCol2
, pProgress
);
1729 SortReorderByRow(pArray
.get(), aSortParam
.nCol1
, aSortParam
.nCol2
, pProgress
);
1733 pUndo
->maSortRange
= ScRange(rSortParam
.nCol1
, nRow1
, nTab
, rSortParam
.nCol2
, nLastRow
, nTab
);
1734 pUndo
->maOrderIndices
= pArray
->GetOrderIndices();
1740 SCCOL nLastCol
= rSortParam
.nCol2
;
1741 SCCOL nCol1
= (rSortParam
.bHasHeader
? rSortParam
.nCol1
+ 1 : rSortParam
.nCol1
);
1742 if (nCol1
< nLastCol
&& !IsSorted(nCol1
, nLastCol
))
1745 pProgress
->SetState( 0, nLastCol
-nCol1
);
1747 boost::scoped_ptr
<ScSortInfoArray
> pArray(CreateSortInfoArray(aSortParam
, nCol1
, nLastCol
, bKeepQuery
, bUpdateRefs
));
1749 QuickSort(pArray
.get(), nCol1
, nLastCol
);
1750 SortReorderByColumn(pArray
.get(), aSortParam
.nRow1
, aSortParam
.nRow2
, aSortParam
.bIncludePattern
, pProgress
);
1754 pUndo
->maSortRange
= ScRange(nCol1
, aSortParam
.nRow1
, nTab
, nLastCol
, aSortParam
.nRow2
, nTab
);
1755 pUndo
->maOrderIndices
= pArray
->GetOrderIndices();
1759 DestroySortCollator();
1762 void ScTable::Reorder( const sc::ReorderParam
& rParam
, ScProgress
* pProgress
)
1764 if (rParam
.maOrderIndices
.empty())
1767 boost::scoped_ptr
<ScSortInfoArray
> pArray(CreateSortInfoArray(rParam
));
1773 // Re-play sorting from the known sort indices.
1774 pArray
->ReorderByRow(rParam
.maOrderIndices
);
1775 if (pArray
->IsUpdateRefs())
1776 SortReorderByRowRefUpdate(
1777 pArray
.get(), rParam
.maSortRange
.aStart
.Col(), rParam
.maSortRange
.aEnd
.Col(), pProgress
);
1780 pArray
.get(), rParam
.maSortRange
.aStart
.Col(), rParam
.maSortRange
.aEnd
.Col(), pProgress
);
1784 // Ordering by column is much simpler. Just set the order indices and we are done.
1785 pArray
->SetOrderIndices(rParam
.maOrderIndices
);
1786 SortReorderByColumn(
1787 pArray
.get(), rParam
.maSortRange
.aStart
.Row(), rParam
.maSortRange
.aEnd
.Row(),
1788 rParam
.mbPattern
, pProgress
);
1794 class SubTotalRowFinder
1796 const ScTable
& mrTab
;
1797 const ScSubTotalParam
& mrParam
;
1800 SubTotalRowFinder(const ScTable
& rTab
, const ScSubTotalParam
& rParam
) :
1801 mrTab(rTab
), mrParam(rParam
) {}
1803 bool operator() (size_t nRow
, const ScFormulaCell
* pCell
)
1805 if (!pCell
->IsSubTotal())
1808 SCCOL nStartCol
= mrParam
.nCol1
;
1809 SCCOL nEndCol
= mrParam
.nCol2
;
1811 for (SCCOL i
= 0; i
<= MAXCOL
; ++i
)
1813 if (nStartCol
<= i
&& i
<= nEndCol
)
1816 if (mrTab
.HasData(i
, nRow
))
1826 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam
& rParam
)
1828 SCCOL nStartCol
= rParam
.nCol1
;
1829 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
1830 SCCOL nEndCol
= rParam
.nCol2
;
1831 SCROW nEndRow
= rParam
.nRow2
;
1833 for (SCCOL nCol
= nStartCol
; nCol
<= nEndCol
; ++nCol
)
1835 const sc::CellStoreType
& rCells
= aCol
[nCol
].maCells
;
1836 SubTotalRowFinder
aFunc(*this, rParam
);
1837 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
=
1838 sc::FindFormula(rCells
, nStartRow
, nEndRow
, aFunc
);
1839 if (aPos
.first
!= rCells
.end())
1847 class RemoveSubTotalsHandler
1849 std::vector
<SCROW
> maRemoved
;
1852 void operator() (size_t nRow
, const ScFormulaCell
* p
)
1854 if (p
->IsSubTotal())
1855 maRemoved
.push_back(nRow
);
1858 void getRows(std::vector
<SCROW
>& rRows
)
1860 // Sort and remove duplicates.
1861 std::sort(maRemoved
.begin(), maRemoved
.end());
1862 std::vector
<SCROW
>::iterator it
= std::unique(maRemoved
.begin(), maRemoved
.end());
1863 maRemoved
.erase(it
, maRemoved
.end());
1865 maRemoved
.swap(rRows
);
1871 void ScTable::RemoveSubTotals( ScSubTotalParam
& rParam
)
1873 SCCOL nStartCol
= rParam
.nCol1
;
1874 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
1875 SCCOL nEndCol
= rParam
.nCol2
;
1876 SCROW nEndRow
= rParam
.nRow2
; // will change
1878 RemoveSubTotalsHandler aFunc
;
1879 for (SCCOL nCol
= nStartCol
; nCol
<= nEndCol
; ++nCol
)
1881 const sc::CellStoreType
& rCells
= aCol
[nCol
].maCells
;
1882 sc::ParseFormula(rCells
.begin(), rCells
, nStartRow
, nEndRow
, aFunc
);
1885 std::vector
<SCROW
> aRows
;
1886 aFunc
.getRows(aRows
);
1888 std::vector
<SCROW
>::reverse_iterator it
= aRows
.rbegin(), itEnd
= aRows
.rend();
1889 for (; it
!= itEnd
; ++it
)
1892 RemoveRowBreak(nRow
+1, false, true);
1893 pDocument
->DeleteRow(0, nTab
, MAXCOL
, nTab
, nRow
, 1);
1896 rParam
.nRow2
-= aRows
.size();
1899 // Delete hard number formats (for result formulas)
1901 static void lcl_RemoveNumberFormat( ScTable
* pTab
, SCCOL nCol
, SCROW nRow
)
1903 const ScPatternAttr
* pPattern
= pTab
->GetPattern( nCol
, nRow
);
1904 if ( pPattern
->GetItemSet().GetItemState( ATTR_VALUE_FORMAT
, false )
1905 == SfxItemState::SET
)
1907 ScPatternAttr
aNewPattern( *pPattern
);
1908 SfxItemSet
& rSet
= aNewPattern
.GetItemSet();
1909 rSet
.ClearItem( ATTR_VALUE_FORMAT
);
1910 rSet
.ClearItem( ATTR_LANGUAGE_FORMAT
);
1911 pTab
->SetPattern( nCol
, nRow
, aNewPattern
, true );
1915 // at least MSC needs this at linkage level to be able to use it in a template
1916 typedef struct lcl_ScTable_DoSubTotals_RowEntry
1918 sal_uInt16 nGroupNo
;
1925 // new intermediate results
1926 // rParam.nRow2 is changed !
1928 bool ScTable::DoSubTotals( ScSubTotalParam
& rParam
)
1930 SCCOL nStartCol
= rParam
.nCol1
;
1931 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
1932 SCCOL nEndCol
= rParam
.nCol2
;
1933 SCROW nEndRow
= rParam
.nRow2
; // will change
1936 // Remove emty rows at the end
1937 // so that all exceeding (MAXROW) can be found by InsertRow (#35180#)
1938 // If sorted, all empty rows are at the end.
1939 SCSIZE nEmpty
= GetEmptyLinesInBlock( nStartCol
, nStartRow
, nEndCol
, nEndRow
, DIR_BOTTOM
);
1942 sal_uInt16 nLevelCount
= 0; // Number of levels
1943 bool bDoThis
= true;
1944 for (i
=0; i
<MAXSUBTOTAL
&& bDoThis
; i
++)
1945 if (rParam
.bGroupActive
[i
])
1950 if (nLevelCount
==0) // do nothing
1953 SCCOL
* nGroupCol
= rParam
.nField
; // columns which will be used when grouping
1955 // With (blank) as a separate category, subtotal rows from
1956 // the other columns must always be tested
1957 // (previously only when a column occured more than once)
1958 bool bTestPrevSub
= ( nLevelCount
> 1 );
1960 OUString aSubString
;
1961 OUString aOutString
;
1963 bool bIgnoreCase
= !rParam
.bCaseSens
;
1965 OUString
*pCompString
[MAXSUBTOTAL
]; // Pointer due to compiler problemens
1966 for (i
=0; i
<MAXSUBTOTAL
; i
++)
1967 pCompString
[i
] = new OUString
;
1971 ScStyleSheet
* pStyle
= static_cast<ScStyleSheet
*>(pDocument
->GetStyleSheetPool()->Find(
1972 ScGlobal::GetRscString(STR_STYLENAME_RESULT
), SFX_STYLE_FAMILY_PARA
));
1974 bool bSpaceLeft
= true; // Succsess when inserting?
1976 // For performance reasons collect formula entries so their
1977 // references don't have to be tested for updates each time a new row is
1980 ::std::vector
< RowEntry
> aRowVector
;
1982 for (sal_uInt16 nLevel
=0; nLevel
<=nLevelCount
&& bSpaceLeft
; nLevel
++) // including grand total
1984 bool bTotal
= ( nLevel
== nLevelCount
);
1985 aRowEntry
.nGroupNo
= bTotal
? 0 : (nLevelCount
-nLevel
-1);
1987 // how many results per level
1988 SCCOL nResCount
= rParam
.nSubTotals
[aRowEntry
.nGroupNo
];
1990 ScSubTotalFunc
* eResFunc
= rParam
.pFunctions
[aRowEntry
.nGroupNo
];
1992 if (nResCount
> 0) // otherwise only sort
1994 for (i
=0; i
<=aRowEntry
.nGroupNo
; i
++)
1996 GetString( nGroupCol
[i
], nStartRow
, aSubString
);
1998 *pCompString
[i
] = ScGlobal::pCharClass
->uppercase( aSubString
);
2000 *pCompString
[i
] = aSubString
;
2001 } // aSubString stays on the last
2003 bool bBlockVis
= false; // group visible?
2004 aRowEntry
.nSubStartRow
= nStartRow
;
2005 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
+1 && bSpaceLeft
; nRow
++)
2016 for (i
=0; i
<=aRowEntry
.nGroupNo
&& !bChanged
; i
++)
2018 GetString( nGroupCol
[i
], nRow
, aString
);
2020 aString
= ScGlobal::pCharClass
->uppercase(aString
);
2021 // when sorting, blanks are seperate group
2022 // otherwise blak cells are allowed below
2023 bChanged
= ( ( !aString
.isEmpty() || rParam
.bDoSort
) &&
2024 aString
!= *pCompString
[i
] );
2026 if ( bChanged
&& bTestPrevSub
)
2028 // No group change on rows that will contain subtotal formulas
2029 for ( ::std::vector
< RowEntry
>::const_iterator
2030 iEntry( aRowVector
.begin());
2031 iEntry
!= aRowVector
.end(); ++iEntry
)
2033 if ( iEntry
->nDestRow
== nRow
)
2044 aRowEntry
.nDestRow
= nRow
;
2045 aRowEntry
.nFuncStart
= aRowEntry
.nSubStartRow
;
2046 aRowEntry
.nFuncEnd
= nRow
-1;
2048 bSpaceLeft
= pDocument
->InsertRow( 0, nTab
, MAXCOL
, nTab
,
2049 aRowEntry
.nDestRow
, 1 );
2050 DBShowRow( aRowEntry
.nDestRow
, bBlockVis
);
2052 if ( rParam
.bPagebreak
&& nRow
< MAXROW
&&
2053 aRowEntry
.nSubStartRow
!= nStartRow
&& nLevel
== 0)
2054 SetRowBreak(aRowEntry
.nSubStartRow
, false, true);
2058 for ( ::std::vector
< RowEntry
>::iterator
iMove(
2059 aRowVector
.begin() );
2060 iMove
!= aRowVector
.end(); ++iMove
)
2062 if ( aRowEntry
.nDestRow
<= iMove
->nSubStartRow
)
2063 ++iMove
->nSubStartRow
;
2064 if ( aRowEntry
.nDestRow
<= iMove
->nDestRow
)
2066 if ( aRowEntry
.nDestRow
<= iMove
->nFuncStart
)
2067 ++iMove
->nFuncStart
;
2068 if ( aRowEntry
.nDestRow
<= iMove
->nFuncEnd
)
2071 // collect formula positions
2072 aRowVector
.push_back( aRowEntry
);
2074 if (bTotal
) // "Grand total"
2075 aOutString
= ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS
);
2078 aOutString
= aSubString
;
2079 if (aOutString
.isEmpty())
2080 aOutString
= ScGlobal::GetRscString( STR_EMPTYDATA
);
2082 sal_uInt16 nStrId
= STR_TABLE_ERGEBNIS
;
2083 if ( nResCount
== 1 )
2084 switch ( eResFunc
[0] )
2086 case SUBTOTAL_FUNC_AVE
: nStrId
= STR_FUN_TEXT_AVG
; break;
2087 case SUBTOTAL_FUNC_CNT
:
2088 case SUBTOTAL_FUNC_CNT2
: nStrId
= STR_FUN_TEXT_COUNT
; break;
2089 case SUBTOTAL_FUNC_MAX
: nStrId
= STR_FUN_TEXT_MAX
; break;
2090 case SUBTOTAL_FUNC_MIN
: nStrId
= STR_FUN_TEXT_MIN
; break;
2091 case SUBTOTAL_FUNC_PROD
: nStrId
= STR_FUN_TEXT_PRODUCT
; break;
2092 case SUBTOTAL_FUNC_STD
:
2093 case SUBTOTAL_FUNC_STDP
: nStrId
= STR_FUN_TEXT_STDDEV
; break;
2094 case SUBTOTAL_FUNC_SUM
: nStrId
= STR_FUN_TEXT_SUM
; break;
2095 case SUBTOTAL_FUNC_VAR
:
2096 case SUBTOTAL_FUNC_VARP
: nStrId
= STR_FUN_TEXT_VAR
; break;
2099 // added to avoid warnings
2102 aOutString
+= ScGlobal::GetRscString( nStrId
);
2104 SetString( nGroupCol
[aRowEntry
.nGroupNo
], aRowEntry
.nDestRow
, nTab
, aOutString
);
2105 ApplyStyle( nGroupCol
[aRowEntry
.nGroupNo
], aRowEntry
.nDestRow
, *pStyle
);
2109 aRowEntry
.nSubStartRow
= nRow
;
2110 for (i
=0; i
<=aRowEntry
.nGroupNo
; i
++)
2112 GetString( nGroupCol
[i
], nRow
, aSubString
);
2114 *pCompString
[i
] = ScGlobal::pCharClass
->uppercase( aSubString
);
2116 *pCompString
[i
] = aSubString
;
2120 bBlockVis
= !RowFiltered(nRow
);
2125 // now insert the formulas
2126 ScComplexRefData aRef
;
2128 aRef
.Ref1
.SetAbsTab(nTab
);
2129 aRef
.Ref2
.SetAbsTab(nTab
);
2130 for ( ::std::vector
< RowEntry
>::const_iterator
iEntry( aRowVector
.begin());
2131 iEntry
!= aRowVector
.end(); ++iEntry
)
2133 SCCOL nResCount
= rParam
.nSubTotals
[iEntry
->nGroupNo
];
2134 SCCOL
* nResCols
= rParam
.pSubTotals
[iEntry
->nGroupNo
];
2135 ScSubTotalFunc
* eResFunc
= rParam
.pFunctions
[iEntry
->nGroupNo
];
2136 for ( SCCOL nResult
=0; nResult
< nResCount
; ++nResult
)
2138 aRef
.Ref1
.SetAbsCol(nResCols
[nResult
]);
2139 aRef
.Ref1
.SetAbsRow(iEntry
->nFuncStart
);
2140 aRef
.Ref2
.SetAbsCol(nResCols
[nResult
]);
2141 aRef
.Ref2
.SetAbsRow(iEntry
->nFuncEnd
);
2144 aArr
.AddOpCode( ocSubTotal
);
2145 aArr
.AddOpCode( ocOpen
);
2146 aArr
.AddDouble( (double) eResFunc
[nResult
] );
2147 aArr
.AddOpCode( ocSep
);
2148 aArr
.AddDoubleReference( aRef
);
2149 aArr
.AddOpCode( ocClose
);
2150 aArr
.AddOpCode( ocStop
);
2151 ScFormulaCell
* pCell
= new ScFormulaCell(
2152 pDocument
, ScAddress(nResCols
[nResult
], iEntry
->nDestRow
, nTab
), aArr
);
2154 SetFormulaCell(nResCols
[nResult
], iEntry
->nDestRow
, pCell
);
2156 if ( nResCols
[nResult
] != nGroupCol
[iEntry
->nGroupNo
] )
2158 ApplyStyle( nResCols
[nResult
], iEntry
->nDestRow
, *pStyle
);
2160 lcl_RemoveNumberFormat( this, nResCols
[nResult
], iEntry
->nDestRow
);
2166 //TODO: according to setting, shift intermediate-sum rows up ?
2168 //TODO: create Outlines directly?
2171 DoAutoOutline( nStartCol
, nStartRow
, nEndCol
, nEndRow
);
2173 for (i
=0; i
<MAXSUBTOTAL
; i
++)
2174 delete pCompString
[i
];
2176 rParam
.nRow2
= nEndRow
; // new end
2182 class QueryEvaluator
2185 svl::SharedStringPool
& mrStrPool
;
2186 const ScTable
& mrTab
;
2187 const ScQueryParam
& mrParam
;
2188 const bool* mpTestEqualCondition
;
2189 utl::TransliterationWrapper
* mpTransliteration
;
2190 CollatorWrapper
* mpCollator
;
2191 const bool mbMatchWholeCell
;
2193 static bool isPartialTextMatchOp(const ScQueryEntry
& rEntry
)
2197 // these operators can only be used with textural comparisons.
2199 case SC_DOES_NOT_CONTAIN
:
2200 case SC_BEGINS_WITH
:
2202 case SC_DOES_NOT_BEGIN_WITH
:
2203 case SC_DOES_NOT_END_WITH
:
2211 static bool isTextMatchOp(const ScQueryEntry
& rEntry
)
2213 if (isPartialTextMatchOp(rEntry
))
2218 // these operators can be used for either textural or value comparison.
2228 bool isRealRegExp(const ScQueryEntry
& rEntry
) const
2230 if (!mrParam
.bRegExp
)
2233 return isTextMatchOp(rEntry
);
2236 bool isTestRegExp(const ScQueryEntry
& rEntry
) const
2238 if (!mpTestEqualCondition
)
2241 if (!mrParam
.bRegExp
)
2244 return (rEntry
.eOp
== SC_LESS_EQUAL
|| rEntry
.eOp
== SC_GREATER_EQUAL
);
2248 QueryEvaluator(ScDocument
& rDoc
, const ScTable
& rTab
, const ScQueryParam
& rParam
,
2249 const bool* pTestEqualCondition
) :
2251 mrStrPool(rDoc
.GetSharedStringPool()),
2254 mpTestEqualCondition(pTestEqualCondition
),
2255 mbMatchWholeCell(rDoc
.GetDocOptions().IsMatchWholeCell())
2257 if (rParam
.bCaseSens
)
2259 mpTransliteration
= ScGlobal::GetCaseTransliteration();
2260 mpCollator
= ScGlobal::GetCaseCollator();
2264 mpTransliteration
= ScGlobal::GetpTransliteration();
2265 mpCollator
= ScGlobal::GetCollator();
2269 bool isQueryByValue(
2270 const ScQueryEntry::Item
& rItem
, SCCOL nCol
, SCROW nRow
, ScRefCellValue
& rCell
)
2272 if (rItem
.meType
== ScQueryEntry::ByString
)
2275 if (!rCell
.isEmpty())
2277 if (rCell
.meType
== CELLTYPE_FORMULA
&& rCell
.mpFormula
->GetErrCode())
2278 // Error values are compared as string.
2281 return rCell
.hasNumeric();
2284 return mrTab
.HasValueData(nCol
, nRow
);
2287 bool isQueryByString(
2288 const ScQueryEntry
& rEntry
, const ScQueryEntry::Item
& rItem
,
2289 SCCOL nCol
, SCROW nRow
, ScRefCellValue
& rCell
)
2291 if (isTextMatchOp(rEntry
))
2294 if (rItem
.meType
!= ScQueryEntry::ByString
)
2297 if (!rCell
.isEmpty())
2298 return rCell
.hasString();
2300 return mrTab
.HasStringData(nCol
, nRow
);
2303 std::pair
<bool,bool> compareByValue(
2304 const ScRefCellValue
& rCell
, SCCOL nCol
, SCROW nRow
,
2305 const ScQueryEntry
& rEntry
, const ScQueryEntry::Item
& rItem
)
2308 bool bTestEqual
= false;
2310 if (!rCell
.isEmpty())
2312 switch (rCell
.meType
)
2314 case CELLTYPE_VALUE
:
2315 nCellVal
= rCell
.mfValue
;
2317 case CELLTYPE_FORMULA
:
2318 nCellVal
= rCell
.mpFormula
->GetValue();
2326 nCellVal
= mrTab
.GetValue(nCol
, nRow
);
2328 /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
2329 * date+time format was queried rEntry.bQueryByDate is not set. In
2330 * case other queries wanted to use this mechanism they should do
2331 * the same, in other words only if rEntry.nVal is an integer value
2332 * rEntry.bQueryByDate should be true and the time fraction be
2334 if (rItem
.meType
== ScQueryEntry::ByDate
)
2336 sal_uInt32 nNumFmt
= mrTab
.GetNumberFormat(nCol
, nRow
);
2337 const SvNumberformat
* pEntry
= mrDoc
.GetFormatTable()->GetEntry(nNumFmt
);
2340 short nNumFmtType
= pEntry
->GetType();
2341 /* NOTE: Omitting the check for absence of
2342 * css::util::NumberFormat::TIME would include also date+time formatted
2343 * values of the same day. That may be desired in some
2344 * cases, querying all time values of a day, but confusing
2345 * in other cases. A user can always setup a standard
2346 * filter query for x >= date AND x < date+1 */
2347 if ((nNumFmtType
& css::util::NumberFormat::DATE
) && !(nNumFmtType
& css::util::NumberFormat::TIME
))
2349 // The format is of date type. Strip off the time
2351 nCellVal
= ::rtl::math::approxFloor(nCellVal
);
2359 bOk
= ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2362 bOk
= (nCellVal
< rItem
.mfVal
) && !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2365 bOk
= (nCellVal
> rItem
.mfVal
) && !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2367 case SC_LESS_EQUAL
:
2368 bOk
= (nCellVal
< rItem
.mfVal
) || ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2369 if ( bOk
&& mpTestEqualCondition
)
2370 bTestEqual
= ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2372 case SC_GREATER_EQUAL
:
2373 bOk
= (nCellVal
> rItem
.mfVal
) || ::rtl::math::approxEqual( nCellVal
, rItem
.mfVal
);
2374 if ( bOk
&& mpTestEqualCondition
)
2375 bTestEqual
= ::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2378 bOk
= !::rtl::math::approxEqual(nCellVal
, rItem
.mfVal
);
2382 // added to avoid warnings
2386 return std::pair
<bool,bool>(bOk
, bTestEqual
);
2389 std::pair
<bool,bool> compareByString(
2390 ScRefCellValue
& rCell
, SCROW nRow
, const ScQueryEntry
& rEntry
, const ScQueryEntry::Item
& rItem
)
2393 bool bTestEqual
= false;
2394 bool bMatchWholeCell
= mbMatchWholeCell
;
2395 svl::SharedString aCellStr
;
2396 if (isPartialTextMatchOp(rEntry
))
2397 // may have to do partial textural comparison.
2398 bMatchWholeCell
= false;
2400 if (!rCell
.isEmpty())
2402 if (rCell
.meType
== CELLTYPE_FORMULA
&& rCell
.mpFormula
->GetErrCode())
2404 // Error cell is evaluated as string (for now).
2405 aCellStr
= mrStrPool
.intern(ScGlobal::GetErrorString(rCell
.mpFormula
->GetErrCode()));
2407 else if (rCell
.meType
== CELLTYPE_STRING
)
2408 aCellStr
= *rCell
.mpString
;
2411 sal_uLong nFormat
= mrTab
.GetNumberFormat( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
2413 ScCellFormat::GetInputString(rCell
, nFormat
, aStr
, *mrDoc
.GetFormatTable(), &mrDoc
);
2414 aCellStr
= mrStrPool
.intern(aStr
);
2420 mrTab
.GetInputString(static_cast<SCCOL
>(rEntry
.nField
), nRow
, aStr
);
2421 aCellStr
= mrStrPool
.intern(aStr
);
2424 bool bRealRegExp
= isRealRegExp(rEntry
);
2425 bool bTestRegExp
= isTestRegExp(rEntry
);
2427 if ( bRealRegExp
|| bTestRegExp
)
2429 sal_Int32 nStart
= 0;
2430 sal_Int32 nEnd
= aCellStr
.getLength();
2432 // from 614 on, nEnd is behind the found text
2433 bool bMatch
= false;
2434 if ( rEntry
.eOp
== SC_ENDS_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
2437 nStart
= aCellStr
.getLength();
2438 bMatch
= rEntry
.GetSearchTextPtr( mrParam
.bCaseSens
)
2439 ->SearchBackward(aCellStr
.getString(), &nStart
, &nEnd
);
2443 bMatch
= rEntry
.GetSearchTextPtr( mrParam
.bCaseSens
)
2444 ->SearchForward(aCellStr
.getString(), &nStart
, &nEnd
);
2446 if ( bMatch
&& bMatchWholeCell
2447 && (nStart
!= 0 || nEnd
!= aCellStr
.getLength()) )
2448 bMatch
= false; // RegExp must match entire cell string
2457 case SC_DOES_NOT_CONTAIN
:
2460 case SC_BEGINS_WITH
:
2461 bOk
= ( bMatch
&& (nStart
== 0) );
2463 case SC_DOES_NOT_BEGIN_WITH
:
2464 bOk
= !( bMatch
&& (nStart
== 0) );
2467 bOk
= ( bMatch
&& (nEnd
== aCellStr
.getLength()) );
2469 case SC_DOES_NOT_END_WITH
:
2470 bOk
= !( bMatch
&& (nEnd
== aCellStr
.getLength()) );
2474 // added to avoid warnings
2478 bTestEqual
= bMatch
;
2482 // Simple string matching i.e. no regexp match.
2483 if (isTextMatchOp(rEntry
))
2485 if (rItem
.meType
!= ScQueryEntry::ByString
&& rItem
.maString
.isEmpty())
2487 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
2488 // the query value is assigned directly, and the string is empty. In that case,
2489 // don't find any string (isEqual would find empty string results in formula cells).
2491 if ( rEntry
.eOp
== SC_NOT_EQUAL
)
2494 else if ( bMatchWholeCell
)
2496 // Fast string equality check by comparing string identifiers.
2497 if (mrParam
.bCaseSens
)
2498 bOk
= aCellStr
.getData() == rItem
.maString
.getData();
2500 bOk
= aCellStr
.getDataIgnoreCase() == rItem
.maString
.getDataIgnoreCase();
2502 if ( rEntry
.eOp
== SC_NOT_EQUAL
)
2507 OUString aQueryStr
= rItem
.maString
.getString();
2508 OUString
aCell( mpTransliteration
->transliterate(
2509 aCellStr
.getString(), ScGlobal::eLnge
, 0, aCellStr
.getLength(),
2511 OUString
aQuer( mpTransliteration
->transliterate(
2512 aQueryStr
, ScGlobal::eLnge
, 0, aQueryStr
.getLength(),
2514 sal_Int32 nIndex
= (rEntry
.eOp
== SC_ENDS_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
) ?
2515 (aCell
.getLength() - aQuer
.getLength()) : 0;
2516 sal_Int32 nStrPos
= ((nIndex
< 0) ? -1 : aCell
.indexOf( aQuer
, nIndex
));
2521 bOk
= ( nStrPos
!= -1 );
2524 case SC_DOES_NOT_CONTAIN
:
2525 bOk
= ( nStrPos
== -1 );
2527 case SC_BEGINS_WITH
:
2528 bOk
= ( nStrPos
== 0 );
2530 case SC_DOES_NOT_BEGIN_WITH
:
2531 bOk
= ( nStrPos
!= 0 );
2534 bOk
= (nStrPos
>= 0 && nStrPos
+ aQuer
.getLength() == aCell
.getLength() );
2536 case SC_DOES_NOT_END_WITH
:
2537 bOk
= (nStrPos
< 0 || nStrPos
+ aQuer
.getLength() != aCell
.getLength() );
2541 // added to avoid warnings
2547 { // use collator here because data was probably sorted
2548 sal_Int32 nCompare
= mpCollator
->compareString(
2549 aCellStr
.getString(), rItem
.maString
.getString());
2553 bOk
= (nCompare
< 0);
2556 bOk
= (nCompare
> 0);
2558 case SC_LESS_EQUAL
:
2559 bOk
= (nCompare
<= 0);
2560 if ( bOk
&& mpTestEqualCondition
&& !bTestEqual
)
2561 bTestEqual
= (nCompare
== 0);
2563 case SC_GREATER_EQUAL
:
2564 bOk
= (nCompare
>= 0);
2565 if ( bOk
&& mpTestEqualCondition
&& !bTestEqual
)
2566 bTestEqual
= (nCompare
== 0);
2570 // added to avoid warnings
2576 return std::pair
<bool,bool>(bOk
, bTestEqual
);
2579 // To be called only if both isQueryByValue() and isQueryByString()
2580 // returned false and range lookup is wanted! In range lookup comparison
2581 // numbers are less than strings. Nothing else is compared.
2582 std::pair
<bool,bool> compareByRangeLookup(
2583 const ScRefCellValue
& rCell
, SCCOL nCol
, SCROW nRow
,
2584 const ScQueryEntry
& rEntry
, const ScQueryEntry::Item
& rItem
)
2586 bool bTestEqual
= false;
2588 if (rItem
.meType
== ScQueryEntry::ByString
&& rEntry
.eOp
!= SC_LESS
&& rEntry
.eOp
!= SC_LESS_EQUAL
)
2589 return std::pair
<bool,bool>(false, bTestEqual
);
2591 if (rItem
.meType
!= ScQueryEntry::ByString
&& rEntry
.eOp
!= SC_GREATER
&& rEntry
.eOp
!= SC_GREATER_EQUAL
)
2592 return std::pair
<bool,bool>(false, bTestEqual
);
2594 if (!rCell
.isEmpty())
2596 if (rItem
.meType
== ScQueryEntry::ByString
)
2598 if (rCell
.meType
== CELLTYPE_FORMULA
&& rCell
.mpFormula
->GetErrCode())
2599 // Error values are compared as string.
2600 return std::pair
<bool,bool>(false, bTestEqual
);
2602 return std::pair
<bool,bool>(rCell
.hasNumeric(), bTestEqual
);
2605 return std::pair
<bool,bool>(!rCell
.hasNumeric(), bTestEqual
);
2608 if (rItem
.meType
== ScQueryEntry::ByString
)
2609 return std::pair
<bool,bool>(mrTab
.HasValueData(nCol
, nRow
), bTestEqual
);
2611 return std::pair
<bool,bool>(!mrTab
.HasValueData(nCol
, nRow
), bTestEqual
);
2617 bool ScTable::ValidQuery(
2618 SCROW nRow
, const ScQueryParam
& rParam
, ScRefCellValue
* pCell
, bool* pbTestEqualCondition
)
2620 if (!rParam
.GetEntry(0).bDoQuery
)
2623 SCSIZE nEntryCount
= rParam
.GetEntryCount();
2625 typedef std::pair
<bool,bool> ResultType
;
2626 static std::vector
<ResultType
> aResults
;
2627 if (aResults
.size() < nEntryCount
)
2628 aResults
.resize(nEntryCount
);
2631 QueryEvaluator
aEval(*pDocument
, *this, rParam
, pbTestEqualCondition
);
2632 ScQueryParam::const_iterator it
, itBeg
= rParam
.begin(), itEnd
= rParam
.end();
2633 for (it
= itBeg
; it
!= itEnd
&& it
->bDoQuery
; ++it
)
2635 const ScQueryEntry
& rEntry
= *it
;
2636 SCCOL nCol
= static_cast<SCCOL
>(rEntry
.nField
);
2638 // we can only handle one single direct query
2639 ScRefCellValue aCell
;
2640 if (pCell
&& it
== itBeg
)
2643 aCell
= GetCellValue(nCol
, nRow
);
2645 std::pair
<bool,bool> aRes(false, false);
2647 const ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
2648 if (rItems
.size() == 1 && rItems
.front().meType
== ScQueryEntry::ByEmpty
)
2650 if (rEntry
.IsQueryByEmpty())
2651 aRes
.first
= !aCol
[rEntry
.nField
].HasDataAt(nRow
);
2654 OSL_ASSERT(rEntry
.IsQueryByNonEmpty());
2655 aRes
.first
= aCol
[rEntry
.nField
].HasDataAt(nRow
);
2660 ScQueryEntry::QueryItemsType::const_iterator itr
= rItems
.begin(), itrEnd
= rItems
.end();
2662 for (; itr
!= itrEnd
; ++itr
)
2664 if (aEval
.isQueryByValue(*itr
, nCol
, nRow
, aCell
))
2666 std::pair
<bool,bool> aThisRes
=
2667 aEval
.compareByValue(aCell
, nCol
, nRow
, rEntry
, *itr
);
2668 aRes
.first
|= aThisRes
.first
;
2669 aRes
.second
|= aThisRes
.second
;
2671 else if (aEval
.isQueryByString(rEntry
, *itr
, nCol
, nRow
, aCell
))
2673 std::pair
<bool,bool> aThisRes
=
2674 aEval
.compareByString(aCell
, nRow
, rEntry
, *itr
);
2675 aRes
.first
|= aThisRes
.first
;
2676 aRes
.second
|= aThisRes
.second
;
2678 else if (rParam
.mbRangeLookup
)
2680 std::pair
<bool,bool> aThisRes
=
2681 aEval
.compareByRangeLookup(aCell
, nCol
, nRow
, rEntry
, *itr
);
2682 aRes
.first
|= aThisRes
.first
;
2683 aRes
.second
|= aThisRes
.second
;
2686 if (aRes
.first
&& aRes
.second
)
2694 aResults
[nPos
] = aRes
;
2698 if (rEntry
.eConnect
== SC_AND
)
2700 aResults
[nPos
].first
= aResults
[nPos
].first
&& aRes
.first
;
2701 aResults
[nPos
].second
= aResults
[nPos
].second
&& aRes
.second
;
2706 aResults
[nPos
] = aRes
;
2711 for ( long j
=1; j
<= nPos
; j
++ )
2713 aResults
[0].first
= aResults
[0].first
|| aResults
[j
].first
;
2714 aResults
[0].second
= aResults
[0].second
|| aResults
[j
].second
;
2717 bool bRet
= aResults
[0].first
;
2718 if ( pbTestEqualCondition
)
2719 *pbTestEqualCondition
= aResults
[0].second
;
2724 void ScTable::TopTenQuery( ScQueryParam
& rParam
)
2726 bool bSortCollatorInitialized
= false;
2727 SCSIZE nEntryCount
= rParam
.GetEntryCount();
2728 SCROW nRow1
= (rParam
.bHasHeader
? rParam
.nRow1
+ 1 : rParam
.nRow1
);
2729 SCSIZE nCount
= static_cast<SCSIZE
>(rParam
.nRow2
- nRow1
+ 1);
2730 for ( SCSIZE i
=0; (i
<nEntryCount
) && (rParam
.GetEntry(i
).bDoQuery
); i
++ )
2732 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
2733 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
2735 switch ( rEntry
.eOp
)
2742 ScSortParam
aLocalSortParam( rParam
, static_cast<SCCOL
>(rEntry
.nField
) );
2743 aSortParam
= aLocalSortParam
; // used in CreateSortInfoArray, Compare
2744 if ( !bSortCollatorInitialized
)
2746 bSortCollatorInitialized
= true;
2747 InitSortCollator( aLocalSortParam
);
2749 boost::scoped_ptr
<ScSortInfoArray
> pArray(CreateSortInfoArray(aSortParam
, nRow1
, rParam
.nRow2
, bGlobalKeepQuery
, false));
2750 DecoladeRow( pArray
.get(), nRow1
, rParam
.nRow2
);
2751 QuickSort( pArray
.get(), nRow1
, rParam
.nRow2
);
2752 ScSortInfo
** ppInfo
= pArray
->GetFirstArray();
2753 SCSIZE nValidCount
= nCount
;
2754 // Don't count note or blank cells, they are sorted to the end
2755 while (nValidCount
> 0 && ppInfo
[nValidCount
-1]->maCell
.isEmpty())
2757 // Don't count Strings, they are between Value and blank
2758 while (nValidCount
> 0 && ppInfo
[nValidCount
-1]->maCell
.hasString())
2760 if ( nValidCount
> 0 )
2762 if ( rItem
.meType
== ScQueryEntry::ByString
)
2763 { // by string ain't going to work
2764 rItem
.meType
= ScQueryEntry::ByValue
;
2765 rItem
.mfVal
= 10; // 10 and 10% respectively
2767 SCSIZE nVal
= (rItem
.mfVal
>= 1 ? static_cast<SCSIZE
>(rItem
.mfVal
) : 1);
2769 switch ( rEntry
.eOp
)
2773 rEntry
.eOp
= SC_GREATER_EQUAL
;
2774 if ( nVal
> nValidCount
)
2776 nOffset
= nValidCount
- nVal
; // 1 <= nVal <= nValidCount
2781 rEntry
.eOp
= SC_LESS_EQUAL
;
2782 if ( nVal
> nValidCount
)
2784 nOffset
= nVal
- 1; // 1 <= nVal <= nValidCount
2789 rEntry
.eOp
= SC_GREATER_EQUAL
;
2792 nOffset
= nValidCount
- (nValidCount
* nVal
/ 100);
2793 if ( nOffset
>= nValidCount
)
2794 nOffset
= nValidCount
- 1;
2799 rEntry
.eOp
= SC_LESS_EQUAL
;
2802 nOffset
= (nValidCount
* nVal
/ 100);
2803 if ( nOffset
>= nValidCount
)
2804 nOffset
= nValidCount
- 1;
2809 // added to avoid warnings
2812 ScRefCellValue aCell
= ppInfo
[nOffset
]->maCell
;
2813 if (aCell
.hasNumeric())
2814 rItem
.mfVal
= aCell
.getValue();
2817 OSL_FAIL( "TopTenQuery: pCell no ValueData" );
2818 rEntry
.eOp
= SC_GREATER_EQUAL
;
2824 rEntry
.eOp
= SC_GREATER_EQUAL
;
2825 rItem
.meType
= ScQueryEntry::ByValue
;
2832 // added to avoid warnings
2836 if ( bSortCollatorInitialized
)
2837 DestroySortCollator();
2842 class PrepareQueryItem
: public std::unary_function
<ScQueryEntry::Item
, void>
2844 const ScDocument
& mrDoc
;
2846 PrepareQueryItem(const ScDocument
& rDoc
) : mrDoc(rDoc
) {}
2848 void operator() (ScQueryEntry::Item
& rItem
)
2850 if (rItem
.meType
!= ScQueryEntry::ByString
&& rItem
.meType
!= ScQueryEntry::ByDate
)
2853 sal_uInt32 nIndex
= 0;
2854 bool bNumber
= mrDoc
.GetFormatTable()->
2855 IsNumberFormat(rItem
.maString
.getString(), nIndex
, rItem
.mfVal
);
2857 // Advanced Filter creates only ByString queries that need to be
2858 // converted to ByValue if appropriate. rItem.mfVal now holds the value
2859 // if bNumber==true.
2861 if (rItem
.meType
== ScQueryEntry::ByString
)
2864 rItem
.meType
= ScQueryEntry::ByValue
;
2868 // Double-check if the query by date is really appropriate.
2870 if (bNumber
&& ((nIndex
% SV_COUNTRY_LANGUAGE_OFFSET
) != 0))
2872 const SvNumberformat
* pEntry
= mrDoc
.GetFormatTable()->GetEntry(nIndex
);
2875 short nNumFmtType
= pEntry
->GetType();
2876 if (!((nNumFmtType
& css::util::NumberFormat::DATE
) && !(nNumFmtType
& css::util::NumberFormat::TIME
)))
2877 rItem
.meType
= ScQueryEntry::ByValue
; // not a date only
2880 rItem
.meType
= ScQueryEntry::ByValue
; // what the ... not a date
2883 rItem
.meType
= ScQueryEntry::ByValue
; // not a date
2887 void lcl_PrepareQuery( const ScDocument
* pDoc
, ScTable
* pTab
, ScQueryParam
& rParam
)
2889 bool bTopTen
= false;
2890 SCSIZE nEntryCount
= rParam
.GetEntryCount();
2892 for ( SCSIZE i
= 0; i
< nEntryCount
; ++i
)
2894 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
2895 if (!rEntry
.bDoQuery
)
2898 ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
2899 std::for_each(rItems
.begin(), rItems
.end(), PrepareQueryItem(*pDoc
));
2903 switch ( rEntry
.eOp
)
2922 pTab
->TopTenQuery( rParam
);
2928 SCSIZE
ScTable::Query(ScQueryParam
& rParamOrg
, bool bKeepSub
)
2930 ScQueryParam
aParam( rParamOrg
);
2931 typedef std::unordered_set
<OUString
, OUStringHash
> StrSetType
;
2934 bool bStarted
= false;
2935 bool bOldResult
= true;
2936 SCROW nOldStart
= 0;
2941 SCROW nHeader
= aParam
.bHasHeader
? 1 : 0;
2943 lcl_PrepareQuery(pDocument
, this, aParam
);
2945 if (!aParam
.bInplace
)
2947 nOutRow
= aParam
.nDestRow
+ nHeader
;
2949 CopyData( aParam
.nCol1
, aParam
.nRow1
, aParam
.nCol2
, aParam
.nRow1
,
2950 aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
2953 SCROW nRealRow2
= aParam
.nRow2
;
2954 for (SCROW j
= aParam
.nRow1
+ nHeader
; j
<= nRealRow2
; ++j
)
2956 bool bResult
; // Filter result
2957 bool bValid
= ValidQuery(j
, aParam
);
2958 if (!bValid
&& bKeepSub
) // Keep subtotals
2960 for (SCCOL nCol
=aParam
.nCol1
; nCol
<=aParam
.nCol2
&& !bValid
; nCol
++)
2962 ScRefCellValue aCell
= GetCellValue(nCol
, j
);
2963 if (aCell
.meType
!= CELLTYPE_FORMULA
)
2966 if (!aCell
.mpFormula
->IsSubTotal())
2969 if (RefVisible(aCell
.mpFormula
))
2975 if (aParam
.bDuplicate
)
2980 for (SCCOL k
=aParam
.nCol1
; k
<= aParam
.nCol2
; k
++)
2983 GetString(k
, j
, aCellStr
);
2984 OUStringBuffer
aBuf(aStr
);
2985 aBuf
.append(aCellStr
);
2986 aBuf
.append(static_cast<sal_Unicode
>(1));
2987 aStr
= aBuf
.makeStringAndClear();
2990 std::pair
<StrSetType::iterator
, bool> r
= aStrSet
.insert(aStr
);
2991 bool bIsUnique
= r
.second
; // unique if inserted.
2992 bResult
= bIsUnique
;
2998 if (aParam
.bInplace
)
3000 if (bResult
== bOldResult
&& bStarted
)
3005 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
3006 nOldStart
= nOldEnd
= j
;
3007 bOldResult
= bResult
;
3015 CopyData( aParam
.nCol1
,j
, aParam
.nCol2
,j
, aParam
.nDestCol
,nOutRow
,aParam
.nDestTab
);
3023 if (aParam
.bInplace
&& bStarted
)
3024 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
3026 if (aParam
.bInplace
)
3032 bool ScTable::CreateExcelQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
3035 boost::scoped_array
<SCCOL
> pFields(new SCCOL
[nCol2
-nCol1
+1]);
3038 OSL_ENSURE( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
3039 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
3040 SCROW nDBRow1
= rQueryParam
.nRow1
;
3041 SCCOL nDBCol2
= rQueryParam
.nCol2
;
3042 // First row must be column headers
3043 while (bValid
&& (nCol
<= nCol2
))
3046 GetUpperCellString(nCol
, nRow1
, aQueryStr
);
3047 bool bFound
= false;
3048 SCCOL i
= rQueryParam
.nCol1
;
3049 while (!bFound
&& (i
<= nDBCol2
))
3051 if ( nTab
== nDBTab
)
3052 GetUpperCellString(i
, nDBRow1
, aCellStr
);
3054 pDocument
->GetUpperCellString(i
, nDBRow1
, nDBTab
, aCellStr
);
3055 bFound
= (aCellStr
== aQueryStr
);
3059 pFields
[nCol
- nCol1
] = i
;
3066 sal_uLong nVisible
= 0;
3067 for ( nCol
=nCol1
; nCol
<=nCol2
; nCol
++ )
3068 nVisible
+= aCol
[nCol
].VisibleCount( nRow1
+1, nRow2
);
3070 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
3072 OSL_FAIL("too many filter criteria");
3076 SCSIZE nNewEntries
= nVisible
;
3077 rQueryParam
.Resize( nNewEntries
);
3080 SCROW nRow
= nRow1
+ 1;
3081 svl::SharedStringPool
& rPool
= pDocument
->GetSharedStringPool();
3082 while (nRow
<= nRow2
)
3085 while (nCol
<= nCol2
)
3087 GetInputString( nCol
, nRow
, aCellStr
);
3088 if (!aCellStr
.isEmpty())
3090 if (nIndex
< nNewEntries
)
3092 rQueryParam
.GetEntry(nIndex
).nField
= pFields
[nCol
- nCol1
];
3093 rQueryParam
.FillInExcelSyntax(rPool
, aCellStr
, nIndex
, NULL
);
3095 if (nIndex
< nNewEntries
)
3096 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_AND
;
3104 if (nIndex
< nNewEntries
)
3105 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_OR
;
3111 bool ScTable::CreateStarQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
3113 // A valid StarQuery must be at least 4 columns wide. To be precise it
3114 // should be exactly 4 columns ...
3115 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
3116 // column Excel style query range immediately left to itself would result
3117 // in a circular reference when the field name or operator or value (first
3118 // to third query range column) is obtained (#i58354#). Furthermore, if the
3119 // range wasn't sufficiently specified data changes wouldn't flag formula
3120 // cells for recalculation.
3121 if (nCol2
- nCol1
< 3)
3129 OSL_ENSURE( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
3130 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
3131 SCROW nDBRow1
= rQueryParam
.nRow1
;
3132 SCCOL nDBCol2
= rQueryParam
.nCol2
;
3134 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRow2
-nRow1
+1);
3135 rQueryParam
.Resize( nNewEntries
);
3136 svl::SharedStringPool
& rPool
= pDocument
->GetSharedStringPool();
3140 ScQueryEntry
& rEntry
= rQueryParam
.GetEntry(nIndex
);
3143 // First column AND/OR
3146 GetUpperCellString(nCol1
, nRow
, aCellStr
);
3147 if ( aCellStr
== ScGlobal::GetRscString(STR_TABLE_UND
) )
3149 rEntry
.eConnect
= SC_AND
;
3152 else if ( aCellStr
== ScGlobal::GetRscString(STR_TABLE_ODER
) )
3154 rEntry
.eConnect
= SC_OR
;
3158 // Second column field name
3159 if ((nIndex
< 1) || bValid
)
3162 GetUpperCellString(nCol1
+ 1, nRow
, aCellStr
);
3163 for (SCCOL i
=rQueryParam
.nCol1
; (i
<= nDBCol2
) && (!bFound
); i
++)
3166 if ( nTab
== nDBTab
)
3167 GetUpperCellString(i
, nDBRow1
, aFieldStr
);
3169 pDocument
->GetUpperCellString(i
, nDBRow1
, nDBTab
, aFieldStr
);
3170 bFound
= (aCellStr
== aFieldStr
);
3180 // Third column operator =<>...
3184 GetUpperCellString(nCol1
+ 2, nRow
, aCellStr
);
3185 if (aCellStr
.startsWith("<"))
3187 if (aCellStr
[1] == '>')
3188 rEntry
.eOp
= SC_NOT_EQUAL
;
3189 else if (aCellStr
[1] == '=')
3190 rEntry
.eOp
= SC_LESS_EQUAL
;
3192 rEntry
.eOp
= SC_LESS
;
3194 else if (aCellStr
.startsWith(">"))
3196 if (aCellStr
[1] == '=')
3197 rEntry
.eOp
= SC_GREATER_EQUAL
;
3199 rEntry
.eOp
= SC_GREATER
;
3201 else if (aCellStr
.startsWith("="))
3202 rEntry
.eOp
= SC_EQUAL
;
3205 // Fourth column values
3209 GetString(nCol1
+ 3, nRow
, aStr
);
3210 rEntry
.GetQueryItem().maString
= rPool
.intern(aStr
);
3211 rEntry
.bDoQuery
= true;
3216 while (bValid
&& (nRow
<= nRow2
) /* && (nIndex < MAXQUERY) */ );
3220 bool ScTable::CreateQueryParam(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
3223 PutInOrder(nCol1
, nCol2
);
3224 PutInOrder(nRow1
, nRow2
);
3226 nCount
= rQueryParam
.GetEntryCount();
3227 for (i
=0; i
< nCount
; i
++)
3228 rQueryParam
.GetEntry(i
).Clear();
3230 // Standard query table
3231 bool bValid
= CreateStarQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
3232 // Excel Query table
3234 bValid
= CreateExcelQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
3236 nCount
= rQueryParam
.GetEntryCount();
3239 // bQueryByString must be set
3240 for (i
=0; i
< nCount
; i
++)
3241 rQueryParam
.GetEntry(i
).GetQueryItem().meType
= ScQueryEntry::ByString
;
3246 for (i
=0; i
< nCount
; i
++)
3247 rQueryParam
.GetEntry(i
).Clear();
3252 bool ScTable::HasColHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW
/* nEndRow */ ) const
3254 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
3256 CellType eType
= GetCellType( nCol
, nStartRow
);
3257 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
3263 bool ScTable::HasRowHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL
/* nEndCol */, SCROW nEndRow
) const
3265 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
3267 CellType eType
= GetCellType( nStartCol
, nRow
);
3268 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
3274 void ScTable::GetFilterEntries(SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, std::vector
<ScTypedStrData
>& rStrings
, bool& rHasDates
)
3276 sc::ColumnBlockConstPosition aBlockPos
;
3277 aCol
[nCol
].InitBlockPosition(aBlockPos
);
3278 aCol
[nCol
].GetFilterEntries(aBlockPos
, nRow1
, nRow2
, rStrings
, rHasDates
);
3281 void ScTable::GetFilteredFilterEntries(
3282 SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, const ScQueryParam
& rParam
, std::vector
<ScTypedStrData
>& rStrings
, bool& rHasDates
)
3284 sc::ColumnBlockConstPosition aBlockPos
;
3285 aCol
[nCol
].InitBlockPosition(aBlockPos
);
3287 // remove the entry for this column from the query parameter
3288 ScQueryParam
aParam( rParam
);
3289 aParam
.RemoveEntryByField(nCol
);
3291 lcl_PrepareQuery(pDocument
, this, aParam
);
3292 bool bHasDates
= false;
3293 for ( SCROW j
= nRow1
; j
<= nRow2
; ++j
)
3295 if (ValidQuery(j
, aParam
))
3297 bool bThisHasDates
= false;
3298 aCol
[nCol
].GetFilterEntries(aBlockPos
, j
, j
, rStrings
, bThisHasDates
);
3299 bHasDates
|= bThisHasDates
;
3303 rHasDates
= bHasDates
;
3306 bool ScTable::GetDataEntries(SCCOL nCol
, SCROW nRow
, std::set
<ScTypedStrData
>& rStrings
, bool bLimit
)
3308 return aCol
[nCol
].GetDataEntries( nRow
, rStrings
, bLimit
);
3311 sal_uLong
ScTable::GetCellCount() const
3313 sal_uLong nCellCount
= 0;
3315 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
3316 nCellCount
+= aCol
[nCol
].GetCellCount();
3321 sal_uLong
ScTable::GetWeightedCount() const
3323 sal_uLong nCellCount
= 0;
3325 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
3326 if ( aCol
[nCol
].GetCellCount() ) // GetCellCount ist inline
3327 nCellCount
+= aCol
[nCol
].GetWeightedCount();
3332 sal_uLong
ScTable::GetCodeCount() const
3334 sal_uLong nCodeCount
= 0;
3336 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
3337 if ( aCol
[nCol
].GetCellCount() ) // GetCellCount ist inline
3338 nCodeCount
+= aCol
[nCol
].GetCodeCount();
3343 sal_Int32
ScTable::GetMaxStringLen( SCCOL nCol
, SCROW nRowStart
,
3344 SCROW nRowEnd
, rtl_TextEncoding eCharSet
) const
3346 if ( ValidCol(nCol
) )
3347 return aCol
[nCol
].GetMaxStringLen( nRowStart
, nRowEnd
, eCharSet
);
3352 sal_Int32
ScTable::GetMaxNumberStringLen(
3353 sal_uInt16
& nPrecision
, SCCOL nCol
, SCROW nRowStart
, SCROW nRowEnd
) const
3355 if ( ValidCol(nCol
) )
3356 return aCol
[nCol
].GetMaxNumberStringLen( nPrecision
, nRowStart
, nRowEnd
);
3361 void ScTable::UpdateSelectionFunction( ScFunctionData
& rData
, const ScMarkData
& rMark
)
3363 ScRangeList aRanges
= rMark
.GetMarkedRanges();
3364 for (SCCOL nCol
= 0; nCol
<= MAXCOL
&& !rData
.bError
; ++nCol
)
3366 if (pColFlags
&& ColHidden(nCol
))
3369 aCol
[nCol
].UpdateSelectionFunction(aRanges
, rData
, *mpHiddenRows
);
3373 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */