1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svl/zforlist.hxx>
22 #include "scitems.hxx"
24 #include "dociter.hxx"
25 #include "document.hxx"
28 #include "formulacell.hxx"
29 #include "attarray.hxx"
30 #include "patattr.hxx"
31 #include "docoptio.hxx"
32 #include "cellform.hxx"
33 #include "segmenttree.hxx"
34 #include "progress.hxx"
35 #include "queryparam.hxx"
36 #include "queryentry.hxx"
37 #include "globstr.hrc"
38 #include "editutil.hxx"
39 #include "cellvalue.hxx"
40 #include "scmatrix.hxx"
42 #include "tools/fract.hxx"
43 #include "editeng/editobj.hxx"
44 #include "svl/sharedstring.hxx"
48 using ::rtl::math::approxEqual
;
52 // STATIC DATA -----------------------------------------------------------
56 template<typename _Iter
>
57 void incBlock(std::pair
<_Iter
, size_t>& rPos
)
59 // Move to the next block.
64 template<typename _Iter
>
65 void incPos(std::pair
<_Iter
, size_t>& rPos
)
67 if (rPos
.second
+ 1 < rPos
.first
->size
)
68 // Increment within the block.
74 template<typename _Iter
>
75 size_t toLogicalPos(const std::pair
<_Iter
, size_t>& rPos
)
77 return rPos
.first
->position
+ rPos
.second
;
82 void ScAttrArray_IterGetNumberFormat( sal_uLong
& nFormat
, const ScAttrArray
*& rpArr
,
83 SCROW
& nAttrEndRow
, const ScAttrArray
* pNewArr
, SCROW nRow
,
86 if ( rpArr
!= pNewArr
|| nAttrEndRow
< nRow
)
89 SCROW nRowEnd
= MAXROW
;
90 const ScPatternAttr
* pPattern
;
91 if( !(pPattern
= pNewArr
->GetPatternRange( nRowStart
, nRowEnd
, nRow
) ) )
93 pPattern
= pDoc
->GetDefPattern();
97 nFormat
= pPattern
->GetNumberFormat( pDoc
->GetFormatTable() );
99 nAttrEndRow
= nRowEnd
;
103 ScValueIterator::ScValueIterator( ScDocument
* pDocument
, const ScRange
& rRange
,
104 bool bSTotal
, bool bTextZero
) :
107 maStartPos(rRange
.aStart
),
108 maEndPos(rRange
.aEnd
),
109 nNumFmtType( NUMBERFORMAT_UNDEFINED
),
112 bCalcAsShown( pDocument
->GetDocOptions().IsCalcAsShown() ),
113 bTextAsZero( bTextZero
)
115 SCTAB nDocMaxTab
= pDocument
->GetTableCount() - 1;
117 if (!ValidCol(maStartPos
.Col())) maStartPos
.SetCol(MAXCOL
);
118 if (!ValidCol(maEndPos
.Col())) maEndPos
.SetCol(MAXCOL
);
119 if (!ValidRow(maStartPos
.Row())) maStartPos
.SetRow(MAXROW
);
120 if (!ValidRow(maEndPos
.Row())) maEndPos
.SetRow(MAXROW
);
121 if (!ValidTab(maStartPos
.Tab()) || maStartPos
.Tab() > nDocMaxTab
) maStartPos
.SetTab(nDocMaxTab
);
122 if (!ValidTab(maEndPos
.Tab()) || maEndPos
.Tab() > nDocMaxTab
) maEndPos
.SetTab(nDocMaxTab
);
124 nNumFormat
= 0; // Initialized in GetNumberFormat
129 SCROW
ScValueIterator::GetRow() const
131 // Position of the head of the current block + offset within the block
132 // equals the logical element position.
133 return maCurPos
.first
->position
+ maCurPos
.second
;
136 void ScValueIterator::IncBlock()
142 void ScValueIterator::IncPos()
144 if (maCurPos
.second
+ 1 < maCurPos
.first
->size
)
145 // Move within the same block.
148 // Move to the next block.
152 void ScValueIterator::SetPos(size_t nPos
)
154 maCurPos
= mpCells
->position(maCurPos
.first
, nPos
);
157 bool ScValueIterator::GetThis(double& rValue
, sal_uInt16
& rErr
)
161 bool bNextColumn
= maCurPos
.first
== mpCells
->end();
164 if (GetRow() > maEndPos
.Row())
168 ScColumn
* pCol
= NULL
;
171 // Find the next available column.
175 if (mnCol
> maEndPos
.Col())
177 mnCol
= maStartPos
.Col();
179 if (mnTab
> maEndPos
.Tab())
182 return false; // Over and out
185 pCol
= &(pDoc
->maTabs
[mnTab
])->aCol
[mnCol
];
187 while (pCol
->IsEmptyData());
189 mpCells
= &pCol
->maCells
;
190 maCurPos
= mpCells
->position(maStartPos
.Row());
193 SCROW nCurRow
= GetRow();
195 if (bSubTotal
&& pDoc
->maTabs
[mnTab
]->RowFiltered(nCurRow
, NULL
, &nLastRow
))
197 // Skip all filtered rows for subtotal mode.
202 switch (maCurPos
.first
->type
)
204 case sc::element_type_numeric
:
207 rValue
= sc::numeric_block::at(*maCurPos
.first
->data
, maCurPos
.second
);
211 ScAttrArray_IterGetNumberFormat(nNumFormat
, pAttrArray
,
212 nAttrEndRow
, pCol
->pAttrArray
, nCurRow
, pDoc
);
213 rValue
= pDoc
->RoundValueAsShown(rValue
, nNumFormat
);
215 return true; // Found it!
218 case sc::element_type_formula
:
220 ScFormulaCell
& rCell
= *sc::formula_block::at(*maCurPos
.first
->data
, maCurPos
.second
);
221 if (bSubTotal
&& rCell
.IsSubTotal())
223 // Skip subtotal formula cells.
228 if (rCell
.GetErrorOrValue(rErr
, rValue
))
231 return true; // Found it!
233 else if (bTextAsZero
)
242 case sc::element_type_string
:
243 case sc::element_type_edittext
:
249 nNumFmtType
= NUMBERFORMAT_NUMBER
;
257 case sc::element_type_empty
:
259 // Skip the whole block.
265 void ScValueIterator::GetCurNumFmtInfo( short& nType
, sal_uLong
& nIndex
)
267 if (!bNumValid
&& mnTab
< pDoc
->GetTableCount())
269 SCROW nCurRow
= GetRow();
270 const ScColumn
* pCol
= &(pDoc
->maTabs
[mnTab
])->aCol
[mnCol
];
271 nNumFmtIndex
= pCol
->GetNumberFormat(nCurRow
);
272 nNumFmtType
= pDoc
->GetFormatTable()->GetType( nNumFmtIndex
);
277 nIndex
= nNumFmtIndex
;
280 bool ScValueIterator::GetFirst(double& rValue
, sal_uInt16
& rErr
)
282 mnCol
= maStartPos
.Col();
283 mnTab
= maStartPos
.Tab();
285 ScTable
* pTab
= pDoc
->FetchTable(mnTab
);
289 nNumFormat
= 0; // Initialized in GetNumberFormat
293 mpCells
= &pTab
->aCol
[maStartPos
.Col()].maCells
;
294 maCurPos
= mpCells
->position(maStartPos
.Row());
295 return GetThis(rValue
, rErr
);
298 bool ScValueIterator::GetNext(double& rValue
, sal_uInt16
& rErr
)
301 return GetThis(rValue
, rErr
);
304 // ============================================================================
306 ScDBQueryDataIterator::DataAccess::DataAccess(const ScDBQueryDataIterator
* pParent
) :
311 ScDBQueryDataIterator::DataAccess::~DataAccess()
315 const sc::CellStoreType
* ScDBQueryDataIterator::GetColumnCellStore(ScDocument
& rDoc
, SCTAB nTab
, SCCOL nCol
)
317 ScTable
* pTab
= rDoc
.FetchTable(nTab
);
321 return &pTab
->aCol
[nCol
].maCells
;
324 const ScAttrArray
* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument
& rDoc
, SCTAB nTab
, SCCOL nCol
)
326 if (nTab
>= rDoc
.GetTableCount())
327 OSL_FAIL("try to access index out of bounds, FIX IT");
328 ScColumn
* pCol
= &rDoc
.maTabs
[nTab
]->aCol
[nCol
];
329 return pCol
->pAttrArray
;
332 bool ScDBQueryDataIterator::IsQueryValid(
333 ScDocument
& rDoc
, const ScQueryParam
& rParam
, SCTAB nTab
, SCROW nRow
, ScRefCellValue
* pCell
)
335 if (nTab
>= rDoc
.GetTableCount())
336 OSL_FAIL("try to access index out of bounds, FIX IT");
337 return rDoc
.maTabs
[nTab
]->ValidQuery(nRow
, rParam
, pCell
);
340 // ----------------------------------------------------------------------------
342 ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(const ScDBQueryDataIterator
* pParent
, ScDBQueryParamInternal
* pParam
, ScDocument
* pDoc
) :
347 bCalcAsShown( pDoc
->GetDocOptions().IsCalcAsShown() )
349 nCol
= mpParam
->mnField
;
350 nRow
= mpParam
->nRow1
;
351 nTab
= mpParam
->nTab
;
353 SCSIZE nCount
= mpParam
->GetEntryCount();
354 for (i
=0; (i
<nCount
) && (mpParam
->GetEntry(i
).bDoQuery
); i
++)
356 ScQueryEntry
& rEntry
= mpParam
->GetEntry(i
);
357 ScQueryEntry::QueryItemsType
& rItems
= rEntry
.GetQueryItems();
359 ScQueryEntry::Item
& rItem
= rItems
.front();
360 sal_uInt32 nIndex
= 0;
361 bool bNumber
= mpDoc
->GetFormatTable()->IsNumberFormat(
362 rItem
.maString
.getString(), nIndex
, rItem
.mfVal
);
363 rItem
.meType
= bNumber
? ScQueryEntry::ByValue
: ScQueryEntry::ByString
;
365 nNumFormat
= 0; // Initialized in GetNumberFormat
370 ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal()
374 bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value
& rValue
)
376 // Start with the current row position, and find the first row position
377 // that satisfies the query.
379 // If the query starts in the same column as the result vector we can
380 // prefetch the cell which saves us one fetch in the success case.
381 SCCOLROW nFirstQueryField
= mpParam
->GetEntry(0).nField
;
382 ScRefCellValue aCell
;
386 if (maCurPos
.first
== mpCells
->end() || nRow
> mpParam
->nRow2
)
388 // Bottom of the range reached. Bail out.
393 if (maCurPos
.first
->type
== sc::element_type_empty
)
395 // Skip the whole empty block.
400 ScRefCellValue
* pCell
= NULL
;
401 if (nCol
== static_cast<SCCOL
>(nFirstQueryField
))
403 aCell
= sc::toRefCell(maCurPos
.first
, maCurPos
.second
);
407 if (ScDBQueryDataIterator::IsQueryValid(*mpDoc
, *mpParam
, nTab
, nRow
, pCell
))
410 aCell
= sc::toRefCell(maCurPos
.first
, maCurPos
.second
);
411 switch (aCell
.meType
)
415 rValue
.mfValue
= aCell
.mfValue
;
416 rValue
.mbIsNumber
= true;
419 const ScAttrArray
* pNewAttrArray
=
420 ScDBQueryDataIterator::GetAttrArrayByCol(*mpDoc
, nTab
, nCol
);
421 ScAttrArray_IterGetNumberFormat( nNumFormat
, pAttrArray
,
422 nAttrEndRow
, pNewAttrArray
, nRow
, mpDoc
);
423 rValue
.mfValue
= mpDoc
->RoundValueAsShown( rValue
.mfValue
, nNumFormat
);
425 nNumFmtType
= NUMBERFORMAT_NUMBER
;
428 return true; // Found it!
431 case CELLTYPE_FORMULA
:
433 if (aCell
.mpFormula
->IsValue())
435 rValue
.mfValue
= aCell
.mpFormula
->GetValue();
436 rValue
.mbIsNumber
= true;
437 mpDoc
->GetNumberFormatInfo(
438 nNumFmtType
, nNumFmtIndex
, ScAddress(nCol
, nRow
, nTab
));
439 rValue
.mnError
= aCell
.mpFormula
->GetErrCode();
440 return true; // Found it!
442 else if(mpParam
->mbSkipString
)
446 rValue
.maString
= aCell
.mpFormula
->GetString().getString();
447 rValue
.mfValue
= 0.0;
448 rValue
.mnError
= aCell
.mpFormula
->GetErrCode();
449 rValue
.mbIsNumber
= false;
454 case CELLTYPE_STRING
:
456 if (mpParam
->mbSkipString
)
460 rValue
.maString
= aCell
.getString(mpDoc
);
461 rValue
.mfValue
= 0.0;
463 rValue
.mbIsNumber
= false;
474 // statement unreachable
477 bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value
& rValue
)
479 if (mpParam
->bHasHeader
)
482 mpCells
= ScDBQueryDataIterator::GetColumnCellStore(*mpDoc
, nTab
, nCol
);
486 maCurPos
= mpCells
->position(nRow
);
487 return getCurrent(rValue
);
490 bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value
& rValue
)
492 if (!mpCells
|| maCurPos
.first
== mpCells
->end())
496 return getCurrent(rValue
);
499 void ScDBQueryDataIterator::DataAccessInternal::incBlock()
504 nRow
= maCurPos
.first
->position
;
507 void ScDBQueryDataIterator::DataAccessInternal::incPos()
509 if (maCurPos
.second
+ 1 < maCurPos
.first
->size
)
511 // Move within the same block.
516 // Move to the next block.
520 void ScDBQueryDataIterator::DataAccessInternal::setPos(size_t nPos
)
522 maCurPos
= mpCells
->position(maCurPos
.first
, nPos
);
526 // ----------------------------------------------------------------------------
528 ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(const ScDBQueryDataIterator
* pParent
, ScDBQueryParamMatrix
* pParam
) :
533 mpParam
->mpMatrix
->GetDimensions(nC
, nR
);
534 mnRows
= static_cast<SCROW
>(nR
);
535 mnCols
= static_cast<SCCOL
>(nC
);
538 ScDBQueryDataIterator::DataAccessMatrix::~DataAccessMatrix()
542 bool ScDBQueryDataIterator::DataAccessMatrix::getCurrent(Value
& rValue
)
544 // Starting from row == mnCurRow, get the first row that satisfies all the
546 for ( ;mnCurRow
< mnRows
; ++mnCurRow
)
548 const ScMatrix
& rMat
= *mpParam
->mpMatrix
;
549 if (rMat
.IsEmpty(mpParam
->mnField
, mnCurRow
))
550 // Don't take empty values into account.
553 bool bIsStrVal
= rMat
.IsString(mpParam
->mnField
, mnCurRow
);
554 if (bIsStrVal
&& mpParam
->mbSkipString
)
557 if (isValidQuery(mnCurRow
, rMat
))
559 rValue
.maString
= rMat
.GetString(mpParam
->mnField
, mnCurRow
).getString();
560 rValue
.mfValue
= rMat
.GetDouble(mpParam
->mnField
, mnCurRow
);
561 rValue
.mbIsNumber
= !bIsStrVal
;
569 bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value
& rValue
)
571 mnCurRow
= mpParam
->bHasHeader
? 1 : 0;
572 return getCurrent(rValue
);
575 bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value
& rValue
)
578 return getCurrent(rValue
);
583 bool isQueryByValue(const ScQueryEntry::Item
& rItem
, const ScMatrix
& rMat
, SCSIZE nCol
, SCSIZE nRow
)
585 if (rItem
.meType
== ScQueryEntry::ByString
)
588 if (!rMat
.IsValueOrEmpty(nCol
, nRow
))
594 bool isQueryByString(const ScQueryEntry
& rEntry
, const ScQueryEntry::Item
& rItem
, const ScMatrix
& rMat
, SCSIZE nCol
, SCSIZE nRow
)
601 case SC_DOES_NOT_CONTAIN
:
604 case SC_DOES_NOT_BEGIN_WITH
:
605 case SC_DOES_NOT_END_WITH
:
611 if (rItem
.meType
== ScQueryEntry::ByString
&& rMat
.IsString(nCol
, nRow
))
619 bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow
, const ScMatrix
& rMat
) const
621 SCSIZE nEntryCount
= mpParam
->GetEntryCount();
622 vector
<bool> aResults
;
623 aResults
.reserve(nEntryCount
);
625 const CollatorWrapper
& rCollator
=
626 mpParam
->bCaseSens
? *ScGlobal::GetCaseCollator() : *ScGlobal::GetCollator();
628 for (SCSIZE i
= 0; i
< nEntryCount
; ++i
)
630 const ScQueryEntry
& rEntry
= mpParam
->GetEntry(i
);
631 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
632 if (!rEntry
.bDoQuery
)
641 case SC_GREATER_EQUAL
:
645 // Only the above operators are supported.
651 SCSIZE nField
= static_cast<SCSIZE
>(rEntry
.nField
);
652 if (isQueryByValue(rItem
, rMat
, nField
, nRow
))
655 double fMatVal
= rMat
.GetDouble(nField
, nRow
);
656 bool bEqual
= approxEqual(fMatVal
, rItem
.mfVal
);
663 bValid
= (fMatVal
< rItem
.mfVal
) && !bEqual
;
666 bValid
= (fMatVal
> rItem
.mfVal
) && !bEqual
;
669 bValid
= (fMatVal
< rItem
.mfVal
) || bEqual
;
671 case SC_GREATER_EQUAL
:
672 bValid
= (fMatVal
> rItem
.mfVal
) || bEqual
;
681 else if (isQueryByString(rEntry
, rItem
, rMat
, nField
, nRow
))
686 // Equality check first.
687 svl::SharedString aMatStr
= rMat
.GetString(nField
, nRow
);
688 svl::SharedString aQueryStr
= rEntry
.GetQueryItem().maString
;
690 rtl_uString
* p1
= mpParam
->bCaseSens
? aMatStr
.getData() : aMatStr
.getDataIgnoreCase();
691 rtl_uString
* p2
= mpParam
->bCaseSens
? aQueryStr
.getData() : aQueryStr
.getDataIgnoreCase();
709 // Unequality check using collator.
710 sal_Int32 nCompare
= rCollator
.compareString(aMatStr
.getString(), aQueryStr
.getString());
714 bValid
= (nCompare
< 0);
717 bValid
= (nCompare
> 0);
720 bValid
= (nCompare
<= 0);
722 case SC_GREATER_EQUAL
:
723 bValid
= (nCompare
>= 0);
732 if (aResults
.empty())
733 // First query entry.
734 aResults
.push_back(bValid
);
735 else if (rEntry
.eConnect
== SC_AND
)
737 // For AND op, tuck the result into the last result value.
738 size_t n
= aResults
.size();
739 aResults
[n
-1] = aResults
[n
-1] && bValid
;
742 // For OR op, store its own result.
743 aResults
.push_back(bValid
);
746 // Row is valid as long as there is at least one result being true.
747 vector
<bool>::const_iterator itr
= aResults
.begin(), itrEnd
= aResults
.end();
748 for (; itr
!= itrEnd
; ++itr
)
755 // ----------------------------------------------------------------------------
757 ScDBQueryDataIterator::Value::Value() :
758 mnError(0), mbIsNumber(true)
760 ::rtl::math::setNan(&mfValue
);
763 // ----------------------------------------------------------------------------
765 ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument
* pDocument
, ScDBQueryParamBase
* pParam
) :
768 switch (mpParam
->GetType())
770 case ScDBQueryParamBase::INTERNAL
:
772 ScDBQueryParamInternal
* p
= static_cast<ScDBQueryParamInternal
*>(pParam
);
773 mpData
.reset(new DataAccessInternal(this, p
, pDocument
));
776 case ScDBQueryParamBase::MATRIX
:
778 ScDBQueryParamMatrix
* p
= static_cast<ScDBQueryParamMatrix
*>(pParam
);
779 mpData
.reset(new DataAccessMatrix(this, p
));
784 bool ScDBQueryDataIterator::GetFirst(Value
& rValue
)
786 return mpData
->getFirst(rValue
);
789 bool ScDBQueryDataIterator::GetNext(Value
& rValue
)
791 return mpData
->getNext(rValue
);
794 ScCellIterator::ScCellIterator( ScDocument
* pDoc
, const ScRange
& rRange
, bool bSTotal
) :
796 maStartPos(rRange
.aStart
),
797 maEndPos(rRange
.aEnd
),
803 void ScCellIterator::incBlock()
806 maCurColPos
.second
= 0;
808 maCurPos
.SetRow(maCurColPos
.first
->position
);
811 void ScCellIterator::incPos()
813 if (maCurColPos
.second
+ 1 < maCurColPos
.first
->size
)
815 // Move within the same block.
816 ++maCurColPos
.second
;
820 // Move to the next block.
824 void ScCellIterator::setPos(size_t nPos
)
826 maCurColPos
= getColumn()->maCells
.position(maCurColPos
.first
, nPos
);
827 maCurPos
.SetRow(nPos
);
830 const ScColumn
* ScCellIterator::getColumn() const
832 return &mpDoc
->maTabs
[maCurPos
.Tab()]->aCol
[maCurPos
.Col()];
835 void ScCellIterator::init()
837 SCTAB nDocMaxTab
= mpDoc
->GetTableCount() - 1;
839 PutInOrder(maStartPos
, maEndPos
);
841 if (!ValidCol(maStartPos
.Col())) maStartPos
.SetCol(MAXCOL
);
842 if (!ValidCol(maEndPos
.Col())) maEndPos
.SetCol(MAXCOL
);
843 if (!ValidRow(maStartPos
.Row())) maStartPos
.SetRow(MAXROW
);
844 if (!ValidRow(maEndPos
.Row())) maEndPos
.SetRow(MAXROW
);
845 if (!ValidTab(maStartPos
.Tab(), nDocMaxTab
)) maStartPos
.SetTab(nDocMaxTab
);
846 if (!ValidTab(maEndPos
.Tab(), nDocMaxTab
)) maEndPos
.SetTab(nDocMaxTab
);
848 while (maEndPos
.Tab() > 0 && !mpDoc
->maTabs
[maEndPos
.Tab()])
849 maEndPos
.IncTab(-1); // Only the tables in use
851 if (maStartPos
.Tab() > maEndPos
.Tab())
852 maStartPos
.SetTab(maEndPos
.Tab());
854 maCurPos
= maStartPos
;
856 if (!mpDoc
->maTabs
[maCurPos
.Tab()])
858 OSL_FAIL("Table not found");
859 maStartPos
= ScAddress(MAXCOL
+1, MAXROW
+1, MAXTAB
+1); // -> Abort on GetFirst.
860 maCurPos
= maStartPos
;
864 bool ScCellIterator::getCurrent()
866 const ScColumn
* pCol
= getColumn();
870 bool bNextColumn
= maCurColPos
.first
== pCol
->maCells
.end();
873 if (maCurPos
.Row() > maEndPos
.Row())
879 // Move to the next column.
880 maCurPos
.SetRow(maStartPos
.Row());
884 if (maCurPos
.Col() > maEndPos
.Col())
886 maCurPos
.SetCol(maStartPos
.Col());
888 if (maCurPos
.Tab() > maEndPos
.Tab())
891 return false; // Over and out
896 while (pCol
->IsEmptyData());
898 maCurColPos
= pCol
->maCells
.position(maCurPos
.Row());
901 if (maCurColPos
.first
->type
== sc::element_type_empty
)
908 if (mbSubTotal
&& pCol
->GetDoc().maTabs
[maCurPos
.Tab()]->RowFiltered(maCurPos
.Row(), NULL
, &nLastRow
))
910 // Skip all filtered rows for subtotal mode.
915 if (maCurColPos
.first
->type
== sc::element_type_formula
)
917 const ScFormulaCell
* pCell
= sc::formula_block::at(*maCurColPos
.first
->data
, maCurColPos
.second
);
918 if (pCell
->IsSubTotal())
920 // Skip subtotal formula cells.
926 maCurCell
= sc::toRefCell(maCurColPos
.first
, maCurColPos
.second
);
932 CellType
ScCellIterator::getType() const
934 return maCurCell
.meType
;
937 OUString
ScCellIterator::getString()
939 return maCurCell
.getString(mpDoc
);
942 const EditTextObject
* ScCellIterator::getEditText() const
944 return maCurCell
.mpEditText
;
947 ScFormulaCell
* ScCellIterator::getFormulaCell()
949 return maCurCell
.mpFormula
;
952 const ScFormulaCell
* ScCellIterator::getFormulaCell() const
954 return maCurCell
.mpFormula
;
957 double ScCellIterator::getValue()
959 return maCurCell
.getValue();
962 ScCellValue
ScCellIterator::getCellValue() const
965 aRet
.meType
= maCurCell
.meType
;
967 switch (maCurCell
.meType
)
969 case CELLTYPE_STRING
:
970 aRet
.mpString
= new svl::SharedString(*maCurCell
.mpString
);
973 aRet
.mpEditText
= maCurCell
.mpEditText
->Clone();
976 aRet
.mfValue
= maCurCell
.mfValue
;
978 case CELLTYPE_FORMULA
:
979 aRet
.mpFormula
= maCurCell
.mpFormula
->Clone();
988 const ScRefCellValue
& ScCellIterator::getRefCellValue() const
993 bool ScCellIterator::hasString() const
995 return maCurCell
.hasString();
998 bool ScCellIterator::hasNumeric() const
1000 return maCurCell
.hasNumeric();
1003 bool ScCellIterator::hasEmptyData() const
1005 if (maCurCell
.isEmpty())
1008 if (maCurCell
.meType
== CELLTYPE_FORMULA
)
1009 return maCurCell
.mpFormula
->IsEmpty();
1014 bool ScCellIterator::isEmpty() const
1016 return maCurCell
.isEmpty();
1019 bool ScCellIterator::equalsWithoutFormat( const ScAddress
& rPos
) const
1021 ScRefCellValue aOther
;
1022 aOther
.assign(*mpDoc
, rPos
);
1023 return maCurCell
.equalsWithoutFormat(aOther
);
1026 bool ScCellIterator::first()
1028 if (!ValidTab(maCurPos
.Tab()))
1031 maCurPos
= maStartPos
;
1032 const ScColumn
* pCol
= getColumn();
1034 maCurColPos
= pCol
->maCells
.position(maCurPos
.Row());
1035 return getCurrent();
1038 bool ScCellIterator::next()
1041 return getCurrent();
1044 //-------------------------------------------------------------------------------
1046 ScQueryCellIterator::ScQueryCellIterator(ScDocument
* pDocument
, SCTAB nTable
,
1047 const ScQueryParam
& rParam
, bool bMod
) :
1048 mpParam(new ScQueryParam(rParam
)),
1051 nStopOnMismatch( nStopOnMismatchDisabled
),
1052 nTestEqualCondition( nTestEqualConditionDisabled
),
1053 bAdvanceQuery( false ),
1054 bIgnoreMismatchOnLeadingStrings( false )
1056 nCol
= mpParam
->nCol1
;
1057 nRow
= mpParam
->nRow1
;
1059 if (bMod
) // Or else it's already inserted
1061 SCSIZE nCount
= mpParam
->GetEntryCount();
1062 for (i
= 0; (i
< nCount
) && (mpParam
->GetEntry(i
).bDoQuery
); ++i
)
1064 ScQueryEntry
& rEntry
= mpParam
->GetEntry(i
);
1065 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
1066 sal_uInt32 nIndex
= 0;
1067 bool bNumber
= pDoc
->GetFormatTable()->IsNumberFormat(
1068 rItem
.maString
.getString(), nIndex
, rItem
.mfVal
);
1069 rItem
.meType
= bNumber
? ScQueryEntry::ByValue
: ScQueryEntry::ByString
;
1072 nNumFormat
= 0; // Initialized in GetNumberFormat
1077 void ScQueryCellIterator::InitPos()
1079 nRow
= mpParam
->nRow1
;
1080 if (mpParam
->bHasHeader
&& mpParam
->bByRow
)
1082 ScColumn
* pCol
= &(pDoc
->maTabs
[nTab
])->aCol
[nCol
];
1083 maCurPos
= pCol
->maCells
.position(nRow
);
1086 void ScQueryCellIterator::IncPos()
1088 if (maCurPos
.second
+ 1 < maCurPos
.first
->size
)
1090 // Move within the same block.
1095 // Move to the next block.
1099 void ScQueryCellIterator::IncBlock()
1102 maCurPos
.second
= 0;
1104 nRow
= maCurPos
.first
->position
;
1107 bool ScQueryCellIterator::GetThis()
1109 if (nTab
>= pDoc
->GetTableCount())
1110 OSL_FAIL("try to access index out of bounds, FIX IT");
1111 const ScQueryEntry
& rEntry
= mpParam
->GetEntry(0);
1112 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
1114 SCCOLROW nFirstQueryField
= rEntry
.nField
;
1115 bool bAllStringIgnore
= bIgnoreMismatchOnLeadingStrings
&&
1116 rItem
.meType
!= ScQueryEntry::ByString
;
1117 bool bFirstStringIgnore
= bIgnoreMismatchOnLeadingStrings
&&
1118 !mpParam
->bHasHeader
&& rItem
.meType
== ScQueryEntry::ByString
&&
1119 ((mpParam
->bByRow
&& nRow
== mpParam
->nRow1
) ||
1120 (!mpParam
->bByRow
&& nCol
== mpParam
->nCol1
));
1122 ScColumn
* pCol
= &(pDoc
->maTabs
[nTab
])->aCol
[nCol
];
1125 bool bNextColumn
= maCurPos
.first
== pCol
->maCells
.end();
1128 if (nRow
> mpParam
->nRow2
)
1136 if ( ++nCol
> mpParam
->nCol2
)
1137 return false; // Over and out
1138 if ( bAdvanceQuery
)
1140 AdvanceQueryParamEntryField();
1141 nFirstQueryField
= rEntry
.nField
;
1143 pCol
= &(pDoc
->maTabs
[nTab
])->aCol
[nCol
];
1145 while (pCol
->IsEmptyData());
1149 bFirstStringIgnore
= bIgnoreMismatchOnLeadingStrings
&&
1150 !mpParam
->bHasHeader
&& rItem
.meType
== ScQueryEntry::ByString
&&
1154 if (maCurPos
.first
->type
== sc::element_type_empty
)
1160 ScRefCellValue aCell
= sc::toRefCell(maCurPos
.first
, maCurPos
.second
);
1162 if (bAllStringIgnore
&& aCell
.hasString())
1166 bool bTestEqualCondition
= false;
1167 if ( pDoc
->maTabs
[nTab
]->ValidQuery( nRow
, *mpParam
,
1168 (nCol
== static_cast<SCCOL
>(nFirstQueryField
) ? &aCell
: NULL
),
1169 (nTestEqualCondition
? &bTestEqualCondition
: NULL
) ) )
1171 if ( nTestEqualCondition
&& bTestEqualCondition
)
1172 nTestEqualCondition
|= nTestEqualConditionMatched
;
1173 return !aCell
.isEmpty(); // Found it!
1175 else if ( nStopOnMismatch
)
1177 // Yes, even a mismatch may have a fulfilled equal
1178 // condition if regular expressions were involved and
1179 // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried.
1180 if ( nTestEqualCondition
&& bTestEqualCondition
)
1182 nTestEqualCondition
|= nTestEqualConditionMatched
;
1183 nStopOnMismatch
|= nStopOnMismatchOccurred
;
1187 if (bFirstStringIgnore
)
1189 if (aCell
.hasString())
1201 nStopOnMismatch
|= nStopOnMismatchOccurred
;
1208 bFirstStringIgnore
= false;
1212 bool ScQueryCellIterator::GetFirst()
1214 if (nTab
>= pDoc
->GetTableCount())
1215 OSL_FAIL("try to access index out of bounds, FIX IT");
1216 nCol
= mpParam
->nCol1
;
1221 bool ScQueryCellIterator::GetNext()
1224 if ( nStopOnMismatch
)
1225 nStopOnMismatch
= nStopOnMismatchEnabled
;
1226 if ( nTestEqualCondition
)
1227 nTestEqualCondition
= nTestEqualConditionEnabled
;
1231 void ScQueryCellIterator::AdvanceQueryParamEntryField()
1233 SCSIZE nEntries
= mpParam
->GetEntryCount();
1234 for ( SCSIZE j
= 0; j
< nEntries
; j
++ )
1236 ScQueryEntry
& rEntry
= mpParam
->GetEntry( j
);
1237 if ( rEntry
.bDoQuery
)
1239 if ( rEntry
.nField
< MAXCOL
)
1243 OSL_FAIL( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" );
1252 bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL
& nFoundCol
,
1253 SCROW
& nFoundRow
, bool bSearchForEqualAfterMismatch
,
1254 bool bIgnoreMismatchOnLeadingStringsP
)
1256 // Set and automatically reset mpParam->mbRangeLookup when returning. We
1257 // could use comphelper::FlagRestorationGuard, but really, that one is
1258 // overengineered for this simple purpose here.
1263 BoolResetter( bool& r
, bool b
) : mr(r
), mb(r
) { r
= b
; }
1264 ~BoolResetter() { mr
= mb
; }
1265 } aRangeLookupResetter( mpParam
->mbRangeLookup
, true);
1267 nFoundCol
= MAXCOL
+1;
1268 nFoundRow
= MAXROW
+1;
1269 SetStopOnMismatch( true ); // assume sorted keys
1270 SetTestEqualCondition( true );
1271 bIgnoreMismatchOnLeadingStrings
= bIgnoreMismatchOnLeadingStringsP
;
1272 bool bRegExp
= mpParam
->bRegExp
&& mpParam
->GetEntry(0).GetQueryItem().meType
== ScQueryEntry::ByString
;
1273 bool bBinary
= !bRegExp
&& mpParam
->bByRow
&& (mpParam
->GetEntry(0).eOp
==
1274 SC_LESS_EQUAL
|| mpParam
->GetEntry(0).eOp
== SC_GREATER_EQUAL
);
1275 bool bFound
= false;
1280 // BinarySearch() already positions correctly and only needs real
1281 // query comparisons afterwards, skip the verification check below.
1282 mpParam
->mbRangeLookup
= false;
1288 bFound
= GetFirst();
1292 // First equal entry or last smaller than (greater than) entry.
1293 PositionType aPosSave
;
1297 nFoundCol
= GetCol();
1298 nFoundRow
= GetRow();
1299 aPosSave
= maCurPos
;
1301 while ( !IsEqualConditionFulfilled() && (bNext
= GetNext()));
1303 // There may be no pNext but equal condition fulfilled if regular
1304 // expressions are involved. Keep the found entry and proceed.
1305 if (!bNext
&& !IsEqualConditionFulfilled())
1307 // Step back to last in range and adjust position markers for
1308 // GetNumberFormat() or similar.
1309 SCCOL nColDiff
= nCol
- nFoundCol
;
1312 maCurPos
= aPosSave
;
1313 if (mpParam
->mbRangeLookup
)
1315 // Verify that the found entry does not only fulfill the range
1316 // lookup but also the real query, i.e. not numeric was found
1317 // if query is ByString and vice versa.
1318 mpParam
->mbRangeLookup
= false;
1319 // Step back the last field advance if GetNext() did one.
1320 if (bAdvanceQuery
&& nColDiff
)
1322 SCSIZE nEntries
= mpParam
->GetEntryCount();
1323 for (SCSIZE j
=0; j
< nEntries
; ++j
)
1325 ScQueryEntry
& rEntry
= mpParam
->GetEntry( j
);
1326 if (rEntry
.bDoQuery
)
1328 if (rEntry
.nField
- nColDiff
>= 0)
1329 rEntry
.nField
-= nColDiff
;
1332 assert(!"FindEqualOrSortedLastInRange: rEntry.nField -= nColDiff < 0");
1342 nFoundCol
= MAXCOL
+1;
1343 nFoundRow
= MAXROW
+1;
1348 if ( IsEqualConditionFulfilled() )
1350 // Position on last equal entry.
1351 SCSIZE nEntries
= mpParam
->GetEntryCount();
1352 for ( SCSIZE j
= 0; j
< nEntries
; j
++ )
1354 ScQueryEntry
& rEntry
= mpParam
->GetEntry( j
);
1355 if ( rEntry
.bDoQuery
)
1357 switch ( rEntry
.eOp
)
1359 case SC_LESS_EQUAL
:
1360 case SC_GREATER_EQUAL
:
1361 rEntry
.eOp
= SC_EQUAL
;
1365 // added to avoid warnings
1372 PositionType aPosSave
;
1373 bIgnoreMismatchOnLeadingStrings
= false;
1374 SetTestEqualCondition( false );
1377 nFoundCol
= GetCol();
1378 nFoundRow
= GetRow();
1379 aPosSave
= maCurPos
;
1380 } while (GetNext());
1382 // Step back conditions are the same as above
1385 maCurPos
= aPosSave
;
1388 if ( (bSearchForEqualAfterMismatch
|| mpParam
->bRegExp
) &&
1389 StoppedOnMismatch() )
1391 // Assume found entry to be the last value less than respectively
1392 // greater than the query. But keep on searching for an equal match.
1393 SCSIZE nEntries
= mpParam
->GetEntryCount();
1394 for ( SCSIZE j
= 0; j
< nEntries
; j
++ )
1396 ScQueryEntry
& rEntry
= mpParam
->GetEntry( j
);
1397 if ( rEntry
.bDoQuery
)
1399 switch ( rEntry
.eOp
)
1401 case SC_LESS_EQUAL
:
1402 case SC_GREATER_EQUAL
:
1403 rEntry
.eOp
= SC_EQUAL
;
1407 // added to avoid warnings
1414 SetStopOnMismatch( false );
1415 SetTestEqualCondition( false );
1418 // Last of a consecutive area, avoid searching the entire parameter
1419 // range as it is a real performance bottleneck in case of regular
1421 PositionType aPosSave
;
1424 nFoundCol
= GetCol();
1425 nFoundRow
= GetRow();
1426 aPosSave
= maCurPos
;
1427 SetStopOnMismatch( true );
1428 } while (GetNext());
1431 maCurPos
= aPosSave
;
1434 return (nFoundCol
<= MAXCOL
) && (nFoundRow
<= MAXROW
);
1438 bool ScQueryCellIterator::BinarySearch()
1440 // TODO: This will be extremely slow with mdds::multi_type_vector.
1442 if (nTab
>= pDoc
->GetTableCount())
1443 OSL_FAIL("try to access index out of bounds, FIX IT");
1444 nCol
= mpParam
->nCol1
;
1445 ScColumn
* pCol
= &(pDoc
->maTabs
[nTab
])->aCol
[nCol
];
1446 if (pCol
->IsEmptyData())
1449 PositionType aHiPos
, aLoPos
;
1450 ScRefCellValue aCell
;
1452 CollatorWrapper
* pCollator
= (mpParam
->bCaseSens
? ScGlobal::GetCaseCollator() :
1453 ScGlobal::GetCollator());
1454 SvNumberFormatter
& rFormatter
= *(pDoc
->GetFormatTable());
1455 const ScQueryEntry
& rEntry
= mpParam
->GetEntry(0);
1456 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
1457 bool bLessEqual
= rEntry
.eOp
== SC_LESS_EQUAL
;
1458 bool bByString
= rItem
.meType
== ScQueryEntry::ByString
;
1459 bool bAllStringIgnore
= bIgnoreMismatchOnLeadingStrings
&& !bByString
;
1460 bool bFirstStringIgnore
= bIgnoreMismatchOnLeadingStrings
&&
1461 !mpParam
->bHasHeader
&& bByString
;
1463 nRow
= mpParam
->nRow1
;
1464 if (mpParam
->bHasHeader
)
1467 aLoPos
= pCol
->maCells
.position(nRow
);
1468 if (bFirstStringIgnore
&& aLoPos
.first
->type
== sc::element_type_string
)
1471 sal_uLong nFormat
= pCol
->GetNumberFormat(toLogicalPos(aLoPos
));
1472 aCell
= sc::toRefCell(aLoPos
.first
, aLoPos
.second
);
1473 ScCellFormat::GetInputString(aCell
, nFormat
, aCellStr
, rFormatter
, pDoc
);
1474 sal_Int32 nTmp
= pCollator
->compareString(aCellStr
, rEntry
.GetQueryItem().maString
.getString());
1475 if ((rEntry
.eOp
== SC_LESS_EQUAL
&& nTmp
> 0) ||
1476 (rEntry
.eOp
== SC_GREATER_EQUAL
&& nTmp
< 0) ||
1477 (rEntry
.eOp
== SC_EQUAL
&& nTmp
!= 0))
1479 // Skip the first string value at low point.
1484 aHiPos
= pCol
->maCells
.position(mpParam
->nRow2
);
1485 if (bAllStringIgnore
)
1487 // Skip all string cells, but never go past the high point.
1488 if (aLoPos
.first
->type
== sc::element_type_string
)
1490 if (aLoPos
.first
== pCol
->maCells
.end())
1491 // This is the last block. Move to the last element in this block.
1492 aLoPos
.second
= aLoPos
.first
->size
- 1;
1494 // Move to the next block.
1498 if (toLogicalPos(aLoPos
) > toLogicalPos(aHiPos
))
1499 // Never go past the high point.
1503 // Bookkeeping values for breaking up the binary search in case the data
1504 // range isn't strictly sorted.
1505 PositionType aLastInRange
= aLoPos
;
1506 PositionType aFirstLastInRange
= aLastInRange
;
1507 double fLastInRangeValue
= bLessEqual
?
1508 -(::std::numeric_limits
<double>::max()) :
1509 ::std::numeric_limits
<double>::max();
1510 OUString aLastInRangeString
;
1512 aLastInRangeString
= OUString(sal_Unicode(0xFFFF));
1513 if (aLastInRange
.first
!= pCol
->maCells
.end())
1515 aCell
= sc::toRefCell(aLastInRange
.first
, aLastInRange
.second
);
1516 if (aCell
.hasString())
1518 sal_uLong nFormat
= pCol
->GetNumberFormat(toLogicalPos(aLastInRange
));
1520 ScCellFormat::GetInputString(aCell
, nFormat
, aStr
, rFormatter
, pDoc
);
1521 aLastInRangeString
= aStr
;
1525 switch (aCell
.meType
)
1527 case CELLTYPE_VALUE
:
1528 fLastInRangeValue
= aCell
.mfValue
;
1530 case CELLTYPE_FORMULA
:
1531 fLastInRangeValue
= aCell
.mpFormula
->GetValue();
1535 // added to avoid warnings
1542 bool bFound
= false;
1544 size_t nLogicalLow
= toLogicalPos(aLoPos
), nLogicalHigh
= toLogicalPos(aHiPos
);
1545 while (nLogicalLow
<= nLogicalHigh
&& !bDone
)
1547 size_t nMid
= (nLogicalLow
+nLogicalHigh
)/2;
1549 if (i
> nLogicalHigh
)
1552 nLogicalHigh
= nMid
- 1;
1558 bool bHaveRefCell
= false;
1559 PositionType aPos
= pCol
->maCells
.position(i
);
1561 switch (aPos
.first
->type
)
1563 case sc::element_type_formula
:
1564 aCell
= sc::toRefCell(aPos
.first
, aPos
.second
);
1565 bHaveRefCell
= true;
1566 bStr
= aCell
.hasString();
1568 case sc::element_type_string
:
1569 case sc::element_type_edittext
:
1578 // compares are content<query:-1, content>query:1
1579 // Cell value comparison similar to ScTable::ValidQuery()
1580 if (!bStr
&& !bByString
)
1584 aCell
= sc::toRefCell(aPos
.first
, aPos
.second
);
1585 switch (aCell
.meType
)
1587 case CELLTYPE_VALUE
:
1588 nCellVal
= aCell
.mfValue
;
1590 case CELLTYPE_FORMULA
:
1591 nCellVal
= aCell
.mpFormula
->GetValue();
1596 if ((nCellVal
< rItem
.mfVal
) && !::rtl::math::approxEqual(
1597 nCellVal
, rItem
.mfVal
))
1602 if (fLastInRangeValue
< nCellVal
)
1604 fLastInRangeValue
= nCellVal
;
1605 aLastInRange
= aPos
;
1607 else if (fLastInRangeValue
> nCellVal
)
1609 // not strictly sorted, continue with GetThis()
1610 aLastInRange
= aFirstLastInRange
;
1615 else if ((nCellVal
> rItem
.mfVal
) && !::rtl::math::approxEqual(
1616 nCellVal
, rItem
.mfVal
))
1621 if (fLastInRangeValue
> nCellVal
)
1623 fLastInRangeValue
= nCellVal
;
1624 aLastInRange
= aPos
;
1626 else if (fLastInRangeValue
< nCellVal
)
1628 // not strictly sorted, continue with GetThis()
1629 aLastInRange
= aFirstLastInRange
;
1635 else if (bStr
&& bByString
)
1638 sal_uLong nFormat
= pCol
->GetNumberFormat(i
);
1640 aCell
= sc::toRefCell(aPos
.first
, aPos
.second
);
1641 ScCellFormat::GetInputString(aCell
, nFormat
, aCellStr
, rFormatter
, pDoc
);
1643 nRes
= pCollator
->compareString(aCellStr
, rEntry
.GetQueryItem().maString
.getString());
1644 if (nRes
< 0 && bLessEqual
)
1646 sal_Int32 nTmp
= pCollator
->compareString( aLastInRangeString
,
1650 aLastInRangeString
= aCellStr
;
1651 aLastInRange
= aPos
;
1655 // not strictly sorted, continue with GetThis()
1656 aLastInRange
= aFirstLastInRange
;
1660 else if (nRes
> 0 && !bLessEqual
)
1662 sal_Int32 nTmp
= pCollator
->compareString( aLastInRangeString
,
1666 aLastInRangeString
= aCellStr
;
1667 aLastInRange
= aPos
;
1671 // not strictly sorted, continue with GetThis()
1672 aLastInRange
= aFirstLastInRange
;
1677 else if (!bStr
&& bByString
)
1679 nRes
= -1; // numeric < string
1681 aLastInRange
= aPos
;
1683 else // if (bStr && !bByString)
1685 nRes
= 1; // string > numeric
1687 aLastInRange
= aPos
;
1692 nLogicalLow
= nMid
+ 1;
1693 else // assumed to be SC_GREATER_EQUAL
1696 nLogicalHigh
= nMid
- 1;
1706 nLogicalHigh
= nMid
- 1;
1710 else // assumed to be SC_GREATER_EQUAL
1711 nLogicalLow
= nMid
+ 1;
1716 bDone
= bFound
= true;
1721 // If all hits didn't result in a moving limit there's something
1722 // strange, e.g. data range not properly sorted, or only identical
1723 // values encountered, which doesn't mean there aren't any others in
1724 // between.. leave it to GetThis(). The condition for this would be
1725 // if (nLastInRange == nFirstLastInRange) nLo = nFirstLastInRange;
1726 // Else, in case no exact match was found, we step back for a
1727 // subsequent GetThis() to find the last in range. Effectively this is
1728 // --nLo with nLastInRange == nLo-1. Both conditions combined yield:
1729 aLoPos
= aLastInRange
;
1732 if (aLoPos
.first
!= pCol
->maCells
.end() && toLogicalPos(aLoPos
) <= static_cast<size_t>(mpParam
->nRow2
))
1734 nRow
= toLogicalPos(aLoPos
);
1740 nRow
= mpParam
->nRow2
+ 1;
1741 // Set current position to the last possible row.
1742 maCurPos
.first
= pCol
->maCells
.end();
1744 maCurPos
.second
= maCurPos
.first
->size
- 1;
1750 //-------------------------------------------------------------------------------
1752 ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument
* pDocument
, SCTAB nTable
,
1753 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) :
1754 maColPositions(nCol2
-nCol1
+1),
1765 if (mnTab
>= pDoc
->GetTableCount())
1766 OSL_FAIL("try to access index out of bounds, FIX IT");
1768 pNextRows
= new SCROW
[ nCol2
-nCol1
+1 ];
1769 pNextIndices
= new SCSIZE
[ nCol2
-nCol1
+1 ];
1774 ScHorizontalCellIterator::~ScHorizontalCellIterator()
1776 delete [] pNextRows
;
1777 delete [] pNextIndices
;
1780 void ScHorizontalCellIterator::SetTab( SCTAB nTabP
)
1787 // Set the start position in each column.
1788 for (SCCOL i
= nStartCol
; i
<= nEndCol
; ++i
)
1790 ScColumn
* pCol
= &pDoc
->maTabs
[mnTab
]->aCol
[i
];
1791 ColParam
& rParam
= maColPositions
[i
-nStartCol
];
1792 rParam
.maPos
= pCol
->maCells
.position(nStartRow
).first
;
1793 rParam
.maEnd
= pCol
->maCells
.end();
1794 if (rParam
.maPos
!= rParam
.maEnd
)
1801 ColParam
& rParam
= maColPositions
[0];
1802 if (rParam
.maPos
== rParam
.maEnd
|| rParam
.maPos
->type
== sc::element_type_empty
)
1803 // Skip to the first non-empty cell.
1807 ScRefCellValue
* ScHorizontalCellIterator::GetNext( SCCOL
& rCol
, SCROW
& rRow
)
1812 // Return the current non-empty cell, and move the cursor to the next one.
1816 ColParam
& r
= maColPositions
[mnCol
-nStartCol
];
1817 size_t nOffset
= static_cast<size_t>(mnRow
) - r
.maPos
->position
;
1818 maCurCell
= sc::toRefCell(r
.maPos
, nOffset
);
1824 bool ScHorizontalCellIterator::GetPos( SCCOL
& rCol
, SCROW
& rRow
)
1833 bool advanceBlock(size_t nRow
, sc::CellStoreType::const_iterator
& rPos
, const sc::CellStoreType::const_iterator
& rEnd
)
1835 if (nRow
< rPos
->position
+ rPos
->size
)
1836 // Block already contains the specified row. Nothing to do.
1839 // This block is behind the current row position. Advance the block.
1840 for (++rPos
; rPos
!= rEnd
; ++rPos
)
1842 if (nRow
< rPos
->position
+ rPos
->size
)
1843 // Found the block that contains the specified row.
1853 void ScHorizontalCellIterator::Advance()
1855 // Find the next non-empty cell in the current row.
1856 for (SCCOL i
= mnCol
+1; i
<= nEndCol
; ++i
)
1858 ColParam
& r
= maColPositions
[i
-nStartCol
];
1859 if (r
.maPos
== r
.maEnd
)
1862 size_t nRow
= static_cast<size_t>(mnRow
);
1863 if (nRow
< r
.maPos
->position
)
1866 if (!advanceBlock(nRow
, r
.maPos
, r
.maEnd
))
1869 if (r
.maPos
->type
== sc::element_type_empty
)
1872 // Found in the current row.
1878 // Move to the next row that has at least one non-empty cell.
1880 while (mnRow
<= nEndRow
)
1882 size_t nRow
= static_cast<size_t>(mnRow
);
1883 size_t nNextRow
= MAXROW
+1;
1884 size_t nNextRowPos
= 0;
1885 for (size_t i
= nNextRowPos
, n
= maColPositions
.size(); i
< n
; ++i
)
1887 ColParam
& r
= maColPositions
[i
];
1888 if (r
.maPos
== r
.maEnd
)
1889 // This column has ended.
1892 if (nRow
< r
.maPos
->position
)
1894 // This block is ahread of the current row position. Skip it.
1895 if (r
.maPos
->position
< nNextRow
)
1897 nNextRow
= r
.maPos
->position
;
1903 if (!advanceBlock(nRow
, r
.maPos
, r
.maEnd
))
1906 if (r
.maPos
->type
== sc::element_type_empty
)
1908 // Empty block. Move to the next block and try next column.
1910 if (r
.maPos
->position
< nNextRow
)
1912 nNextRow
= r
.maPos
->position
;
1918 // Found a non-empty cell block!
1919 mnCol
= i
+ nStartCol
;
1925 if (nNextRow
> static_cast<size_t>(MAXROW
))
1927 // No more blocks to search.
1932 mnRow
= nNextRow
; // move to the next non-empty row.
1938 //------------------------------------------------------------------------
1940 ScHorizontalValueIterator::ScHorizontalValueIterator( ScDocument
* pDocument
,
1941 const ScRange
& rRange
, bool bSTotal
, bool bTextZero
) :
1944 nEndTab( rRange
.aEnd
.Tab() ),
1945 nNumFmtType( NUMBERFORMAT_UNDEFINED
),
1947 bSubTotal( bSTotal
),
1948 bCalcAsShown( pDocument
->GetDocOptions().IsCalcAsShown() ),
1949 bTextAsZero( bTextZero
)
1951 SCCOL nStartCol
= rRange
.aStart
.Col();
1952 SCROW nStartRow
= rRange
.aStart
.Row();
1953 SCTAB nStartTab
= rRange
.aStart
.Tab();
1954 SCCOL nEndCol
= rRange
.aEnd
.Col();
1955 SCROW nEndRow
= rRange
.aEnd
.Row();
1956 PutInOrder( nStartCol
, nEndCol
);
1957 PutInOrder( nStartRow
, nEndRow
);
1958 PutInOrder( nStartTab
, nEndTab
);
1960 if (!ValidCol(nStartCol
)) nStartCol
= MAXCOL
;
1961 if (!ValidCol(nEndCol
)) nEndCol
= MAXCOL
;
1962 if (!ValidRow(nStartRow
)) nStartRow
= MAXROW
;
1963 if (!ValidRow(nEndRow
)) nEndRow
= MAXROW
;
1964 if (!ValidTab(nStartTab
)) nStartTab
= MAXTAB
;
1965 if (!ValidTab(nEndTab
)) nEndTab
= MAXTAB
;
1967 nCurCol
= nStartCol
;
1968 nCurRow
= nStartRow
;
1969 nCurTab
= nStartTab
;
1971 nNumFormat
= 0; // Will be initialized in GetNumberFormat()
1975 pCellIter
= new ScHorizontalCellIterator( pDoc
, nStartTab
, nStartCol
,
1976 nStartRow
, nEndCol
, nEndRow
);
1979 ScHorizontalValueIterator::~ScHorizontalValueIterator()
1984 bool ScHorizontalValueIterator::GetNext( double& rValue
, sal_uInt16
& rErr
)
1986 bool bFound
= false;
1989 ScRefCellValue
* pCell
= pCellIter
->GetNext( nCurCol
, nCurRow
);
1992 if ( nCurTab
< nEndTab
)
1994 pCellIter
->SetTab( ++nCurTab
);
1995 pCell
= pCellIter
->GetNext( nCurCol
, nCurRow
);
2000 if ( !bSubTotal
|| !pDoc
->maTabs
[nCurTab
]->RowFiltered( nCurRow
) )
2002 switch (pCell
->meType
)
2004 case CELLTYPE_VALUE
:
2007 rValue
= pCell
->mfValue
;
2011 ScColumn
* pCol
= &pDoc
->maTabs
[nCurTab
]->aCol
[nCurCol
];
2012 ScAttrArray_IterGetNumberFormat( nNumFormat
, pAttrArray
,
2013 nAttrEndRow
, pCol
->pAttrArray
, nCurRow
, pDoc
);
2014 rValue
= pDoc
->RoundValueAsShown( rValue
, nNumFormat
);
2019 case CELLTYPE_FORMULA
:
2021 if (!bSubTotal
|| !pCell
->mpFormula
->IsSubTotal())
2023 rErr
= pCell
->mpFormula
->GetErrCode();
2024 if (rErr
|| pCell
->mpFormula
->IsValue())
2026 rValue
= pCell
->mpFormula
->GetValue();
2030 else if ( bTextAsZero
)
2039 case CELLTYPE_STRING
:
2040 case CELLTYPE_EDIT
:
2046 nNumFmtType
= NUMBERFORMAT_NUMBER
;
2053 default: ; // nothing
2060 //-------------------------------------------------------------------------------
2062 ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument
* pDocument
, SCTAB nTable
,
2063 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) :
2071 if (nTab
>= pDoc
->GetTableCount())
2072 OSL_FAIL("try to access index out of bounds, FIX IT");
2073 OSL_ENSURE( pDoc
->maTabs
[nTab
], "Table does not exist" );
2081 pIndices
= new SCSIZE
[nEndCol
-nStartCol
+1];
2082 pNextEnd
= new SCROW
[nEndCol
-nStartCol
+1];
2083 ppPatterns
= new const ScPatternAttr
*[nEndCol
-nStartCol
+1];
2085 SCROW nSkipTo
= MAXROW
;
2087 for (i
=nStartCol
; i
<=nEndCol
; i
++)
2089 SCCOL nPos
= i
- nStartCol
;
2090 const ScAttrArray
* pArray
= pDoc
->maTabs
[nTab
]->aCol
[i
].pAttrArray
;
2091 OSL_ENSURE( pArray
, "pArray == 0" );
2094 pArray
->Search( nStartRow
, nIndex
);
2096 const ScPatternAttr
* pPattern
= pArray
->pData
[nIndex
].pPattern
;
2097 SCROW nThisEnd
= pArray
->pData
[nIndex
].nRow
;
2098 if ( IsDefaultItem( pPattern
) )
2101 if ( nThisEnd
< nSkipTo
)
2102 nSkipTo
= nThisEnd
; // nSkipTo can be set here already
2105 bEmpty
= false; // Found attributes
2107 pIndices
[nPos
] = nIndex
;
2108 pNextEnd
[nPos
] = nThisEnd
;
2109 ppPatterns
[nPos
] = pPattern
;
2113 nRow
= nSkipTo
; // Skip until end of next section
2118 ScHorizontalAttrIterator::~ScHorizontalAttrIterator()
2120 delete[] ppPatterns
;
2125 const ScPatternAttr
* ScHorizontalAttrIterator::GetNext( SCCOL
& rCol1
, SCCOL
& rCol2
, SCROW
& rRow
)
2127 if (nTab
>= pDoc
->GetTableCount())
2128 OSL_FAIL("try to access index out of bounds, FIX IT");
2133 // Search in this row
2134 while ( nCol
<= nEndCol
&& !ppPatterns
[nCol
-nStartCol
] )
2137 if ( nCol
<= nEndCol
)
2139 const ScPatternAttr
* pPat
= ppPatterns
[nCol
-nStartCol
];
2142 while ( nCol
< nEndCol
&& ppPatterns
[nCol
+1-nStartCol
] == pPat
)
2145 ++nCol
; // Count up for next call
2146 return pPat
; // Found it!
2152 if ( nRow
> nEndRow
) // Already at the end?
2153 return NULL
; // Found nothing
2158 for ( i
= nStartCol
; i
<= nEndCol
; i
++)
2160 SCCOL nPos
= i
-nStartCol
;
2161 if ( pNextEnd
[nPos
] < nRow
)
2163 const ScAttrArray
* pArray
= pDoc
->maTabs
[nTab
]->aCol
[i
].pAttrArray
;
2165 SCSIZE nIndex
= ++pIndices
[nPos
];
2166 if ( nIndex
< pArray
->nCount
)
2168 const ScPatternAttr
* pPattern
= pArray
->pData
[nIndex
].pPattern
;
2169 SCROW nThisEnd
= pArray
->pData
[nIndex
].nRow
;
2170 if ( IsDefaultItem( pPattern
) )
2173 bEmpty
= false; // Found attributes
2175 pNextEnd
[nPos
] = nThisEnd
;
2176 ppPatterns
[nPos
] = pPattern
;
2178 OSL_ENSURE( pNextEnd
[nPos
] >= nRow
, "Sequence out of order" );
2182 OSL_FAIL("AttrArray does not range to MAXROW");
2183 pNextEnd
[nPos
] = MAXROW
;
2184 ppPatterns
[nPos
] = NULL
;
2187 else if ( ppPatterns
[nPos
] )
2188 bEmpty
= false; // Area not at the end yet
2193 SCCOL nCount
= nEndCol
-nStartCol
+1;
2194 SCROW nSkipTo
= pNextEnd
[0]; // Search next end of area
2195 for (i
=1; i
<nCount
; i
++)
2196 if ( pNextEnd
[i
] < nSkipTo
)
2197 nSkipTo
= pNextEnd
[i
];
2198 nRow
= nSkipTo
; // Skip empty rows
2201 nCol
= nStartCol
; // Start at the left again
2205 //-------------------------------------------------------------------------------
2207 inline bool IsGreater( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
2209 return ( nRow1
> nRow2
) || ( nRow1
== nRow2
&& nCol1
> nCol2
);
2212 ScUsedAreaIterator::ScUsedAreaIterator( ScDocument
* pDocument
, SCTAB nTable
,
2213 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) :
2214 aCellIter( pDocument
, nTable
, nCol1
, nRow1
, nCol2
, nRow2
),
2215 aAttrIter( pDocument
, nTable
, nCol1
, nRow1
, nCol2
, nRow2
),
2219 pCell
= aCellIter
.GetNext( nCellCol
, nCellRow
);
2220 pPattern
= aAttrIter
.GetNext( nAttrCol1
, nAttrCol2
, nAttrRow
);
2223 ScUsedAreaIterator::~ScUsedAreaIterator()
2227 bool ScUsedAreaIterator::GetNext()
2229 // Forward iterators
2230 if ( pCell
&& IsGreater( nNextCol
, nNextRow
, nCellCol
, nCellRow
) )
2231 pCell
= aCellIter
.GetNext( nCellCol
, nCellRow
);
2233 while (pCell
&& pCell
->isEmpty())
2234 pCell
= aCellIter
.GetNext( nCellCol
, nCellRow
);
2236 if ( pPattern
&& IsGreater( nNextCol
, nNextRow
, nAttrCol2
, nAttrRow
) )
2237 pPattern
= aAttrIter
.GetNext( nAttrCol1
, nAttrCol2
, nAttrRow
);
2239 if ( pPattern
&& nAttrRow
== nNextRow
&& nAttrCol1
< nNextCol
)
2240 nAttrCol1
= nNextCol
;
2244 bool bUseCell
= false;
2246 if ( pCell
&& pPattern
)
2248 if ( IsGreater( nCellCol
, nCellRow
, nAttrCol1
, nAttrRow
) ) // Only attributes at the beginning?
2250 maFoundCell
.clear();
2251 pFoundPattern
= pPattern
;
2252 nFoundRow
= nAttrRow
;
2253 nFoundStartCol
= nAttrCol1
;
2254 if ( nCellRow
== nAttrRow
&& nCellCol
<= nAttrCol2
) // Area also contains cell?
2255 nFoundEndCol
= nCellCol
- 1; // Only until right before the cell
2257 nFoundEndCol
= nAttrCol2
; // Everything
2262 if ( nAttrRow
== nCellRow
&& nAttrCol1
== nCellCol
) // Attributes on the cell?
2263 pFoundPattern
= pPattern
;
2265 pFoundPattern
= NULL
;
2268 else if ( pCell
) // Just a cell -> take over right away
2270 pFoundPattern
= NULL
;
2271 bUseCell
= true; // Cell position
2273 else if ( pPattern
) // Just attributes -> take over right away
2275 maFoundCell
.clear();
2276 pFoundPattern
= pPattern
;
2277 nFoundRow
= nAttrRow
;
2278 nFoundStartCol
= nAttrCol1
;
2279 nFoundEndCol
= nAttrCol2
;
2284 if ( bUseCell
) // Cell position
2287 maFoundCell
= *pCell
;
2289 maFoundCell
.clear();
2291 nFoundRow
= nCellRow
;
2292 nFoundStartCol
= nFoundEndCol
= nCellCol
;
2297 nNextRow
= nFoundRow
;
2298 nNextCol
= nFoundEndCol
+ 1;
2304 const ScRefCellValue
& ScUsedAreaIterator::GetCell() const
2309 //-------------------------------------------------------------------------------
2311 ScDocAttrIterator::ScDocAttrIterator(ScDocument
* pDocument
, SCTAB nTable
,
2312 SCCOL nCol1
, SCROW nRow1
,
2313 SCCOL nCol2
, SCROW nRow2
) :
2321 if ( ValidTab(nTab
) && nTab
< pDoc
->GetTableCount() && pDoc
->maTabs
[nTab
] )
2322 pColIter
= pDoc
->maTabs
[nTab
]->aCol
[nCol
].CreateAttrIterator( nStartRow
, nEndRow
);
2327 ScDocAttrIterator::~ScDocAttrIterator()
2332 const ScPatternAttr
* ScDocAttrIterator::GetNext( SCCOL
& rCol
, SCROW
& rRow1
, SCROW
& rRow2
)
2336 const ScPatternAttr
* pPattern
= pColIter
->Next( rRow1
, rRow2
);
2345 if ( nCol
<= nEndCol
)
2346 pColIter
= pDoc
->maTabs
[nTab
]->aCol
[nCol
].CreateAttrIterator( nStartRow
, nEndRow
);
2350 return NULL
; // Nothing anymore
2353 // ============================================================================
2355 ScDocRowHeightUpdater::TabRanges::TabRanges(SCTAB nTab
) :
2356 mnTab(nTab
), mpRanges(new ScFlatBoolRowSegments
)
2360 ScDocRowHeightUpdater::ScDocRowHeightUpdater(ScDocument
& rDoc
, OutputDevice
* pOutDev
, double fPPTX
, double fPPTY
, const vector
<TabRanges
>* pTabRangesArray
) :
2361 mrDoc(rDoc
), mpOutDev(pOutDev
), mfPPTX(fPPTX
), mfPPTY(fPPTY
), mpTabRangesArray(pTabRangesArray
)
2365 void ScDocRowHeightUpdater::update()
2367 if (!mpTabRangesArray
|| mpTabRangesArray
->empty())
2369 // No ranges defined. Update all rows in all tables.
2374 sal_uInt32 nCellCount
= 0;
2375 vector
<TabRanges
>::const_iterator itr
= mpTabRangesArray
->begin(), itrEnd
= mpTabRangesArray
->end();
2376 for (; itr
!= itrEnd
; ++itr
)
2378 ScFlatBoolRowSegments::RangeData aData
;
2379 ScFlatBoolRowSegments::RangeIterator
aRangeItr(*itr
->mpRanges
);
2380 for (bool bFound
= aRangeItr
.getFirst(aData
); bFound
; bFound
= aRangeItr
.getNext(aData
))
2385 nCellCount
+= aData
.mnRow2
- aData
.mnRow1
+ 1;
2389 ScProgress
aProgress(mrDoc
.GetDocumentShell(), ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING
), nCellCount
);
2391 Fraction
aZoom(1, 1);
2392 itr
= mpTabRangesArray
->begin();
2393 sal_uInt32 nProgressStart
= 0;
2394 for (; itr
!= itrEnd
; ++itr
)
2396 SCTAB nTab
= itr
->mnTab
;
2397 if (!ValidTab(nTab
) || nTab
>= mrDoc
.GetTableCount() || !mrDoc
.maTabs
[nTab
])
2400 ScFlatBoolRowSegments::RangeData aData
;
2401 ScFlatBoolRowSegments::RangeIterator
aRangeItr(*itr
->mpRanges
);
2402 for (bool bFound
= aRangeItr
.getFirst(aData
); bFound
; bFound
= aRangeItr
.getNext(aData
))
2407 mrDoc
.maTabs
[nTab
]->SetOptimalHeight(
2408 aData
.mnRow1
, aData
.mnRow2
, 0, mpOutDev
, mfPPTX
, mfPPTY
, aZoom
, aZoom
, false, &aProgress
, nProgressStart
);
2410 nProgressStart
+= aData
.mnRow2
- aData
.mnRow1
+ 1;
2415 void ScDocRowHeightUpdater::updateAll()
2417 sal_uInt32 nCellCount
= 0;
2418 for (SCTAB nTab
= 0; nTab
< mrDoc
.GetTableCount(); ++nTab
)
2420 if (!ValidTab(nTab
) || !mrDoc
.maTabs
[nTab
])
2423 nCellCount
+= mrDoc
.maTabs
[nTab
]->GetWeightedCount();
2426 ScProgress
aProgress(mrDoc
.GetDocumentShell(), ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING
), nCellCount
);
2428 Fraction
aZoom(1, 1);
2429 sal_uLong nProgressStart
= 0;
2430 for (SCTAB nTab
= 0; nTab
< mrDoc
.GetTableCount(); ++nTab
)
2432 if (!ValidTab(nTab
) || !mrDoc
.maTabs
[nTab
])
2435 mrDoc
.maTabs
[nTab
]->SetOptimalHeight(
2436 0, MAXROW
, 0, mpOutDev
, mfPPTX
, mfPPTY
, aZoom
, aZoom
, false, &aProgress
, nProgressStart
);
2438 nProgressStart
+= mrDoc
.maTabs
[nTab
]->GetWeightedCount();
2442 //-------------------------------------------------------------------------------
2444 ScAttrRectIterator::ScAttrRectIterator(ScDocument
* pDocument
, SCTAB nTable
,
2445 SCCOL nCol1
, SCROW nRow1
,
2446 SCCOL nCol2
, SCROW nRow2
) :
2452 nIterStartCol( nCol1
),
2453 nIterEndCol( nCol1
)
2455 if ( ValidTab(nTab
) && nTab
< pDoc
->GetTableCount() && pDoc
->maTabs
[nTab
] )
2457 pColIter
= pDoc
->maTabs
[nTab
]->aCol
[nIterStartCol
].CreateAttrIterator( nStartRow
, nEndRow
);
2458 while ( nIterEndCol
< nEndCol
&&
2459 pDoc
->maTabs
[nTab
]->aCol
[nIterEndCol
].IsAllAttrEqual(
2460 pDoc
->maTabs
[nTab
]->aCol
[nIterEndCol
+1], nStartRow
, nEndRow
) )
2467 ScAttrRectIterator::~ScAttrRectIterator()
2472 void ScAttrRectIterator::DataChanged()
2476 SCROW nNextRow
= pColIter
->GetNextRow();
2478 pColIter
= pDoc
->maTabs
[nTab
]->aCol
[nIterStartCol
].CreateAttrIterator( nNextRow
, nEndRow
);
2482 const ScPatternAttr
* ScAttrRectIterator::GetNext( SCCOL
& rCol1
, SCCOL
& rCol2
,
2483 SCROW
& rRow1
, SCROW
& rRow2
)
2487 const ScPatternAttr
* pPattern
= pColIter
->Next( rRow1
, rRow2
);
2490 rCol1
= nIterStartCol
;
2491 rCol2
= nIterEndCol
;
2496 nIterStartCol
= nIterEndCol
+1;
2497 if ( nIterStartCol
<= nEndCol
)
2499 nIterEndCol
= nIterStartCol
;
2500 pColIter
= pDoc
->maTabs
[nTab
]->aCol
[nIterStartCol
].CreateAttrIterator( nStartRow
, nEndRow
);
2501 while ( nIterEndCol
< nEndCol
&&
2502 pDoc
->maTabs
[nTab
]->aCol
[nIterEndCol
].IsAllAttrEqual(
2503 pDoc
->maTabs
[nTab
]->aCol
[nIterEndCol
+1], nStartRow
, nEndRow
) )
2509 return NULL
; // Nothing anymore
2512 // ============================================================================
2514 SCROW
ScRowBreakIterator::NOT_FOUND
= -1;
2516 ScRowBreakIterator::ScRowBreakIterator(set
<SCROW
>& rBreaks
) :
2518 maItr(rBreaks
.begin()), maEnd(rBreaks
.end())
2522 SCROW
ScRowBreakIterator::first()
2524 maItr
= mrBreaks
.begin();
2525 return maItr
== maEnd
? NOT_FOUND
: *maItr
;
2528 SCROW
ScRowBreakIterator::next()
2531 return maItr
== maEnd
? NOT_FOUND
: *maItr
;
2534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */