Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / data / dociter.cxx
blob0fd07c716608e5aceb3250f485c092fc341d4954
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 <svl/zforlist.hxx>
22 #include "scitems.hxx"
23 #include "global.hxx"
24 #include "dociter.hxx"
25 #include "document.hxx"
26 #include "table.hxx"
27 #include "column.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"
46 #include <vector>
48 using ::rtl::math::approxEqual;
49 using ::std::vector;
50 using ::std::set;
52 // STATIC DATA -----------------------------------------------------------
54 namespace {
56 template<typename _Iter>
57 void incBlock(std::pair<_Iter, size_t>& rPos)
59 // Move to the next block.
60 ++rPos.first;
61 rPos.second = 0;
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.
69 ++rPos.second;
70 else
71 incBlock(rPos);
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,
84 ScDocument* pDoc )
86 if ( rpArr != pNewArr || nAttrEndRow < nRow )
88 SCROW nRowStart = 0;
89 SCROW nRowEnd = MAXROW;
90 const ScPatternAttr* pPattern;
91 if( !(pPattern = pNewArr->GetPatternRange( nRowStart, nRowEnd, nRow ) ) )
93 pPattern = pDoc->GetDefPattern();
94 nRowEnd = MAXROW;
97 nFormat = pPattern->GetNumberFormat( pDoc->GetFormatTable() );
98 rpArr = pNewArr;
99 nAttrEndRow = nRowEnd;
103 ScValueIterator::ScValueIterator( ScDocument* pDocument, const ScRange& rRange,
104 bool bSTotal, bool bTextZero ) :
105 pDoc( pDocument ),
106 nNumFmtIndex(0),
107 maStartPos(rRange.aStart),
108 maEndPos(rRange.aEnd),
109 nNumFmtType( NUMBERFORMAT_UNDEFINED ),
110 bNumValid( false ),
111 bSubTotal(bSTotal),
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
125 pAttrArray = 0;
126 nAttrEndRow = 0;
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()
138 ++maCurPos.first;
139 maCurPos.second = 0;
142 void ScValueIterator::IncPos()
144 if (maCurPos.second + 1 < maCurPos.first->size)
145 // Move within the same block.
146 ++maCurPos.second;
147 else
148 // Move to the next block.
149 IncBlock();
152 void ScValueIterator::SetPos(size_t nPos)
154 maCurPos = mpCells->position(maCurPos.first, nPos);
157 bool ScValueIterator::GetThis(double& rValue, sal_uInt16& rErr)
159 while (true)
161 bool bNextColumn = maCurPos.first == mpCells->end();
162 if (!bNextColumn)
164 if (GetRow() > maEndPos.Row())
165 bNextColumn = true;
168 ScColumn* pCol = NULL;
169 if (bNextColumn)
171 // Find the next available column.
174 ++mnCol;
175 if (mnCol > maEndPos.Col())
177 mnCol = maStartPos.Col();
178 ++mnTab;
179 if (mnTab > maEndPos.Tab())
181 rErr = 0;
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();
194 SCROW nLastRow;
195 if (bSubTotal && pDoc->maTabs[mnTab]->RowFiltered(nCurRow, NULL, &nLastRow))
197 // Skip all filtered rows for subtotal mode.
198 SetPos(nLastRow+1);
199 continue;
202 switch (maCurPos.first->type)
204 case sc::element_type_numeric:
206 bNumValid = false;
207 rValue = sc::numeric_block::at(*maCurPos.first->data, maCurPos.second);
208 rErr = 0;
209 if (bCalcAsShown)
211 ScAttrArray_IterGetNumberFormat(nNumFormat, pAttrArray,
212 nAttrEndRow, pCol->pAttrArray, nCurRow, pDoc);
213 rValue = pDoc->RoundValueAsShown(rValue, nNumFormat);
215 return true; // Found it!
217 break;
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.
224 IncPos();
225 break;
228 if (rCell.GetErrorOrValue(rErr, rValue))
230 bNumValid = false;
231 return true; // Found it!
233 else if (bTextAsZero)
235 rValue = 0.0;
236 bNumValid = false;
237 return true;
239 IncPos();
241 break;
242 case sc::element_type_string :
243 case sc::element_type_edittext :
245 if (bTextAsZero)
247 rErr = 0;
248 rValue = 0.0;
249 nNumFmtType = NUMBERFORMAT_NUMBER;
250 nNumFmtIndex = 0;
251 bNumValid = true;
252 return true;
254 IncBlock();
256 break;
257 case sc::element_type_empty:
258 default:
259 // Skip the whole block.
260 IncBlock();
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 );
273 bNumValid = true;
276 nType = nNumFmtType;
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);
286 if (!pTab)
287 return false;
289 nNumFormat = 0; // Initialized in GetNumberFormat
290 pAttrArray = 0;
291 nAttrEndRow = 0;
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)
300 IncPos();
301 return GetThis(rValue, rErr);
304 // ============================================================================
306 ScDBQueryDataIterator::DataAccess::DataAccess(const ScDBQueryDataIterator* pParent) :
307 mpParent(pParent)
311 ScDBQueryDataIterator::DataAccess::~DataAccess()
315 const sc::CellStoreType* ScDBQueryDataIterator::GetColumnCellStore(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
317 ScTable* pTab = rDoc.FetchTable(nTab);
318 if (!pTab)
319 return NULL;
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) :
343 DataAccess(pParent),
344 mpCells(NULL),
345 mpParam(pParam),
346 mpDoc(pDoc),
347 bCalcAsShown( pDoc->GetDocOptions().IsCalcAsShown() )
349 nCol = mpParam->mnField;
350 nRow = mpParam->nRow1;
351 nTab = mpParam->nTab;
352 SCSIZE i;
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();
358 rItems.resize(1);
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
366 pAttrArray = 0;
367 nAttrEndRow = 0;
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;
384 while (true)
386 if (maCurPos.first == mpCells->end() || nRow > mpParam->nRow2)
388 // Bottom of the range reached. Bail out.
389 rValue.mnError = 0;
390 return false;
393 if (maCurPos.first->type == sc::element_type_empty)
395 // Skip the whole empty block.
396 incBlock();
397 continue;
400 ScRefCellValue* pCell = NULL;
401 if (nCol == static_cast<SCCOL>(nFirstQueryField))
403 aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
404 pCell = &aCell;
407 if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, *mpParam, nTab, nRow, pCell))
409 if (!pCell)
410 aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
411 switch (aCell.meType)
413 case CELLTYPE_VALUE:
415 rValue.mfValue = aCell.mfValue;
416 rValue.mbIsNumber = true;
417 if ( bCalcAsShown )
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;
426 nNumFmtIndex = 0;
427 rValue.mnError = 0;
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)
443 incPos();
444 else
446 rValue.maString = aCell.mpFormula->GetString().getString();
447 rValue.mfValue = 0.0;
448 rValue.mnError = aCell.mpFormula->GetErrCode();
449 rValue.mbIsNumber = false;
450 return true;
453 break;
454 case CELLTYPE_STRING:
455 case CELLTYPE_EDIT:
456 if (mpParam->mbSkipString)
457 incPos();
458 else
460 rValue.maString = aCell.getString(mpDoc);
461 rValue.mfValue = 0.0;
462 rValue.mnError = 0;
463 rValue.mbIsNumber = false;
464 return true;
466 break;
467 default:
468 incPos();
471 else
472 incPos();
474 // statement unreachable
477 bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue)
479 if (mpParam->bHasHeader)
480 ++nRow;
482 mpCells = ScDBQueryDataIterator::GetColumnCellStore(*mpDoc, nTab, nCol);
483 if (!mpCells)
484 return false;
486 maCurPos = mpCells->position(nRow);
487 return getCurrent(rValue);
490 bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue)
492 if (!mpCells || maCurPos.first == mpCells->end())
493 return false;
495 incPos();
496 return getCurrent(rValue);
499 void ScDBQueryDataIterator::DataAccessInternal::incBlock()
501 ++maCurPos.first;
502 maCurPos.second = 0;
504 nRow = maCurPos.first->position;
507 void ScDBQueryDataIterator::DataAccessInternal::incPos()
509 if (maCurPos.second + 1 < maCurPos.first->size)
511 // Move within the same block.
512 ++maCurPos.second;
513 ++nRow;
515 else
516 // Move to the next block.
517 incBlock();
520 void ScDBQueryDataIterator::DataAccessInternal::setPos(size_t nPos)
522 maCurPos = mpCells->position(maCurPos.first, nPos);
523 nRow = nPos;
526 // ----------------------------------------------------------------------------
528 ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(const ScDBQueryDataIterator* pParent, ScDBQueryParamMatrix* pParam) :
529 DataAccess(pParent),
530 mpParam(pParam)
532 SCSIZE nC, nR;
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
545 // query parameters.
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.
551 continue;
553 bool bIsStrVal = rMat.IsString(mpParam->mnField, mnCurRow);
554 if (bIsStrVal && mpParam->mbSkipString)
555 continue;
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;
562 rValue.mnError = 0;
563 return true;
566 return false;
569 bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value& rValue)
571 mnCurRow = mpParam->bHasHeader ? 1 : 0;
572 return getCurrent(rValue);
575 bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value& rValue)
577 ++mnCurRow;
578 return getCurrent(rValue);
581 namespace {
583 bool isQueryByValue(const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
585 if (rItem.meType == ScQueryEntry::ByString)
586 return false;
588 if (!rMat.IsValueOrEmpty(nCol, nRow))
589 return false;
591 return true;
594 bool isQueryByString(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
596 switch (rEntry.eOp)
598 case SC_EQUAL:
599 case SC_NOT_EQUAL:
600 case SC_CONTAINS:
601 case SC_DOES_NOT_CONTAIN:
602 case SC_BEGINS_WITH:
603 case SC_ENDS_WITH:
604 case SC_DOES_NOT_BEGIN_WITH:
605 case SC_DOES_NOT_END_WITH:
606 return true;
607 default:
611 if (rItem.meType == ScQueryEntry::ByString && rMat.IsString(nCol, nRow))
612 return true;
614 return false;
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)
633 continue;
635 switch (rEntry.eOp)
637 case SC_EQUAL:
638 case SC_LESS:
639 case SC_GREATER:
640 case SC_LESS_EQUAL:
641 case SC_GREATER_EQUAL:
642 case SC_NOT_EQUAL:
643 break;
644 default:
645 // Only the above operators are supported.
646 continue;
649 bool bValid = false;
651 SCSIZE nField = static_cast<SCSIZE>(rEntry.nField);
652 if (isQueryByValue(rItem, rMat, nField, nRow))
654 // By value
655 double fMatVal = rMat.GetDouble(nField, nRow);
656 bool bEqual = approxEqual(fMatVal, rItem.mfVal);
657 switch (rEntry.eOp)
659 case SC_EQUAL:
660 bValid = bEqual;
661 break;
662 case SC_LESS:
663 bValid = (fMatVal < rItem.mfVal) && !bEqual;
664 break;
665 case SC_GREATER:
666 bValid = (fMatVal > rItem.mfVal) && !bEqual;
667 break;
668 case SC_LESS_EQUAL:
669 bValid = (fMatVal < rItem.mfVal) || bEqual;
670 break;
671 case SC_GREATER_EQUAL:
672 bValid = (fMatVal > rItem.mfVal) || bEqual;
673 break;
674 case SC_NOT_EQUAL:
675 bValid = !bEqual;
676 break;
677 default:
681 else if (isQueryByString(rEntry, rItem, rMat, nField, nRow))
683 // By string
686 // Equality check first.
687 svl::SharedString aMatStr = rMat.GetString(nField, nRow);
688 svl::SharedString aQueryStr = rEntry.GetQueryItem().maString;
689 bool bDone = false;
690 rtl_uString* p1 = mpParam->bCaseSens ? aMatStr.getData() : aMatStr.getDataIgnoreCase();
691 rtl_uString* p2 = mpParam->bCaseSens ? aQueryStr.getData() : aQueryStr.getDataIgnoreCase();
692 switch (rEntry.eOp)
694 case SC_EQUAL:
695 bValid = (p1 == p2);
696 bDone = true;
697 break;
698 case SC_NOT_EQUAL:
699 bValid = (p1 != p2);
700 bDone = true;
701 break;
702 default:
706 if (bDone)
707 break;
709 // Unequality check using collator.
710 sal_Int32 nCompare = rCollator.compareString(aMatStr.getString(), aQueryStr.getString());
711 switch (rEntry.eOp)
713 case SC_LESS :
714 bValid = (nCompare < 0);
715 break;
716 case SC_GREATER :
717 bValid = (nCompare > 0);
718 break;
719 case SC_LESS_EQUAL :
720 bValid = (nCompare <= 0);
721 break;
722 case SC_GREATER_EQUAL :
723 bValid = (nCompare >= 0);
724 break;
725 default:
729 while (false);
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;
741 else
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)
749 if (*itr)
750 return true;
752 return false;
755 // ----------------------------------------------------------------------------
757 ScDBQueryDataIterator::Value::Value() :
758 mnError(0), mbIsNumber(true)
760 ::rtl::math::setNan(&mfValue);
763 // ----------------------------------------------------------------------------
765 ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryParamBase* pParam) :
766 mpParam (pParam)
768 switch (mpParam->GetType())
770 case ScDBQueryParamBase::INTERNAL:
772 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pParam);
773 mpData.reset(new DataAccessInternal(this, p, pDocument));
775 break;
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 ) :
795 mpDoc(pDoc),
796 maStartPos(rRange.aStart),
797 maEndPos(rRange.aEnd),
798 mbSubTotal(bSTotal)
800 init();
803 void ScCellIterator::incBlock()
805 ++maCurColPos.first;
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;
817 maCurPos.IncRow();
819 else
820 // Move to the next block.
821 incBlock();
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();
868 while (true)
870 bool bNextColumn = maCurColPos.first == pCol->maCells.end();
871 if (!bNextColumn)
873 if (maCurPos.Row() > maEndPos.Row())
874 bNextColumn = true;
877 if (bNextColumn)
879 // Move to the next column.
880 maCurPos.SetRow(maStartPos.Row());
883 maCurPos.IncCol();
884 if (maCurPos.Col() > maEndPos.Col())
886 maCurPos.SetCol(maStartPos.Col());
887 maCurPos.IncTab();
888 if (maCurPos.Tab() > maEndPos.Tab())
890 maCurCell.clear();
891 return false; // Over and out
894 pCol = getColumn();
896 while (pCol->IsEmptyData());
898 maCurColPos = pCol->maCells.position(maCurPos.Row());
901 if (maCurColPos.first->type == sc::element_type_empty)
903 incBlock();
904 continue;
907 SCROW nLastRow;
908 if (mbSubTotal && pCol->GetDoc().maTabs[maCurPos.Tab()]->RowFiltered(maCurPos.Row(), NULL, &nLastRow))
910 // Skip all filtered rows for subtotal mode.
911 setPos(nLastRow+1);
912 continue;
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.
921 incPos();
922 continue;
926 maCurCell = sc::toRefCell(maCurColPos.first, maCurColPos.second);
927 return true;
929 return false;
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
964 ScCellValue aRet;
965 aRet.meType = maCurCell.meType;
967 switch (maCurCell.meType)
969 case CELLTYPE_STRING:
970 aRet.mpString = new svl::SharedString(*maCurCell.mpString);
971 break;
972 case CELLTYPE_EDIT:
973 aRet.mpEditText = maCurCell.mpEditText->Clone();
974 break;
975 case CELLTYPE_VALUE:
976 aRet.mfValue = maCurCell.mfValue;
977 break;
978 case CELLTYPE_FORMULA:
979 aRet.mpFormula = maCurCell.mpFormula->Clone();
980 break;
981 default:
985 return aRet;
988 const ScRefCellValue& ScCellIterator::getRefCellValue() const
990 return maCurCell;
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())
1006 return true;
1008 if (maCurCell.meType == CELLTYPE_FORMULA)
1009 return maCurCell.mpFormula->IsEmpty();
1011 return false;
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()))
1029 return false;
1031 maCurPos = maStartPos;
1032 const ScColumn* pCol = getColumn();
1034 maCurColPos = pCol->maCells.position(maCurPos.Row());
1035 return getCurrent();
1038 bool ScCellIterator::next()
1040 incPos();
1041 return getCurrent();
1044 //-------------------------------------------------------------------------------
1046 ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable,
1047 const ScQueryParam& rParam, bool bMod ) :
1048 mpParam(new ScQueryParam(rParam)),
1049 pDoc( pDocument ),
1050 nTab( nTable),
1051 nStopOnMismatch( nStopOnMismatchDisabled ),
1052 nTestEqualCondition( nTestEqualConditionDisabled ),
1053 bAdvanceQuery( false ),
1054 bIgnoreMismatchOnLeadingStrings( false )
1056 nCol = mpParam->nCol1;
1057 nRow = mpParam->nRow1;
1058 SCSIZE i;
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
1073 pAttrArray = 0;
1074 nAttrEndRow = 0;
1077 void ScQueryCellIterator::InitPos()
1079 nRow = mpParam->nRow1;
1080 if (mpParam->bHasHeader && mpParam->bByRow)
1081 ++nRow;
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.
1091 ++maCurPos.second;
1092 ++nRow;
1094 else
1095 // Move to the next block.
1096 IncBlock();
1099 void ScQueryCellIterator::IncBlock()
1101 ++maCurPos.first;
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];
1123 while (true)
1125 bool bNextColumn = maCurPos.first == pCol->maCells.end();
1126 if (!bNextColumn)
1128 if (nRow > mpParam->nRow2)
1129 bNextColumn = true;
1132 if (bNextColumn)
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());
1147 InitPos();
1149 bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
1150 !mpParam->bHasHeader && rItem.meType == ScQueryEntry::ByString &&
1151 mpParam->bByRow;
1154 if (maCurPos.first->type == sc::element_type_empty)
1156 IncBlock();
1157 continue;
1160 ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
1162 if (bAllStringIgnore && aCell.hasString())
1163 IncPos();
1164 else
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;
1184 return false;
1186 bool bStop;
1187 if (bFirstStringIgnore)
1189 if (aCell.hasString())
1191 IncPos();
1192 bStop = false;
1194 else
1195 bStop = true;
1197 else
1198 bStop = true;
1199 if (bStop)
1201 nStopOnMismatch |= nStopOnMismatchOccurred;
1202 return false;
1205 else
1206 IncPos();
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;
1217 InitPos();
1218 return GetThis();
1221 bool ScQueryCellIterator::GetNext()
1223 IncPos();
1224 if ( nStopOnMismatch )
1225 nStopOnMismatch = nStopOnMismatchEnabled;
1226 if ( nTestEqualCondition )
1227 nTestEqualCondition = nTestEqualConditionEnabled;
1228 return GetThis();
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 )
1240 rEntry.nField++;
1241 else
1243 OSL_FAIL( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" );
1246 else
1247 break; // for
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.
1259 struct BoolResetter
1261 bool& mr;
1262 bool mb;
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;
1276 if (bBinary)
1278 if (BinarySearch())
1280 // BinarySearch() already positions correctly and only needs real
1281 // query comparisons afterwards, skip the verification check below.
1282 mpParam->mbRangeLookup = false;
1283 bFound = GetThis();
1286 else
1288 bFound = GetFirst();
1290 if (bFound)
1292 // First equal entry or last smaller than (greater than) entry.
1293 PositionType aPosSave;
1294 bool bNext = false;
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;
1310 nCol = nFoundCol;
1311 nRow = nFoundRow;
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;
1330 else
1332 assert(!"FindEqualOrSortedLastInRange: rEntry.nField -= nColDiff < 0");
1335 else
1336 break; // for
1339 // Check it.
1340 if (!GetThis())
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;
1362 break;
1363 default:
1365 // added to avoid warnings
1369 else
1370 break; // for
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
1383 nCol = nFoundCol;
1384 nRow = nFoundRow;
1385 maCurPos = aPosSave;
1386 return true;
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;
1404 break;
1405 default:
1407 // added to avoid warnings
1411 else
1412 break; // for
1414 SetStopOnMismatch( false );
1415 SetTestEqualCondition( false );
1416 if (GetNext())
1418 // Last of a consecutive area, avoid searching the entire parameter
1419 // range as it is a real performance bottleneck in case of regular
1420 // expressions.
1421 PositionType aPosSave;
1424 nFoundCol = GetCol();
1425 nFoundRow = GetRow();
1426 aPosSave = maCurPos;
1427 SetStopOnMismatch( true );
1428 } while (GetNext());
1429 nCol = nFoundCol;
1430 nRow = nFoundRow;
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())
1447 return false;
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)
1465 nRow++;
1467 aLoPos = pCol->maCells.position(nRow);
1468 if (bFirstStringIgnore && aLoPos.first->type == sc::element_type_string)
1470 OUString aCellStr;
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.
1480 incPos(aLoPos);
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;
1493 else
1494 // Move to the next block.
1495 incBlock(aLoPos);
1498 if (toLogicalPos(aLoPos) > toLogicalPos(aHiPos))
1499 // Never go past the high point.
1500 aLoPos = aHiPos;
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;
1511 if (!bLessEqual)
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));
1519 OUString aStr;
1520 ScCellFormat::GetInputString(aCell, nFormat, aStr, rFormatter, pDoc);
1521 aLastInRangeString = aStr;
1523 else
1525 switch (aCell.meType)
1527 case CELLTYPE_VALUE :
1528 fLastInRangeValue = aCell.mfValue;
1529 break;
1530 case CELLTYPE_FORMULA :
1531 fLastInRangeValue = aCell.mpFormula->GetValue();
1532 break;
1533 default:
1535 // added to avoid warnings
1541 sal_Int32 nRes = 0;
1542 bool bFound = false;
1543 bool bDone = false;
1544 size_t nLogicalLow = toLogicalPos(aLoPos), nLogicalHigh = toLogicalPos(aHiPos);
1545 while (nLogicalLow <= nLogicalHigh && !bDone)
1547 size_t nMid = (nLogicalLow+nLogicalHigh)/2;
1548 size_t i = nMid;
1549 if (i > nLogicalHigh)
1551 if (nMid > 0)
1552 nLogicalHigh = nMid - 1;
1553 else
1554 bDone = true;
1555 continue; // while
1558 bool bHaveRefCell = false;
1559 PositionType aPos = pCol->maCells.position(i);
1560 bool bStr;
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();
1567 break;
1568 case sc::element_type_string:
1569 case sc::element_type_edittext:
1570 bStr = true;
1571 break;
1572 default:
1573 bStr = false;
1574 break;
1576 nRes = 0;
1578 // compares are content<query:-1, content>query:1
1579 // Cell value comparison similar to ScTable::ValidQuery()
1580 if (!bStr && !bByString)
1582 double nCellVal;
1583 if (!bHaveRefCell)
1584 aCell = sc::toRefCell(aPos.first, aPos.second);
1585 switch (aCell.meType)
1587 case CELLTYPE_VALUE :
1588 nCellVal = aCell.mfValue;
1589 break;
1590 case CELLTYPE_FORMULA :
1591 nCellVal = aCell.mpFormula->GetValue();
1592 break;
1593 default:
1594 nCellVal = 0.0;
1596 if ((nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(
1597 nCellVal, rItem.mfVal))
1599 nRes = -1;
1600 if (bLessEqual)
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;
1611 bDone = true;
1615 else if ((nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(
1616 nCellVal, rItem.mfVal))
1618 nRes = 1;
1619 if (!bLessEqual)
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;
1630 bDone = true;
1635 else if (bStr && bByString)
1637 OUString aCellStr;
1638 sal_uLong nFormat = pCol->GetNumberFormat(i);
1639 if (!bHaveRefCell)
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,
1647 aCellStr);
1648 if (nTmp < 0)
1650 aLastInRangeString = aCellStr;
1651 aLastInRange = aPos;
1653 else if (nTmp > 0)
1655 // not strictly sorted, continue with GetThis()
1656 aLastInRange = aFirstLastInRange;
1657 bDone = true;
1660 else if (nRes > 0 && !bLessEqual)
1662 sal_Int32 nTmp = pCollator->compareString( aLastInRangeString,
1663 aCellStr);
1664 if (nTmp > 0)
1666 aLastInRangeString = aCellStr;
1667 aLastInRange = aPos;
1669 else if (nTmp < 0)
1671 // not strictly sorted, continue with GetThis()
1672 aLastInRange = aFirstLastInRange;
1673 bDone = true;
1677 else if (!bStr && bByString)
1679 nRes = -1; // numeric < string
1680 if (bLessEqual)
1681 aLastInRange = aPos;
1683 else // if (bStr && !bByString)
1685 nRes = 1; // string > numeric
1686 if (!bLessEqual)
1687 aLastInRange = aPos;
1689 if (nRes < 0)
1691 if (bLessEqual)
1692 nLogicalLow = nMid + 1;
1693 else // assumed to be SC_GREATER_EQUAL
1695 if (nMid > 0)
1696 nLogicalHigh = nMid - 1;
1697 else
1698 bDone = true;
1701 else if (nRes > 0)
1703 if (bLessEqual)
1705 if (nMid > 0)
1706 nLogicalHigh = nMid - 1;
1707 else
1708 bDone = true;
1710 else // assumed to be SC_GREATER_EQUAL
1711 nLogicalLow = nMid + 1;
1713 else
1715 aLoPos = aPos;
1716 bDone = bFound = true;
1719 if (!bFound)
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);
1735 maCurPos = aLoPos;
1736 return true;
1738 else
1740 nRow = mpParam->nRow2 + 1;
1741 // Set current position to the last possible row.
1742 maCurPos.first = pCol->maCells.end();
1743 --maCurPos.first;
1744 maCurPos.second = maCurPos.first->size - 1;
1745 return false;
1750 //-------------------------------------------------------------------------------
1752 ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable,
1753 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
1754 maColPositions(nCol2-nCol1+1),
1755 pDoc( pDocument ),
1756 mnTab( nTable ),
1757 nStartCol( nCol1 ),
1758 nEndCol( nCol2 ),
1759 nStartRow( nRow1 ),
1760 nEndRow( nRow2 ),
1761 mnCol( nCol1 ),
1762 mnRow( nRow1 ),
1763 bMore(false)
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 ];
1771 SetTab( mnTab );
1774 ScHorizontalCellIterator::~ScHorizontalCellIterator()
1776 delete [] pNextRows;
1777 delete [] pNextIndices;
1780 void ScHorizontalCellIterator::SetTab( SCTAB nTabP )
1782 bMore = false;
1783 mnTab = nTabP;
1784 mnRow = nStartRow;
1785 mnCol = nStartCol;
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)
1795 bMore = true;
1798 if (!bMore)
1799 return;
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.
1804 Advance();
1807 ScRefCellValue* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow )
1809 if (!bMore)
1810 return NULL;
1812 // Return the current non-empty cell, and move the cursor to the next one.
1813 rCol = mnCol;
1814 rRow = mnRow;
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);
1819 Advance();
1821 return &maCurCell;
1824 bool ScHorizontalCellIterator::GetPos( SCCOL& rCol, SCROW& rRow )
1826 rCol = mnCol;
1827 rRow = mnRow;
1828 return bMore;
1831 namespace {
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.
1837 return true;
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.
1844 return true;
1847 // No more blocks.
1848 return false;
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)
1860 continue;
1862 size_t nRow = static_cast<size_t>(mnRow);
1863 if (nRow < r.maPos->position)
1864 continue;
1866 if (!advanceBlock(nRow, r.maPos, r.maEnd))
1867 continue;
1869 if (r.maPos->type == sc::element_type_empty)
1870 continue;
1872 // Found in the current row.
1873 mnCol = i;
1874 bMore = true;
1875 return;
1878 // Move to the next row that has at least one non-empty cell.
1879 ++mnRow;
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.
1890 continue;
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;
1898 nNextRowPos = i;
1900 continue;
1903 if (!advanceBlock(nRow, r.maPos, r.maEnd))
1904 continue;
1906 if (r.maPos->type == sc::element_type_empty)
1908 // Empty block. Move to the next block and try next column.
1909 ++r.maPos;
1910 if (r.maPos->position < nNextRow)
1912 nNextRow = r.maPos->position;
1913 nNextRowPos = i;
1915 continue;
1918 // Found a non-empty cell block!
1919 mnCol = i + nStartCol;
1920 mnRow = nRow;
1921 bMore = true;
1922 return;
1925 if (nNextRow > static_cast<size_t>(MAXROW))
1927 // No more blocks to search.
1928 bMore = false;
1929 return;
1932 mnRow = nNextRow; // move to the next non-empty row.
1935 bMore = false;
1938 //------------------------------------------------------------------------
1940 ScHorizontalValueIterator::ScHorizontalValueIterator( ScDocument* pDocument,
1941 const ScRange& rRange, bool bSTotal, bool bTextZero ) :
1942 pDoc( pDocument ),
1943 nNumFmtIndex(0),
1944 nEndTab( rRange.aEnd.Tab() ),
1945 nNumFmtType( NUMBERFORMAT_UNDEFINED ),
1946 bNumValid( false ),
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()
1972 pAttrArray = 0;
1973 nAttrEndRow = 0;
1975 pCellIter = new ScHorizontalCellIterator( pDoc, nStartTab, nStartCol,
1976 nStartRow, nEndCol, nEndRow );
1979 ScHorizontalValueIterator::~ScHorizontalValueIterator()
1981 delete pCellIter;
1984 bool ScHorizontalValueIterator::GetNext( double& rValue, sal_uInt16& rErr )
1986 bool bFound = false;
1987 while ( !bFound )
1989 ScRefCellValue* pCell = pCellIter->GetNext( nCurCol, nCurRow );
1990 while ( !pCell )
1992 if ( nCurTab < nEndTab )
1994 pCellIter->SetTab( ++nCurTab);
1995 pCell = pCellIter->GetNext( nCurCol, nCurRow );
1997 else
1998 return false;
2000 if ( !bSubTotal || !pDoc->maTabs[nCurTab]->RowFiltered( nCurRow ) )
2002 switch (pCell->meType)
2004 case CELLTYPE_VALUE:
2006 bNumValid = false;
2007 rValue = pCell->mfValue;
2008 rErr = 0;
2009 if ( bCalcAsShown )
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 );
2016 bFound = true;
2018 break;
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();
2027 bNumValid = false;
2028 bFound = true;
2030 else if ( bTextAsZero )
2032 rValue = 0.0;
2033 bNumValid = false;
2034 bFound = true;
2038 break;
2039 case CELLTYPE_STRING :
2040 case CELLTYPE_EDIT :
2042 if ( bTextAsZero )
2044 rErr = 0;
2045 rValue = 0.0;
2046 nNumFmtType = NUMBERFORMAT_NUMBER;
2047 nNumFmtIndex = 0;
2048 bNumValid = true;
2049 bFound = true;
2052 break;
2053 default: ; // nothing
2057 return bFound;
2060 //-------------------------------------------------------------------------------
2062 ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument* pDocument, SCTAB nTable,
2063 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
2064 pDoc( pDocument ),
2065 nTab( nTable ),
2066 nStartCol( nCol1 ),
2067 nStartRow( nRow1 ),
2068 nEndCol( nCol2 ),
2069 nEndRow( 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" );
2075 SCCOL i;
2077 nRow = nStartRow;
2078 nCol = nStartCol;
2079 bRowEmpty = false;
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;
2086 bool bEmpty = true;
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" );
2093 SCSIZE nIndex;
2094 pArray->Search( nStartRow, nIndex );
2096 const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern;
2097 SCROW nThisEnd = pArray->pData[nIndex].nRow;
2098 if ( IsDefaultItem( pPattern ) )
2100 pPattern = NULL;
2101 if ( nThisEnd < nSkipTo )
2102 nSkipTo = nThisEnd; // nSkipTo can be set here already
2104 else
2105 bEmpty = false; // Found attributes
2107 pIndices[nPos] = nIndex;
2108 pNextEnd[nPos] = nThisEnd;
2109 ppPatterns[nPos] = pPattern;
2112 if (bEmpty)
2113 nRow = nSkipTo; // Skip until end of next section
2115 bRowEmpty = bEmpty;
2118 ScHorizontalAttrIterator::~ScHorizontalAttrIterator()
2120 delete[] ppPatterns;
2121 delete[] pNextEnd;
2122 delete[] pIndices;
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");
2129 for (;;)
2131 if (!bRowEmpty)
2133 // Search in this row
2134 while ( nCol <= nEndCol && !ppPatterns[nCol-nStartCol] )
2135 ++nCol;
2137 if ( nCol <= nEndCol )
2139 const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol];
2140 rRow = nRow;
2141 rCol1 = nCol;
2142 while ( nCol < nEndCol && ppPatterns[nCol+1-nStartCol] == pPat )
2143 ++nCol;
2144 rCol2 = nCol;
2145 ++nCol; // Count up for next call
2146 return pPat; // Found it!
2150 // Next row
2151 ++nRow;
2152 if ( nRow > nEndRow ) // Already at the end?
2153 return NULL; // Found nothing
2155 bool bEmpty = true;
2156 SCCOL i;
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 ) )
2171 pPattern = NULL;
2172 else
2173 bEmpty = false; // Found attributes
2175 pNextEnd[nPos] = nThisEnd;
2176 ppPatterns[nPos] = pPattern;
2178 OSL_ENSURE( pNextEnd[nPos] >= nRow, "Sequence out of order" );
2180 else
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
2191 if (bEmpty)
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
2200 bRowEmpty = bEmpty;
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 ),
2216 nNextCol( nCol1 ),
2217 nNextRow( nRow1 )
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;
2242 // Find next area
2243 bool bFound = true;
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
2256 else
2257 nFoundEndCol = nAttrCol2; // Everything
2259 else
2261 bUseCell = true;
2262 if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attributes on the cell?
2263 pFoundPattern = pPattern;
2264 else
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;
2281 else // Nothing
2282 bFound = false;
2284 if ( bUseCell ) // Cell position
2286 if (pCell)
2287 maFoundCell = *pCell;
2288 else
2289 maFoundCell.clear();
2291 nFoundRow = nCellRow;
2292 nFoundStartCol = nFoundEndCol = nCellCol;
2295 if (bFound)
2297 nNextRow = nFoundRow;
2298 nNextCol = nFoundEndCol + 1;
2301 return bFound;
2304 const ScRefCellValue& ScUsedAreaIterator::GetCell() const
2306 return maFoundCell;
2309 //-------------------------------------------------------------------------------
2311 ScDocAttrIterator::ScDocAttrIterator(ScDocument* pDocument, SCTAB nTable,
2312 SCCOL nCol1, SCROW nRow1,
2313 SCCOL nCol2, SCROW nRow2) :
2314 pDoc( pDocument ),
2315 nTab( nTable ),
2316 nEndCol( nCol2 ),
2317 nStartRow( nRow1 ),
2318 nEndRow( nRow2 ),
2319 nCol( nCol1 )
2321 if ( ValidTab(nTab) && nTab < pDoc->GetTableCount() && pDoc->maTabs[nTab] )
2322 pColIter = pDoc->maTabs[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow );
2323 else
2324 pColIter = NULL;
2327 ScDocAttrIterator::~ScDocAttrIterator()
2329 delete pColIter;
2332 const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 )
2334 while ( pColIter )
2336 const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 );
2337 if ( pPattern )
2339 rCol = nCol;
2340 return pPattern;
2343 delete pColIter;
2344 ++nCol;
2345 if ( nCol <= nEndCol )
2346 pColIter = pDoc->maTabs[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow );
2347 else
2348 pColIter = NULL;
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.
2370 updateAll();
2371 return;
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))
2382 if (!aData.mbValue)
2383 continue;
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])
2398 continue;
2400 ScFlatBoolRowSegments::RangeData aData;
2401 ScFlatBoolRowSegments::RangeIterator aRangeItr(*itr->mpRanges);
2402 for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
2404 if (!aData.mbValue)
2405 continue;
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])
2421 continue;
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])
2433 continue;
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) :
2447 pDoc( pDocument ),
2448 nTab( nTable ),
2449 nEndCol( nCol2 ),
2450 nStartRow( nRow1 ),
2451 nEndRow( 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 ) )
2461 ++nIterEndCol;
2463 else
2464 pColIter = NULL;
2467 ScAttrRectIterator::~ScAttrRectIterator()
2469 delete pColIter;
2472 void ScAttrRectIterator::DataChanged()
2474 if (pColIter)
2476 SCROW nNextRow = pColIter->GetNextRow();
2477 delete pColIter;
2478 pColIter = pDoc->maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( nNextRow, nEndRow );
2482 const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2,
2483 SCROW& rRow1, SCROW& rRow2 )
2485 while ( pColIter )
2487 const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 );
2488 if ( pPattern )
2490 rCol1 = nIterStartCol;
2491 rCol2 = nIterEndCol;
2492 return pPattern;
2495 delete pColIter;
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 ) )
2504 ++nIterEndCol;
2506 else
2507 pColIter = NULL;
2509 return NULL; // Nothing anymore
2512 // ============================================================================
2514 SCROW ScRowBreakIterator::NOT_FOUND = -1;
2516 ScRowBreakIterator::ScRowBreakIterator(set<SCROW>& rBreaks) :
2517 mrBreaks(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()
2530 ++maItr;
2531 return maItr == maEnd ? NOT_FOUND : *maItr;
2534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */