fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / data / table3.cxx
blob1c5a551c500d6ef9ea106a6b999471f11b8eb4f2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
28 #include <stdlib.h>
29 #include <unotools/transliterationwrapper.hxx>
31 #include "table.hxx"
32 #include "scitems.hxx"
33 #include "attrib.hxx"
34 #include "formulacell.hxx"
35 #include "document.hxx"
36 #include "globstr.hrc"
37 #include "global.hxx"
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"
49 #include "postit.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>
71 #include <vector>
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
86 the suffix.
88 @param sWhole
89 Original string to be split into pieces
91 @param sPrefix
92 Prefix string that consists of the part before the first number token
94 @param sSuffix
95 String after the last number token. This may still contain number strings.
97 @param fNum
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() )
117 return false;
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 )
126 return false;
128 fNum = aPRNum.Value;
129 sSuffix = sWhole.copy( aPRNum.EndPos );
131 return true;
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.
139 @param sInput1
140 Input string 1
142 @param sInput2
143 Input string 2
145 @param bCaseSens
146 Boolean value for case sensitivity
148 @param pData
149 Pointer to user defined sort list
151 @param pCW
152 Pointer to collator wrapper for normal string comparison
154 @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
155 sInput2 is greater.
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;
164 double nNum1, nNum2;
165 bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
166 bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
168 short nPreRes; // Prefix comparison result
169 if ( pData )
171 if ( bCaseSens )
173 if ( !bNumFound1 || !bNumFound2 )
174 return static_cast<short>(pData->Compare( sStr1, sStr2 ));
175 else
176 nPreRes = pData->Compare( sPre1, sPre2 );
178 else
180 if ( !bNumFound1 || !bNumFound2 )
181 return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
182 else
183 nPreRes = pData->ICompare( sPre1, sPre2 );
186 else
188 if ( !bNumFound1 || !bNumFound2 )
189 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
190 else
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.
206 sStr1 = sSuf1;
207 sStr2 = sSuf2;
209 } while (true);
211 return 0;
216 // STATIC DATA -----------------------------------------------------------
218 struct ScSortInfo
220 ScRefCellValue maCell;
221 SCCOLROW nOrg;
222 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo );
224 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo )
226 // END OF STATIC DATA -----------------------------------------------------
228 class ScSortInfoArray : boost::noncopyable
230 public:
232 struct Cell
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) {}
243 struct Row
245 std::vector<Cell> maCells;
247 bool mbHidden:1;
248 bool mbFiltered:1;
250 Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
253 typedef std::vector<Row*> RowsType;
255 private:
256 boost::scoped_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
258 ScSortInfo*** pppInfo;
259 SCSIZE nCount;
260 SCCOLROW nStart;
261 SCCOLROW mnLastIndex; /// index of last non-empty cell position.
262 sal_uInt16 nUsedSorts;
264 std::vector<SCCOLROW> maOrderIndices;
265 bool mbKeepQuery;
266 bool mbUpdateRefs;
268 public:
269 ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
270 pppInfo(NULL),
271 nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
272 mnLastIndex(nInd2),
273 nUsedSorts(nSorts),
274 mbKeepQuery(false),
275 mbUpdateRefs(false)
277 if (nUsedSorts)
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);
293 ~ScSortInfoArray()
295 if (pppInfo)
297 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
299 ScSortInfo** ppInfo = pppInfo[nSort];
300 for ( SCSIZE j = 0; j < nCount; j++ )
301 delete ppInfo[j];
302 delete [] ppInfo;
304 delete[] pppInfo;
307 if (mpRows)
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
324 OSL_ASSERT(pppInfo);
325 return pppInfo[0];
329 * Call this only during normal sorting, not from reordering.
331 ScSortInfo* Get( sal_uInt16 nSort, SCCOLROW nInd )
333 OSL_ASSERT(pppInfo);
334 return (pppInfo[nSort])[ nInd - nStart ];
338 * Call this only during normal sorting, not from reordering.
340 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
342 OSL_ASSERT(pppInfo);
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];
350 ppInfo[n2] = pTmp;
353 std::swap(maOrderIndices[n1], maOrderIndices[n2]);
355 if (mpRows)
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 )
374 if (!mpRows)
375 return;
377 RowsType& rRows = *mpRows;
379 std::vector<SCCOLROW> aOrderIndices2;
380 aOrderIndices2.reserve(rIndices.size());
382 RowsType aRows2;
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]);
393 rRows.swap(aRows2);
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));
411 return *mpRows;
414 RowsType* GetDataRows()
416 return mpRows.get();
420 namespace {
422 void initDataRows(
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);
454 if (bHiddenFiltered)
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;
471 if (rParam.mbByRow)
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);
483 initDataRows(
484 *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2,
485 rParam.mbPattern, rParam.mbHiddenFiltered);
487 else
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);
497 return pArray;
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 )
506 nUsedSorts++;
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);
523 pInfo->nOrg = nRow;
527 initDataRows(
528 *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2,
529 rSortParam.bIncludePattern, bKeepQuery);
531 else
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);
541 pInfo->nOrg = nCol;
545 return pArray;
548 namespace {
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;
585 SortedRowFlags() :
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();
612 struct PatternSpan
614 SCROW mnRow1;
615 SCROW mnRow2;
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) );
639 else
640 { // SYSTEM
641 DestroySortCollator();
642 pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() :
643 ScGlobal::GetCollator());
647 void ScTable::DestroySortCollator()
649 if ( pSortCollator )
651 if ( !IsSortCollatorGlobal() )
652 delete pSortCollator;
653 pSortCollator = NULL;
657 namespace {
659 template<typename _Hint, typename _ReorderMap, typename _Index>
660 class ReorderNotifier : std::unary_function<SvtListener*, void>
662 _Hint maHint;
663 public:
664 ReorderNotifier( const _ReorderMap& rMap, SCTAB nTab, _Index nPos1, _Index nPos2 ) :
665 maHint(rMap, nTab, nPos1, nPos2) {}
667 void operator() ( SvtListener* p )
669 p->Notify(maHint);
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;
679 public:
680 StartListeningNotifier() {}
682 void operator() ( SvtListener* p )
684 p->Notify(maHint);
688 class StopListeningNotifier : std::unary_function<SvtListener*, void>
690 sc::RefStopListeningHint maHint;
691 public:
692 StopListeningNotifier() {}
694 void operator() ( SvtListener* p )
696 p->Notify(maHint);
700 class FormulaGroupPosCollector : std::unary_function<SvtListener*, void>
702 sc::RefQueryFormulaGroup& mrQuery;
704 public:
705 FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
707 void operator() ( SvtListener* p )
709 p->Query(mrQuery);
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
730 // grouping.
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);
749 break;
750 case CELLTYPE_VALUE:
751 assert(rCell.mpAttr);
752 rCellStore.push_back(rCell.maCell.mfValue);
753 break;
754 case CELLTYPE_EDIT:
755 assert(rCell.mpAttr);
756 rCellStore.push_back(rCell.maCell.mpEditText->Clone());
757 break;
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);
769 else
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())
781 *it = pNew;
784 rCellStore.push_back(pNew);
786 break;
787 default:
788 assert(!rCell.mpAttr);
789 rCellStore.push_back_empty();
792 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j).maCellTextAttrs;
793 if (rCell.mpAttr)
794 rAttrStore.push_back(*rCell.mpAttr);
795 else
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));
807 else
808 rBCStore.push_back_empty();
811 // The same with cell note instances ...
812 sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j).maCellNotes;
813 if (rCell.mpNote)
814 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
815 else
816 rNoteStore.push_back_empty();
818 if (rCell.mpPattern)
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);
830 if (pProgress)
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;
850 ScColumn* mpCol;
852 public:
853 FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
854 mrCells(rCells), mpCol(NULL) {}
856 virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
858 mpCol = pCol;
861 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
863 assert(mpCol);
865 if (!bVal)
866 return;
868 mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
872 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
874 ScColumn* mpCol;
876 boost::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
877 sc::StartListeningContext maStartCxt;
878 sc::EndListeningContext maEndCxt;
880 public:
881 ListenerStartAction( ScDocument& rDoc ) :
882 mpCol(0),
883 mpPosSet(new sc::ColumnBlockPositionSet(rDoc)),
884 maStartCxt(rDoc, mpPosSet),
885 maEndCxt(rDoc, mpPosSet) {}
887 virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
889 mpCol = pCol;
892 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
894 assert(mpCol);
896 if (!bVal)
897 return;
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];
950 if (nDest != nSrc)
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;
960 if (pProgress)
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
1003 // once.
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);
1039 if (nRow2 < MAXROW)
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());
1052 if (nCol2 < nCol1)
1053 return;
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
1063 // afterward.
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());
1194 if (nCol2 < nCol1)
1195 return;
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);
1223 ScRangeList aTmp;
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);
1236 continue;
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);
1244 continue;
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
1249 // 2 ranges.
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());
1259 else
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
1388 // their listening.
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(
1476 sal_uInt16 nSort,
1477 ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1478 ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1480 short nRes = 0;
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())
1490 bStr1 = false;
1491 bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1492 if (eType2 == CELLTYPE_FORMULA && rCell2.mpFormula->IsValue())
1493 bStr2 = false;
1495 if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen!
1497 OUString aStr1;
1498 OUString aStr2;
1499 if (eType1 == CELLTYPE_STRING)
1500 aStr1 = rCell1.mpString->getString();
1501 else
1502 GetString(nCell1Col, nCell1Row, aStr1);
1503 if (eType2 == CELLTYPE_STRING)
1504 aStr2 = rCell2.mpString->getString();
1505 else
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
1512 if (bUserDef)
1514 ScUserList* pList = ScGlobal::GetUserList();
1515 const ScUserListData* pData = (*pList)[aSortParam.nUserIndex];
1517 if (pData)
1519 if ( bNaturalSort )
1520 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, pData, pSortCollator );
1521 else
1523 if ( bCaseSens )
1524 nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) );
1525 else
1526 nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) );
1529 else
1530 bUserDef = false;
1533 if (!bUserDef)
1535 if ( bNaturalSort )
1536 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, NULL, pSortCollator );
1537 else
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();
1549 if (nVal1 < nVal2)
1550 nRes = -1;
1551 else if (nVal1 > nVal2)
1552 nRes = 1;
1554 if ( !aSortParam.maKeyState[nSort].bAscending )
1555 nRes = -nRes;
1557 else
1558 nRes = -1;
1560 else
1562 if (!rCell2.isEmpty())
1563 nRes = 1;
1564 else
1565 nRes = 0; // both empty
1567 return nRes;
1570 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1572 short nRes;
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 );
1582 else
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() );
1587 if( nRes == 0 )
1589 ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 );
1590 ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 );
1591 if( pInfo1->nOrg < pInfo2->nOrg )
1592 nRes = -1;
1593 else if( pInfo1->nOrg > pInfo2->nOrg )
1594 nRes = 1;
1596 return nRes;
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 );
1606 else
1608 SCsCOLROW ni = nLo;
1609 SCsCOLROW nj = nHi;
1612 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1613 ni++;
1614 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1615 nj--;
1616 if (ni <= nj)
1618 if (ni != nj)
1619 pArray->Swap( ni, nj );
1620 ni++;
1621 nj--;
1623 } while (ni < nj);
1624 if ((nj - nLo) < (nHi - ni))
1626 if (nLo < nj)
1627 QuickSort(pArray, nLo, nj);
1628 if (ni < nHi)
1629 QuickSort(pArray, ni, nHi);
1631 else
1633 if (ni < nHi)
1634 QuickSort(pArray, ni, nHi);
1635 if (nLo < nj)
1636 QuickSort(pArray, nLo, nj);
1641 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1643 short nRes;
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 );
1656 else
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 );
1667 return nRes;
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)
1675 return false;
1677 return true;
1680 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1682 SCROW nRow;
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);
1691 void ScTable::Sort(
1692 const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1693 ScProgress* pProgress, sc::ReorderParam* pUndo )
1695 InitSortCollator( rSortParam );
1696 bGlobalKeepQuery = bKeepQuery;
1698 if (pUndo)
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))
1717 if(pProgress)
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);
1728 else
1729 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1731 if (pUndo)
1733 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1734 pUndo->maOrderIndices = pArray->GetOrderIndices();
1738 else
1740 SCCOL nLastCol = rSortParam.nCol2;
1741 SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1742 if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1744 if(pProgress)
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);
1752 if (pUndo)
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())
1765 return;
1767 boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1768 if (!pArray)
1769 return;
1771 if (rParam.mbByRow)
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);
1778 else
1779 SortReorderByRow(
1780 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), pProgress);
1782 else
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);
1792 namespace {
1794 class SubTotalRowFinder
1796 const ScTable& mrTab;
1797 const ScSubTotalParam& mrParam;
1799 public:
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())
1806 return false;
1808 SCCOL nStartCol = mrParam.nCol1;
1809 SCCOL nEndCol = mrParam.nCol2;
1811 for (SCCOL i = 0; i <= MAXCOL; ++i)
1813 if (nStartCol <= i && i <= nEndCol)
1814 continue;
1816 if (mrTab.HasData(i, nRow))
1817 return true;
1820 return false;
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())
1840 return true;
1842 return false;
1845 namespace {
1847 class RemoveSubTotalsHandler
1849 std::vector<SCROW> maRemoved;
1850 public:
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)
1891 SCROW nRow = *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;
1919 SCROW nSubStartRow;
1920 SCROW nDestRow;
1921 SCROW nFuncStart;
1922 SCROW nFuncEnd;
1923 } RowEntry;
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
1934 sal_uInt16 i;
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 );
1940 nEndRow -= nEmpty;
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])
1946 nLevelCount = i+1;
1947 else
1948 bDoThis = false;
1950 if (nLevelCount==0) // do nothing
1951 return true;
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;
1969 //TODO: sort?
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
1978 // inserted
1979 RowEntry aRowEntry;
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];
1989 // result functions
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 );
1997 if ( bIgnoreCase )
1998 *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
1999 else
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++)
2007 bool bChanged;
2008 if (nRow>nEndRow)
2009 bChanged = true;
2010 else
2012 bChanged = false;
2013 if (!bTotal)
2015 OUString aString;
2016 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
2018 GetString( nGroupCol[i], nRow, aString );
2019 if (bIgnoreCase)
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 )
2035 bChanged = false;
2036 break;
2042 if ( bChanged )
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 );
2051 bBlockVis = false;
2052 if ( rParam.bPagebreak && nRow < MAXROW &&
2053 aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2054 SetRowBreak(aRowEntry.nSubStartRow, false, true);
2056 if (bSpaceLeft)
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 )
2065 ++iMove->nDestRow;
2066 if ( aRowEntry.nDestRow <= iMove->nFuncStart )
2067 ++iMove->nFuncStart;
2068 if ( aRowEntry.nDestRow <= iMove->nFuncEnd )
2069 ++iMove->nFuncEnd;
2071 // collect formula positions
2072 aRowVector.push_back( aRowEntry );
2074 if (bTotal) // "Grand total"
2075 aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS );
2076 else
2077 { // "Result"
2078 aOutString = aSubString;
2079 if (aOutString.isEmpty())
2080 aOutString = ScGlobal::GetRscString( STR_EMPTYDATA );
2081 aOutString += " ";
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;
2097 default:
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 );
2107 ++nRow;
2108 ++nEndRow;
2109 aRowEntry.nSubStartRow = nRow;
2110 for (i=0; i<=aRowEntry.nGroupNo; i++)
2112 GetString( nGroupCol[i], nRow, aSubString );
2113 if ( bIgnoreCase )
2114 *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
2115 else
2116 *pCompString[i] = aSubString;
2120 bBlockVis = !RowFiltered(nRow);
2125 // now insert the formulas
2126 ScComplexRefData aRef;
2127 aRef.InitFlags();
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);
2143 ScTokenArray aArr;
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?
2170 if (bSpaceLeft)
2171 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2173 for (i=0; i<MAXSUBTOTAL; i++)
2174 delete pCompString[i];
2176 rParam.nRow2 = nEndRow; // new end
2177 return bSpaceLeft;
2180 namespace {
2182 class QueryEvaluator
2184 ScDocument& mrDoc;
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)
2195 switch (rEntry.eOp)
2197 // these operators can only be used with textural comparisons.
2198 case SC_CONTAINS:
2199 case SC_DOES_NOT_CONTAIN:
2200 case SC_BEGINS_WITH:
2201 case SC_ENDS_WITH:
2202 case SC_DOES_NOT_BEGIN_WITH:
2203 case SC_DOES_NOT_END_WITH:
2204 return true;
2205 default:
2208 return false;
2211 static bool isTextMatchOp(const ScQueryEntry& rEntry)
2213 if (isPartialTextMatchOp(rEntry))
2214 return true;
2216 switch (rEntry.eOp)
2218 // these operators can be used for either textural or value comparison.
2219 case SC_EQUAL:
2220 case SC_NOT_EQUAL:
2221 return true;
2222 default:
2225 return false;
2228 bool isRealRegExp(const ScQueryEntry& rEntry) const
2230 if (!mrParam.bRegExp)
2231 return false;
2233 return isTextMatchOp(rEntry);
2236 bool isTestRegExp(const ScQueryEntry& rEntry) const
2238 if (!mpTestEqualCondition)
2239 return false;
2241 if (!mrParam.bRegExp)
2242 return false;
2244 return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
2247 public:
2248 QueryEvaluator(ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
2249 const bool* pTestEqualCondition) :
2250 mrDoc(rDoc),
2251 mrStrPool(rDoc.GetSharedStringPool()),
2252 mrTab(rTab),
2253 mrParam(rParam),
2254 mpTestEqualCondition(pTestEqualCondition),
2255 mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell())
2257 if (rParam.bCaseSens)
2259 mpTransliteration = ScGlobal::GetCaseTransliteration();
2260 mpCollator = ScGlobal::GetCaseCollator();
2262 else
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)
2273 return false;
2275 if (!rCell.isEmpty())
2277 if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
2278 // Error values are compared as string.
2279 return false;
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))
2292 return true;
2294 if (rItem.meType != ScQueryEntry::ByString)
2295 return false;
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)
2307 bool bOk = false;
2308 bool bTestEqual = false;
2309 double nCellVal;
2310 if (!rCell.isEmpty())
2312 switch (rCell.meType)
2314 case CELLTYPE_VALUE :
2315 nCellVal = rCell.mfValue;
2316 break;
2317 case CELLTYPE_FORMULA :
2318 nCellVal = rCell.mpFormula->GetValue();
2319 break;
2320 default:
2321 nCellVal = 0.0;
2325 else
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
2333 * stripped here. */
2334 if (rItem.meType == ScQueryEntry::ByDate)
2336 sal_uInt32 nNumFmt = mrTab.GetNumberFormat(nCol, nRow);
2337 const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nNumFmt);
2338 if (pEntry)
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
2350 // element.
2351 nCellVal = ::rtl::math::approxFloor(nCellVal);
2356 switch (rEntry.eOp)
2358 case SC_EQUAL :
2359 bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2360 break;
2361 case SC_LESS :
2362 bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2363 break;
2364 case SC_GREATER :
2365 bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2366 break;
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);
2371 break;
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);
2376 break;
2377 case SC_NOT_EQUAL :
2378 bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2379 break;
2380 default:
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)
2392 bool bOk = false;
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;
2409 else
2411 sal_uLong nFormat = mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
2412 OUString aStr;
2413 ScCellFormat::GetInputString(rCell, nFormat, aStr, *mrDoc.GetFormatTable(), &mrDoc);
2414 aCellStr = mrStrPool.intern(aStr);
2417 else
2419 OUString 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 )
2436 nEnd = 0;
2437 nStart = aCellStr.getLength();
2438 bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
2439 ->SearchBackward(aCellStr.getString(), &nStart, &nEnd);
2441 else
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
2449 if ( bRealRegExp )
2450 switch (rEntry.eOp)
2452 case SC_EQUAL:
2453 case SC_CONTAINS:
2454 bOk = bMatch;
2455 break;
2456 case SC_NOT_EQUAL:
2457 case SC_DOES_NOT_CONTAIN:
2458 bOk = !bMatch;
2459 break;
2460 case SC_BEGINS_WITH:
2461 bOk = ( bMatch && (nStart == 0) );
2462 break;
2463 case SC_DOES_NOT_BEGIN_WITH:
2464 bOk = !( bMatch && (nStart == 0) );
2465 break;
2466 case SC_ENDS_WITH:
2467 bOk = ( bMatch && (nEnd == aCellStr.getLength()) );
2468 break;
2469 case SC_DOES_NOT_END_WITH:
2470 bOk = !( bMatch && (nEnd == aCellStr.getLength()) );
2471 break;
2472 default:
2474 // added to avoid warnings
2477 else
2478 bTestEqual = bMatch;
2480 if ( !bRealRegExp )
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).
2490 bOk = false;
2491 if ( rEntry.eOp == SC_NOT_EQUAL )
2492 bOk = !bOk;
2494 else if ( bMatchWholeCell )
2496 // Fast string equality check by comparing string identifiers.
2497 if (mrParam.bCaseSens)
2498 bOk = aCellStr.getData() == rItem.maString.getData();
2499 else
2500 bOk = aCellStr.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2502 if ( rEntry.eOp == SC_NOT_EQUAL )
2503 bOk = !bOk;
2505 else
2507 OUString aQueryStr = rItem.maString.getString();
2508 OUString aCell( mpTransliteration->transliterate(
2509 aCellStr.getString(), ScGlobal::eLnge, 0, aCellStr.getLength(),
2510 NULL ) );
2511 OUString aQuer( mpTransliteration->transliterate(
2512 aQueryStr, ScGlobal::eLnge, 0, aQueryStr.getLength(),
2513 NULL ) );
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 ));
2517 switch (rEntry.eOp)
2519 case SC_EQUAL:
2520 case SC_CONTAINS:
2521 bOk = ( nStrPos != -1 );
2522 break;
2523 case SC_NOT_EQUAL:
2524 case SC_DOES_NOT_CONTAIN:
2525 bOk = ( nStrPos == -1 );
2526 break;
2527 case SC_BEGINS_WITH:
2528 bOk = ( nStrPos == 0 );
2529 break;
2530 case SC_DOES_NOT_BEGIN_WITH:
2531 bOk = ( nStrPos != 0 );
2532 break;
2533 case SC_ENDS_WITH:
2534 bOk = (nStrPos >= 0 && nStrPos + aQuer.getLength() == aCell.getLength() );
2535 break;
2536 case SC_DOES_NOT_END_WITH:
2537 bOk = (nStrPos < 0 || nStrPos + aQuer.getLength() != aCell.getLength() );
2538 break;
2539 default:
2541 // added to avoid warnings
2546 else
2547 { // use collator here because data was probably sorted
2548 sal_Int32 nCompare = mpCollator->compareString(
2549 aCellStr.getString(), rItem.maString.getString());
2550 switch (rEntry.eOp)
2552 case SC_LESS :
2553 bOk = (nCompare < 0);
2554 break;
2555 case SC_GREATER :
2556 bOk = (nCompare > 0);
2557 break;
2558 case SC_LESS_EQUAL :
2559 bOk = (nCompare <= 0);
2560 if ( bOk && mpTestEqualCondition && !bTestEqual )
2561 bTestEqual = (nCompare == 0);
2562 break;
2563 case SC_GREATER_EQUAL :
2564 bOk = (nCompare >= 0);
2565 if ( bOk && mpTestEqualCondition && !bTestEqual )
2566 bTestEqual = (nCompare == 0);
2567 break;
2568 default:
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)
2621 return true;
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);
2630 long nPos = -1;
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)
2641 aCell = *pCell;
2642 else
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);
2652 else
2654 OSL_ASSERT(rEntry.IsQueryByNonEmpty());
2655 aRes.first = aCol[rEntry.nField].HasDataAt(nRow);
2658 else
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)
2687 break;
2691 if (nPos == -1)
2693 nPos++;
2694 aResults[nPos] = aRes;
2696 else
2698 if (rEntry.eConnect == SC_AND)
2700 aResults[nPos].first = aResults[nPos].first && aRes.first;
2701 aResults[nPos].second = aResults[nPos].second && aRes.second;
2703 else
2705 nPos++;
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;
2721 return bRet;
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 )
2737 case SC_TOPVAL:
2738 case SC_BOTVAL:
2739 case SC_TOPPERC:
2740 case SC_BOTPERC:
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())
2756 nValidCount--;
2757 // Don't count Strings, they are between Value and blank
2758 while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.hasString())
2759 nValidCount--;
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);
2768 SCSIZE nOffset = 0;
2769 switch ( rEntry.eOp )
2771 case SC_TOPVAL:
2773 rEntry.eOp = SC_GREATER_EQUAL;
2774 if ( nVal > nValidCount )
2775 nVal = nValidCount;
2776 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
2778 break;
2779 case SC_BOTVAL:
2781 rEntry.eOp = SC_LESS_EQUAL;
2782 if ( nVal > nValidCount )
2783 nVal = nValidCount;
2784 nOffset = nVal - 1; // 1 <= nVal <= nValidCount
2786 break;
2787 case SC_TOPPERC:
2789 rEntry.eOp = SC_GREATER_EQUAL;
2790 if ( nVal > 100 )
2791 nVal = 100;
2792 nOffset = nValidCount - (nValidCount * nVal / 100);
2793 if ( nOffset >= nValidCount )
2794 nOffset = nValidCount - 1;
2796 break;
2797 case SC_BOTPERC:
2799 rEntry.eOp = SC_LESS_EQUAL;
2800 if ( nVal > 100 )
2801 nVal = 100;
2802 nOffset = (nValidCount * nVal / 100);
2803 if ( nOffset >= nValidCount )
2804 nOffset = nValidCount - 1;
2806 break;
2807 default:
2809 // added to avoid warnings
2812 ScRefCellValue aCell = ppInfo[nOffset]->maCell;
2813 if (aCell.hasNumeric())
2814 rItem.mfVal = aCell.getValue();
2815 else
2817 OSL_FAIL( "TopTenQuery: pCell no ValueData" );
2818 rEntry.eOp = SC_GREATER_EQUAL;
2819 rItem.mfVal = 0;
2822 else
2824 rEntry.eOp = SC_GREATER_EQUAL;
2825 rItem.meType = ScQueryEntry::ByValue;
2826 rItem.mfVal = 0;
2829 break;
2830 default:
2832 // added to avoid warnings
2836 if ( bSortCollatorInitialized )
2837 DestroySortCollator();
2840 namespace {
2842 class PrepareQueryItem : public std::unary_function<ScQueryEntry::Item, void>
2844 const ScDocument& mrDoc;
2845 public:
2846 PrepareQueryItem(const ScDocument& rDoc) : mrDoc(rDoc) {}
2848 void operator() (ScQueryEntry::Item& rItem)
2850 if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
2851 return;
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)
2863 if (bNumber)
2864 rItem.meType = ScQueryEntry::ByValue;
2865 return;
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);
2873 if (pEntry)
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
2879 else
2880 rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
2882 else
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)
2896 continue;
2898 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2899 std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc));
2901 if ( !bTopTen )
2903 switch ( rEntry.eOp )
2905 case SC_TOPVAL:
2906 case SC_BOTVAL:
2907 case SC_TOPPERC:
2908 case SC_BOTPERC:
2910 bTopTen = true;
2912 break;
2913 default:
2920 if ( bTopTen )
2922 pTab->TopTenQuery( rParam );
2928 SCSIZE ScTable::Query(ScQueryParam& rParamOrg, bool bKeepSub)
2930 ScQueryParam aParam( rParamOrg );
2931 typedef std::unordered_set<OUString, OUStringHash> StrSetType;
2932 StrSetType aStrSet;
2934 bool bStarted = false;
2935 bool bOldResult = true;
2936 SCROW nOldStart = 0;
2937 SCROW nOldEnd = 0;
2939 SCSIZE nCount = 0;
2940 SCROW nOutRow = 0;
2941 SCROW nHeader = aParam.bHasHeader ? 1 : 0;
2943 lcl_PrepareQuery(pDocument, this, aParam);
2945 if (!aParam.bInplace)
2947 nOutRow = aParam.nDestRow + nHeader;
2948 if (nHeader > 0)
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)
2964 continue;
2966 if (!aCell.mpFormula->IsSubTotal())
2967 continue;
2969 if (RefVisible(aCell.mpFormula))
2970 bValid = true;
2973 if (bValid)
2975 if (aParam.bDuplicate)
2976 bResult = true;
2977 else
2979 OUString aStr;
2980 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
2982 OUString aCellStr;
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;
2995 else
2996 bResult = false;
2998 if (aParam.bInplace)
3000 if (bResult == bOldResult && bStarted)
3001 nOldEnd = j;
3002 else
3004 if (bStarted)
3005 DBShowRows(nOldStart,nOldEnd, bOldResult);
3006 nOldStart = nOldEnd = j;
3007 bOldResult = bResult;
3009 bStarted = true;
3011 else
3013 if (bResult)
3015 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
3016 ++nOutRow;
3019 if (bResult)
3020 ++nCount;
3023 if (aParam.bInplace && bStarted)
3024 DBShowRows(nOldStart,nOldEnd, bOldResult);
3026 if (aParam.bInplace)
3027 SetDrawPageSize();
3029 return nCount;
3032 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3034 bool bValid = true;
3035 boost::scoped_array<SCCOL> pFields(new SCCOL[nCol2-nCol1+1]);
3036 OUString aCellStr;
3037 SCCOL nCol = nCol1;
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))
3045 OUString aQueryStr;
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);
3053 else
3054 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
3055 bFound = (aCellStr == aQueryStr);
3056 if (!bFound) i++;
3058 if (bFound)
3059 pFields[nCol - nCol1] = i;
3060 else
3061 bValid = false;
3062 nCol++;
3064 if (bValid)
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");
3073 nVisible = 0;
3076 SCSIZE nNewEntries = nVisible;
3077 rQueryParam.Resize( nNewEntries );
3079 SCSIZE nIndex = 0;
3080 SCROW nRow = nRow1 + 1;
3081 svl::SharedStringPool& rPool = pDocument->GetSharedStringPool();
3082 while (nRow <= nRow2)
3084 nCol = nCol1;
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);
3094 nIndex++;
3095 if (nIndex < nNewEntries)
3096 rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
3098 else
3099 bValid = false;
3101 nCol++;
3103 nRow++;
3104 if (nIndex < nNewEntries)
3105 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
3108 return bValid;
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)
3122 return false;
3124 bool bValid;
3125 bool bFound;
3126 OUString aCellStr;
3127 SCSIZE nIndex = 0;
3128 SCROW nRow = nRow1;
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);
3142 bValid = false;
3143 // First column AND/OR
3144 if (nIndex > 0)
3146 GetUpperCellString(nCol1, nRow, aCellStr);
3147 if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) )
3149 rEntry.eConnect = SC_AND;
3150 bValid = true;
3152 else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) )
3154 rEntry.eConnect = SC_OR;
3155 bValid = true;
3158 // Second column field name
3159 if ((nIndex < 1) || bValid)
3161 bFound = false;
3162 GetUpperCellString(nCol1 + 1, nRow, aCellStr);
3163 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
3165 OUString aFieldStr;
3166 if ( nTab == nDBTab )
3167 GetUpperCellString(i, nDBRow1, aFieldStr);
3168 else
3169 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
3170 bFound = (aCellStr == aFieldStr);
3171 if (bFound)
3173 rEntry.nField = i;
3174 bValid = true;
3176 else
3177 bValid = false;
3180 // Third column operator =<>...
3181 if (bValid)
3183 bFound = false;
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;
3191 else
3192 rEntry.eOp = SC_LESS;
3194 else if (aCellStr.startsWith(">"))
3196 if (aCellStr[1] == '=')
3197 rEntry.eOp = SC_GREATER_EQUAL;
3198 else
3199 rEntry.eOp = SC_GREATER;
3201 else if (aCellStr.startsWith("="))
3202 rEntry.eOp = SC_EQUAL;
3205 // Fourth column values
3206 if (bValid)
3208 OUString aStr;
3209 GetString(nCol1 + 3, nRow, aStr);
3210 rEntry.GetQueryItem().maString = rPool.intern(aStr);
3211 rEntry.bDoQuery = true;
3213 nIndex++;
3214 nRow++;
3216 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
3217 return bValid;
3220 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3222 SCSIZE i, nCount;
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
3233 if (!bValid)
3234 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3236 nCount = rQueryParam.GetEntryCount();
3237 if (bValid)
3239 // bQueryByString must be set
3240 for (i=0; i < nCount; i++)
3241 rQueryParam.GetEntry(i).GetQueryItem().meType = ScQueryEntry::ByString;
3243 else
3245 // nix
3246 for (i=0; i < nCount; i++)
3247 rQueryParam.GetEntry(i).Clear();
3249 return bValid;
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)
3258 return false;
3260 return true;
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)
3269 return false;
3271 return true;
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();
3318 return nCellCount;
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();
3329 return nCellCount;
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();
3340 return nCodeCount;
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 );
3348 else
3349 return 0;
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 );
3357 else
3358 return 0;
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))
3367 continue;
3369 aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3373 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */