fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / data / dociter.cxx
blob8cf40b61a4211a2feddba72721b84c9036615755
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"
41 #include <rowheightcontext.hxx>
43 #include <tools/fract.hxx>
44 #include <editeng/editobj.hxx>
45 #include <svl/sharedstring.hxx>
47 #include <vector>
49 using ::rtl::math::approxEqual;
50 using ::std::vector;
51 using ::std::set;
53 // iterators have very high frequency use -> custom debug.
54 // #define debugiter(...) fprintf(stderr, __VA_ARGS__)
55 #define debugiter(...)
57 // STATIC DATA -----------------------------------------------------------
59 namespace {
61 template<typename _Iter>
62 void incBlock(std::pair<_Iter, size_t>& rPos)
64 // Move to the next block.
65 ++rPos.first;
66 rPos.second = 0;
69 template<typename _Iter>
70 void decBlock(std::pair<_Iter, size_t>& rPos)
72 // Move to the last element of the previous block.
73 --rPos.first;
74 rPos.second = rPos.first->size - 1;
79 void ScAttrArray_IterGetNumberFormat( sal_uLong& nFormat, const ScAttrArray*& rpArr,
80 SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow,
81 ScDocument* pDoc )
83 if ( rpArr != pNewArr || nAttrEndRow < nRow )
85 SCROW nRowStart = 0;
86 SCROW nRowEnd = MAXROW;
87 const ScPatternAttr* pPattern;
88 if( !(pPattern = pNewArr->GetPatternRange( nRowStart, nRowEnd, nRow ) ) )
90 pPattern = pDoc->GetDefPattern();
91 nRowEnd = MAXROW;
94 nFormat = pPattern->GetNumberFormat( pDoc->GetFormatTable() );
95 rpArr = pNewArr;
96 nAttrEndRow = nRowEnd;
100 ScValueIterator::ScValueIterator( ScDocument* pDocument, const ScRange& rRange,
101 sal_uInt16 nSubTotalFlags, bool bTextZero )
102 : pDoc(pDocument)
103 , pAttrArray(NULL)
104 , nNumFormat(0) // Initialized in GetNumberFormat
105 , nNumFmtIndex(0)
106 , maStartPos(rRange.aStart)
107 , maEndPos(rRange.aEnd)
108 , mnCol(0)
109 , mnTab(0)
110 , nAttrEndRow(0)
111 , mnSubTotalFlags(nSubTotalFlags)
112 , nNumFmtType(css::util::NumberFormat::UNDEFINED)
113 , bNumValid(false)
114 , bCalcAsShown(pDocument->GetDocOptions().IsCalcAsShown())
115 , bTextAsZero(bTextZero)
116 , mpCells(NULL)
118 SCTAB nDocMaxTab = pDocument->GetTableCount() - 1;
120 if (!ValidCol(maStartPos.Col())) maStartPos.SetCol(MAXCOL);
121 if (!ValidCol(maEndPos.Col())) maEndPos.SetCol(MAXCOL);
122 if (!ValidRow(maStartPos.Row())) maStartPos.SetRow(MAXROW);
123 if (!ValidRow(maEndPos.Row())) maEndPos.SetRow(MAXROW);
124 if (!ValidTab(maStartPos.Tab()) || maStartPos.Tab() > nDocMaxTab) maStartPos.SetTab(nDocMaxTab);
125 if (!ValidTab(maEndPos.Tab()) || maEndPos.Tab() > nDocMaxTab) maEndPos.SetTab(nDocMaxTab);
128 SCROW ScValueIterator::GetRow() const
130 // Position of the head of the current block + offset within the block
131 // equals the logical element position.
132 return maCurPos.first->position + maCurPos.second;
135 void ScValueIterator::IncBlock()
137 ++maCurPos.first;
138 maCurPos.second = 0;
141 void ScValueIterator::IncPos()
143 if (maCurPos.second + 1 < maCurPos.first->size)
144 // Move within the same block.
145 ++maCurPos.second;
146 else
147 // Move to the next block.
148 IncBlock();
151 void ScValueIterator::SetPos(size_t nPos)
153 maCurPos = mpCells->position(maCurPos.first, nPos);
156 bool ScValueIterator::GetThis(double& rValue, sal_uInt16& rErr)
158 while (true)
160 bool bNextColumn = maCurPos.first == mpCells->end();
161 if (!bNextColumn)
163 if (GetRow() > maEndPos.Row())
164 bNextColumn = true;
167 ScColumn* pCol = &(pDoc->maTabs[mnTab])->aCol[mnCol];
168 if (bNextColumn)
170 // Find the next available column.
173 ++mnCol;
174 if (mnCol > maEndPos.Col())
176 mnCol = maStartPos.Col();
177 ++mnTab;
178 if (mnTab > maEndPos.Tab())
180 rErr = 0;
181 return false; // Over and out
184 pCol = &(pDoc->maTabs[mnTab])->aCol[mnCol];
186 while (pCol->IsEmptyData());
188 mpCells = &pCol->maCells;
189 maCurPos = mpCells->position(maStartPos.Row());
192 SCROW nCurRow = GetRow();
193 SCROW nLastRow;
194 // Skip all filtered or hidden rows, depending on mnSubTotalFlags
195 if ( ( ( mnSubTotalFlags & SUBTOTAL_IGN_FILTERED ) &&
196 pDoc->RowFiltered( nCurRow, mnTab, NULL, &nLastRow ) ) ||
197 ( ( mnSubTotalFlags & SUBTOTAL_IGN_HIDDEN ) &&
198 pDoc->RowHidden( nCurRow, mnTab, NULL, &nLastRow ) ) )
200 SetPos(nLastRow+1);
201 continue;
204 switch (maCurPos.first->type)
206 case sc::element_type_numeric:
208 bNumValid = false;
209 rValue = sc::numeric_block::at(*maCurPos.first->data, maCurPos.second);
210 rErr = 0;
211 if (bCalcAsShown)
213 ScAttrArray_IterGetNumberFormat(nNumFormat, pAttrArray,
214 nAttrEndRow, pCol->pAttrArray, nCurRow, pDoc);
215 rValue = pDoc->RoundValueAsShown(rValue, nNumFormat);
217 return true; // Found it!
219 break;
220 case sc::element_type_formula:
222 ScFormulaCell& rCell = *sc::formula_block::at(*maCurPos.first->data, maCurPos.second);
223 if ( ( mnSubTotalFlags & SUBTOTAL_IGN_NESTED_ST_AG ) && rCell.IsSubTotal() )
225 // Skip subtotal formula cells.
226 IncPos();
227 break;
230 if (rCell.GetErrorOrValue(rErr, rValue))
232 if ( rErr && ( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
234 IncPos();
235 break;
237 bNumValid = false;
238 return true; // Found it!
240 else if (bTextAsZero)
242 rValue = 0.0;
243 bNumValid = false;
244 return true;
246 IncPos();
248 break;
249 case sc::element_type_string :
250 case sc::element_type_edittext :
252 if (bTextAsZero)
254 rErr = 0;
255 rValue = 0.0;
256 nNumFmtType = css::util::NumberFormat::NUMBER;
257 nNumFmtIndex = 0;
258 bNumValid = true;
259 return true;
261 IncBlock();
263 break;
264 case sc::element_type_empty:
265 default:
266 // Skip the whole block.
267 IncBlock();
272 void ScValueIterator::GetCurNumFmtInfo( short& nType, sal_uLong& nIndex )
274 if (!bNumValid && mnTab < pDoc->GetTableCount())
276 SCROW nCurRow = GetRow();
277 const ScColumn* pCol = &(pDoc->maTabs[mnTab])->aCol[mnCol];
278 nNumFmtIndex = pCol->GetNumberFormat(nCurRow);
279 nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex );
280 bNumValid = true;
283 nType = nNumFmtType;
284 nIndex = nNumFmtIndex;
287 bool ScValueIterator::GetFirst(double& rValue, sal_uInt16& rErr)
289 mnCol = maStartPos.Col();
290 mnTab = maStartPos.Tab();
292 ScTable* pTab = pDoc->FetchTable(mnTab);
293 if (!pTab)
294 return false;
296 nNumFormat = 0; // Initialized in GetNumberFormat
297 pAttrArray = 0;
298 nAttrEndRow = 0;
300 mpCells = &pTab->aCol[maStartPos.Col()].maCells;
301 maCurPos = mpCells->position(maStartPos.Row());
302 return GetThis(rValue, rErr);
305 bool ScValueIterator::GetNext(double& rValue, sal_uInt16& rErr)
307 IncPos();
308 return GetThis(rValue, rErr);
311 ScDBQueryDataIterator::DataAccess::DataAccess(const ScDBQueryDataIterator* pParent) :
312 mpParent(pParent)
316 ScDBQueryDataIterator::DataAccess::~DataAccess()
320 const sc::CellStoreType* ScDBQueryDataIterator::GetColumnCellStore(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
322 ScTable* pTab = rDoc.FetchTable(nTab);
323 if (!pTab)
324 return NULL;
326 return &pTab->aCol[nCol].maCells;
329 const ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
331 if (nTab >= rDoc.GetTableCount())
332 OSL_FAIL("try to access index out of bounds, FIX IT");
333 ScColumn* pCol = &rDoc.maTabs[nTab]->aCol[nCol];
334 return pCol->pAttrArray;
337 bool ScDBQueryDataIterator::IsQueryValid(
338 ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScRefCellValue* pCell)
340 if (nTab >= rDoc.GetTableCount())
341 OSL_FAIL("try to access index out of bounds, FIX IT");
342 return rDoc.maTabs[nTab]->ValidQuery(nRow, rParam, pCell);
345 ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(const ScDBQueryDataIterator* pParent, ScDBQueryParamInternal* pParam, ScDocument* pDoc)
346 : DataAccess(pParent)
347 , mpCells(NULL)
348 , mpParam(pParam)
349 , mpDoc(pDoc)
350 , pAttrArray(0)
351 , nNumFormat(0) // Initialized in GetNumberFormat
352 , nNumFmtIndex(0)
353 , nCol(mpParam->mnField)
354 , nRow(mpParam->nRow1)
355 , nAttrEndRow(0)
356 , nTab(mpParam->nTab)
357 , nNumFmtType(0)
358 , bCalcAsShown(pDoc->GetDocOptions().IsCalcAsShown())
360 SCSIZE i;
361 SCSIZE nCount = mpParam->GetEntryCount();
362 for (i=0; (i<nCount) && (mpParam->GetEntry(i).bDoQuery); i++)
364 ScQueryEntry& rEntry = mpParam->GetEntry(i);
365 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
366 rItems.resize(1);
367 ScQueryEntry::Item& rItem = rItems.front();
368 sal_uInt32 nIndex = 0;
369 bool bNumber = mpDoc->GetFormatTable()->IsNumberFormat(
370 rItem.maString.getString(), nIndex, rItem.mfVal);
371 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
375 ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal()
379 bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue)
381 // Start with the current row position, and find the first row position
382 // that satisfies the query.
384 // If the query starts in the same column as the result vector we can
385 // prefetch the cell which saves us one fetch in the success case.
386 SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField;
387 ScRefCellValue aCell;
389 while (true)
391 if (maCurPos.first == mpCells->end() || nRow > mpParam->nRow2)
393 // Bottom of the range reached. Bail out.
394 rValue.mnError = 0;
395 return false;
398 if (maCurPos.first->type == sc::element_type_empty)
400 // Skip the whole empty block.
401 incBlock();
402 continue;
405 ScRefCellValue* pCell = NULL;
406 if (nCol == static_cast<SCCOL>(nFirstQueryField))
408 aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
409 pCell = &aCell;
412 if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, *mpParam, nTab, nRow, pCell))
414 if (!pCell)
415 aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
416 switch (aCell.meType)
418 case CELLTYPE_VALUE:
420 rValue.mfValue = aCell.mfValue;
421 rValue.mbIsNumber = true;
422 if ( bCalcAsShown )
424 const ScAttrArray* pNewAttrArray =
425 ScDBQueryDataIterator::GetAttrArrayByCol(*mpDoc, nTab, nCol);
426 ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray,
427 nAttrEndRow, pNewAttrArray, nRow, mpDoc );
428 rValue.mfValue = mpDoc->RoundValueAsShown( rValue.mfValue, nNumFormat );
430 nNumFmtType = css::util::NumberFormat::NUMBER;
431 nNumFmtIndex = 0;
432 rValue.mnError = 0;
433 return true; // Found it!
436 case CELLTYPE_FORMULA:
438 if (aCell.mpFormula->IsValue())
440 rValue.mfValue = aCell.mpFormula->GetValue();
441 rValue.mbIsNumber = true;
442 mpDoc->GetNumberFormatInfo(
443 nNumFmtType, nNumFmtIndex, ScAddress(nCol, nRow, nTab));
444 rValue.mnError = aCell.mpFormula->GetErrCode();
445 return true; // Found it!
447 else if(mpParam->mbSkipString)
448 incPos();
449 else
451 rValue.maString = aCell.mpFormula->GetString().getString();
452 rValue.mfValue = 0.0;
453 rValue.mnError = aCell.mpFormula->GetErrCode();
454 rValue.mbIsNumber = false;
455 return true;
458 break;
459 case CELLTYPE_STRING:
460 case CELLTYPE_EDIT:
461 if (mpParam->mbSkipString)
462 incPos();
463 else
465 rValue.maString = aCell.getString(mpDoc);
466 rValue.mfValue = 0.0;
467 rValue.mnError = 0;
468 rValue.mbIsNumber = false;
469 return true;
471 break;
472 default:
473 incPos();
476 else
477 incPos();
479 // statement unreachable
482 bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue)
484 if (mpParam->bHasHeader)
485 ++nRow;
487 mpCells = ScDBQueryDataIterator::GetColumnCellStore(*mpDoc, nTab, nCol);
488 if (!mpCells)
489 return false;
491 maCurPos = mpCells->position(nRow);
492 return getCurrent(rValue);
495 bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue)
497 if (!mpCells || maCurPos.first == mpCells->end())
498 return false;
500 incPos();
501 return getCurrent(rValue);
504 void ScDBQueryDataIterator::DataAccessInternal::incBlock()
506 ++maCurPos.first;
507 maCurPos.second = 0;
509 nRow = maCurPos.first->position;
512 void ScDBQueryDataIterator::DataAccessInternal::incPos()
514 if (maCurPos.second + 1 < maCurPos.first->size)
516 // Move within the same block.
517 ++maCurPos.second;
518 ++nRow;
520 else
521 // Move to the next block.
522 incBlock();
525 ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(const ScDBQueryDataIterator* pParent, ScDBQueryParamMatrix* pParam)
526 : DataAccess(pParent)
527 , mpParam(pParam)
528 , mnCurRow(0)
530 SCSIZE nC, nR;
531 mpParam->mpMatrix->GetDimensions(nC, nR);
532 mnRows = static_cast<SCROW>(nR);
533 mnCols = static_cast<SCCOL>(nC);
536 ScDBQueryDataIterator::DataAccessMatrix::~DataAccessMatrix()
540 bool ScDBQueryDataIterator::DataAccessMatrix::getCurrent(Value& rValue)
542 // Starting from row == mnCurRow, get the first row that satisfies all the
543 // query parameters.
544 for ( ;mnCurRow < mnRows; ++mnCurRow)
546 const ScMatrix& rMat = *mpParam->mpMatrix;
547 if (rMat.IsEmpty(mpParam->mnField, mnCurRow))
548 // Don't take empty values into account.
549 continue;
551 bool bIsStrVal = rMat.IsString(mpParam->mnField, mnCurRow);
552 if (bIsStrVal && mpParam->mbSkipString)
553 continue;
555 if (isValidQuery(mnCurRow, rMat))
557 rValue.maString = rMat.GetString(mpParam->mnField, mnCurRow).getString();
558 rValue.mfValue = rMat.GetDouble(mpParam->mnField, mnCurRow);
559 rValue.mbIsNumber = !bIsStrVal;
560 rValue.mnError = 0;
561 return true;
564 return false;
567 bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value& rValue)
569 mnCurRow = mpParam->bHasHeader ? 1 : 0;
570 return getCurrent(rValue);
573 bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value& rValue)
575 ++mnCurRow;
576 return getCurrent(rValue);
579 namespace {
581 bool isQueryByValue(const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
583 if (rItem.meType == ScQueryEntry::ByString)
584 return false;
586 if (!rMat.IsValueOrEmpty(nCol, nRow))
587 return false;
589 return true;
592 bool isQueryByString(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
594 switch (rEntry.eOp)
596 case SC_EQUAL:
597 case SC_NOT_EQUAL:
598 case SC_CONTAINS:
599 case SC_DOES_NOT_CONTAIN:
600 case SC_BEGINS_WITH:
601 case SC_ENDS_WITH:
602 case SC_DOES_NOT_BEGIN_WITH:
603 case SC_DOES_NOT_END_WITH:
604 return true;
605 default:
609 if (rItem.meType == ScQueryEntry::ByString && rMat.IsString(nCol, nRow))
610 return true;
612 return false;
617 bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow, const ScMatrix& rMat) const
619 SCSIZE nEntryCount = mpParam->GetEntryCount();
620 vector<bool> aResults;
621 aResults.reserve(nEntryCount);
623 const CollatorWrapper& rCollator =
624 mpParam->bCaseSens ? *ScGlobal::GetCaseCollator() : *ScGlobal::GetCollator();
626 for (SCSIZE i = 0; i < nEntryCount; ++i)
628 const ScQueryEntry& rEntry = mpParam->GetEntry(i);
629 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
630 if (!rEntry.bDoQuery)
631 continue;
633 switch (rEntry.eOp)
635 case SC_EQUAL:
636 case SC_LESS:
637 case SC_GREATER:
638 case SC_LESS_EQUAL:
639 case SC_GREATER_EQUAL:
640 case SC_NOT_EQUAL:
641 break;
642 default:
643 // Only the above operators are supported.
644 continue;
647 bool bValid = false;
649 SCSIZE nField = static_cast<SCSIZE>(rEntry.nField);
650 if (isQueryByValue(rItem, rMat, nField, nRow))
652 // By value
653 double fMatVal = rMat.GetDouble(nField, nRow);
654 bool bEqual = approxEqual(fMatVal, rItem.mfVal);
655 switch (rEntry.eOp)
657 case SC_EQUAL:
658 bValid = bEqual;
659 break;
660 case SC_LESS:
661 bValid = (fMatVal < rItem.mfVal) && !bEqual;
662 break;
663 case SC_GREATER:
664 bValid = (fMatVal > rItem.mfVal) && !bEqual;
665 break;
666 case SC_LESS_EQUAL:
667 bValid = (fMatVal < rItem.mfVal) || bEqual;
668 break;
669 case SC_GREATER_EQUAL:
670 bValid = (fMatVal > rItem.mfVal) || bEqual;
671 break;
672 case SC_NOT_EQUAL:
673 bValid = !bEqual;
674 break;
675 default:
679 else if (isQueryByString(rEntry, rItem, rMat, nField, nRow))
681 // By string
684 // Equality check first.
685 svl::SharedString aMatStr = rMat.GetString(nField, nRow);
686 svl::SharedString aQueryStr = rEntry.GetQueryItem().maString;
687 bool bDone = false;
688 rtl_uString* p1 = mpParam->bCaseSens ? aMatStr.getData() : aMatStr.getDataIgnoreCase();
689 rtl_uString* p2 = mpParam->bCaseSens ? aQueryStr.getData() : aQueryStr.getDataIgnoreCase();
690 switch (rEntry.eOp)
692 case SC_EQUAL:
693 bValid = (p1 == p2);
694 bDone = true;
695 break;
696 case SC_NOT_EQUAL:
697 bValid = (p1 != p2);
698 bDone = true;
699 break;
700 default:
704 if (bDone)
705 break;
707 // Unequality check using collator.
708 sal_Int32 nCompare = rCollator.compareString(aMatStr.getString(), aQueryStr.getString());
709 switch (rEntry.eOp)
711 case SC_LESS :
712 bValid = (nCompare < 0);
713 break;
714 case SC_GREATER :
715 bValid = (nCompare > 0);
716 break;
717 case SC_LESS_EQUAL :
718 bValid = (nCompare <= 0);
719 break;
720 case SC_GREATER_EQUAL :
721 bValid = (nCompare >= 0);
722 break;
723 default:
727 while (false);
730 if (aResults.empty())
731 // First query entry.
732 aResults.push_back(bValid);
733 else if (rEntry.eConnect == SC_AND)
735 // For AND op, tuck the result into the last result value.
736 size_t n = aResults.size();
737 aResults[n-1] = aResults[n-1] && bValid;
739 else
740 // For OR op, store its own result.
741 aResults.push_back(bValid);
744 // Row is valid as long as there is at least one result being true.
745 vector<bool>::const_iterator itr = aResults.begin(), itrEnd = aResults.end();
746 for (; itr != itrEnd; ++itr)
747 if (*itr)
748 return true;
750 return false;
753 ScDBQueryDataIterator::Value::Value() :
754 mnError(0), mbIsNumber(true)
756 ::rtl::math::setNan(&mfValue);
759 ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryParamBase* pParam) :
760 mpParam (pParam)
762 switch (mpParam->GetType())
764 case ScDBQueryParamBase::INTERNAL:
766 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pParam);
767 mpData.reset(new DataAccessInternal(this, p, pDocument));
769 break;
770 case ScDBQueryParamBase::MATRIX:
772 ScDBQueryParamMatrix* p = static_cast<ScDBQueryParamMatrix*>(pParam);
773 mpData.reset(new DataAccessMatrix(this, p));
778 bool ScDBQueryDataIterator::GetFirst(Value& rValue)
780 return mpData->getFirst(rValue);
783 bool ScDBQueryDataIterator::GetNext(Value& rValue)
785 return mpData->getNext(rValue);
788 ScCellIterator::ScCellIterator( ScDocument* pDoc, const ScRange& rRange, sal_uInt16 nSubTotalFlags ) :
789 mpDoc(pDoc),
790 maStartPos(rRange.aStart),
791 maEndPos(rRange.aEnd),
792 mnSubTotalFlags(nSubTotalFlags)
794 init();
797 void ScCellIterator::incBlock()
799 ++maCurColPos.first;
800 maCurColPos.second = 0;
802 maCurPos.SetRow(maCurColPos.first->position);
805 void ScCellIterator::incPos()
807 if (maCurColPos.second + 1 < maCurColPos.first->size)
809 // Move within the same block.
810 ++maCurColPos.second;
811 maCurPos.IncRow();
813 else
814 // Move to the next block.
815 incBlock();
818 void ScCellIterator::setPos(size_t nPos)
820 maCurColPos = getColumn()->maCells.position(maCurColPos.first, nPos);
821 maCurPos.SetRow(nPos);
824 const ScColumn* ScCellIterator::getColumn() const
826 return &mpDoc->maTabs[maCurPos.Tab()]->aCol[maCurPos.Col()];
829 void ScCellIterator::init()
831 SCTAB nDocMaxTab = mpDoc->GetTableCount() - 1;
833 PutInOrder(maStartPos, maEndPos);
835 if (!ValidCol(maStartPos.Col())) maStartPos.SetCol(MAXCOL);
836 if (!ValidCol(maEndPos.Col())) maEndPos.SetCol(MAXCOL);
837 if (!ValidRow(maStartPos.Row())) maStartPos.SetRow(MAXROW);
838 if (!ValidRow(maEndPos.Row())) maEndPos.SetRow(MAXROW);
839 if (!ValidTab(maStartPos.Tab(), nDocMaxTab)) maStartPos.SetTab(nDocMaxTab);
840 if (!ValidTab(maEndPos.Tab(), nDocMaxTab)) maEndPos.SetTab(nDocMaxTab);
842 while (maEndPos.Tab() > 0 && !mpDoc->maTabs[maEndPos.Tab()])
843 maEndPos.IncTab(-1); // Only the tables in use
845 if (maStartPos.Tab() > maEndPos.Tab())
846 maStartPos.SetTab(maEndPos.Tab());
848 maCurPos = maStartPos;
850 if (!mpDoc->maTabs[maCurPos.Tab()])
852 OSL_FAIL("Table not found");
853 maStartPos = ScAddress(MAXCOL+1, MAXROW+1, MAXTAB+1); // -> Abort on GetFirst.
854 maCurPos = maStartPos;
858 bool ScCellIterator::getCurrent()
860 const ScColumn* pCol = getColumn();
862 while (true)
864 bool bNextColumn = maCurColPos.first == pCol->maCells.end();
865 if (!bNextColumn)
867 if (maCurPos.Row() > maEndPos.Row())
868 bNextColumn = true;
871 if (bNextColumn)
873 // Move to the next column.
874 maCurPos.SetRow(maStartPos.Row());
877 maCurPos.IncCol();
878 if (maCurPos.Col() > maEndPos.Col())
880 maCurPos.SetCol(maStartPos.Col());
881 maCurPos.IncTab();
882 if (maCurPos.Tab() > maEndPos.Tab())
884 maCurCell.clear();
885 return false; // Over and out
888 pCol = getColumn();
890 while (pCol->IsEmptyData());
892 maCurColPos = pCol->maCells.position(maCurPos.Row());
895 if (maCurColPos.first->type == sc::element_type_empty)
897 incBlock();
898 continue;
901 SCROW nLastRow;
902 // Skip all filtered or hidden rows, depending on mSubTotalFlags
903 if ( ( ( mnSubTotalFlags & SUBTOTAL_IGN_FILTERED ) &&
904 pCol->GetDoc().RowFiltered(maCurPos.Row(), maCurPos.Tab(), NULL, &nLastRow) ) ||
905 ( ( mnSubTotalFlags & SUBTOTAL_IGN_HIDDEN ) &&
906 pCol->GetDoc().RowHidden(maCurPos.Row(), maCurPos.Tab(), NULL, &nLastRow) ) )
908 setPos(nLastRow+1);
909 continue;
912 if (maCurColPos.first->type == sc::element_type_formula)
914 if ( mnSubTotalFlags )
916 ScFormulaCell* pCell = sc::formula_block::at(*maCurColPos.first->data, maCurColPos.second);
917 // Skip formula cells with Subtotal formulae or errors, depending on mnSubTotalFlags
918 if ( ( ( mnSubTotalFlags & SUBTOTAL_IGN_NESTED_ST_AG ) && pCell->IsSubTotal() ) ||
919 ( ( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) && pCell->GetErrCode() ) )
921 incPos();
922 continue;
927 maCurCell = sc::toRefCell(maCurColPos.first, maCurColPos.second);
928 return true;
930 return false;
933 OUString ScCellIterator::getString()
935 return maCurCell.getString(mpDoc);
938 ScCellValue ScCellIterator::getCellValue() const
940 ScCellValue aRet;
941 aRet.meType = maCurCell.meType;
943 switch (maCurCell.meType)
945 case CELLTYPE_STRING:
946 aRet.mpString = new svl::SharedString(*maCurCell.mpString);
947 break;
948 case CELLTYPE_EDIT:
949 aRet.mpEditText = maCurCell.mpEditText->Clone();
950 break;
951 case CELLTYPE_VALUE:
952 aRet.mfValue = maCurCell.mfValue;
953 break;
954 case CELLTYPE_FORMULA:
955 aRet.mpFormula = maCurCell.mpFormula->Clone();
956 break;
957 default:
961 return aRet;
964 bool ScCellIterator::hasString() const
966 return maCurCell.hasString();
969 bool ScCellIterator::hasEmptyData() const
971 if (maCurCell.isEmpty())
972 return true;
974 if (maCurCell.meType == CELLTYPE_FORMULA)
975 return maCurCell.mpFormula->IsEmpty();
977 return false;
980 bool ScCellIterator::isEmpty() const
982 return maCurCell.isEmpty();
985 bool ScCellIterator::equalsWithoutFormat( const ScAddress& rPos ) const
987 ScRefCellValue aOther;
988 aOther.assign(*mpDoc, rPos);
989 return maCurCell.equalsWithoutFormat(aOther);
992 bool ScCellIterator::first()
994 if (!ValidTab(maCurPos.Tab()))
995 return false;
997 maCurPos = maStartPos;
998 const ScColumn* pCol = getColumn();
1000 maCurColPos = pCol->maCells.position(maCurPos.Row());
1001 return getCurrent();
1004 bool ScCellIterator::next()
1006 incPos();
1007 return getCurrent();
1010 ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable,
1011 const ScQueryParam& rParam, bool bMod ) :
1012 mpParam(new ScQueryParam(rParam)),
1013 pDoc( pDocument ),
1014 nTab( nTable),
1015 nStopOnMismatch( nStopOnMismatchDisabled ),
1016 nTestEqualCondition( nTestEqualConditionDisabled ),
1017 bAdvanceQuery( false ),
1018 bIgnoreMismatchOnLeadingStrings( false )
1020 nCol = mpParam->nCol1;
1021 nRow = mpParam->nRow1;
1022 SCSIZE i;
1023 if (bMod) // Or else it's already inserted
1025 SCSIZE nCount = mpParam->GetEntryCount();
1026 for (i = 0; (i < nCount) && (mpParam->GetEntry(i).bDoQuery); ++i)
1028 ScQueryEntry& rEntry = mpParam->GetEntry(i);
1029 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
1030 sal_uInt32 nIndex = 0;
1031 bool bNumber = pDoc->GetFormatTable()->IsNumberFormat(
1032 rItem.maString.getString(), nIndex, rItem.mfVal);
1033 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
1036 nNumFormat = 0; // Initialized in GetNumberFormat
1037 pAttrArray = 0;
1038 nAttrEndRow = 0;
1041 void ScQueryCellIterator::InitPos()
1043 nRow = mpParam->nRow1;
1044 if (mpParam->bHasHeader && mpParam->bByRow)
1045 ++nRow;
1046 ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
1047 maCurPos = pCol->maCells.position(nRow);
1050 void ScQueryCellIterator::IncPos()
1052 if (maCurPos.second + 1 < maCurPos.first->size)
1054 // Move within the same block.
1055 ++maCurPos.second;
1056 ++nRow;
1058 else
1059 // Move to the next block.
1060 IncBlock();
1063 void ScQueryCellIterator::IncBlock()
1065 ++maCurPos.first;
1066 maCurPos.second = 0;
1068 nRow = maCurPos.first->position;
1071 bool ScQueryCellIterator::GetThis()
1073 if (nTab >= pDoc->GetTableCount())
1074 OSL_FAIL("try to access index out of bounds, FIX IT");
1075 const ScQueryEntry& rEntry = mpParam->GetEntry(0);
1076 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
1078 SCCOLROW nFirstQueryField = rEntry.nField;
1079 bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings &&
1080 rItem.meType != ScQueryEntry::ByString;
1081 bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
1082 !mpParam->bHasHeader && rItem.meType == ScQueryEntry::ByString &&
1083 ((mpParam->bByRow && nRow == mpParam->nRow1) ||
1084 (!mpParam->bByRow && nCol == mpParam->nCol1));
1086 ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
1087 while (true)
1089 bool bNextColumn = maCurPos.first == pCol->maCells.end();
1090 if (!bNextColumn)
1092 if (nRow > mpParam->nRow2)
1093 bNextColumn = true;
1096 if (bNextColumn)
1100 if ( ++nCol > mpParam->nCol2 )
1101 return false; // Over and out
1102 if ( bAdvanceQuery )
1104 AdvanceQueryParamEntryField();
1105 nFirstQueryField = rEntry.nField;
1107 pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
1109 while (pCol->IsEmptyData());
1111 InitPos();
1113 bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
1114 !mpParam->bHasHeader && rItem.meType == ScQueryEntry::ByString &&
1115 mpParam->bByRow;
1118 if (maCurPos.first->type == sc::element_type_empty)
1120 if (rItem.mbMatchEmpty && rEntry.GetQueryItems().size() == 1)
1122 // This shortcut, instead of determining if any SC_OR query
1123 // exists or this query is SC_AND'ed (which wouldn't make
1124 // sense, but..) and evaluating them in ValidQuery(), is
1125 // possible only because the interpreter is the only caller
1126 // that sets mbMatchEmpty and there is only one item in those
1127 // cases.
1128 // XXX this would have to be reworked if other filters used it
1129 // in different manners and evaluation would have to be done in
1130 // ValidQuery().
1131 return true;
1133 else
1135 IncBlock();
1136 continue;
1140 ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
1142 if (bAllStringIgnore && aCell.hasString())
1143 IncPos();
1144 else
1146 bool bTestEqualCondition = false;
1147 if ( pDoc->maTabs[nTab]->ValidQuery( nRow, *mpParam,
1148 (nCol == static_cast<SCCOL>(nFirstQueryField) ? &aCell : NULL),
1149 (nTestEqualCondition ? &bTestEqualCondition : NULL) ) )
1151 if ( nTestEqualCondition && bTestEqualCondition )
1152 nTestEqualCondition |= nTestEqualConditionMatched;
1153 return !aCell.isEmpty(); // Found it!
1155 else if ( nStopOnMismatch )
1157 // Yes, even a mismatch may have a fulfilled equal
1158 // condition if regular expressions were involved and
1159 // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried.
1160 if ( nTestEqualCondition && bTestEqualCondition )
1162 nTestEqualCondition |= nTestEqualConditionMatched;
1163 nStopOnMismatch |= nStopOnMismatchOccurred;
1164 return false;
1166 bool bStop;
1167 if (bFirstStringIgnore)
1169 if (aCell.hasString())
1171 IncPos();
1172 bStop = false;
1174 else
1175 bStop = true;
1177 else
1178 bStop = true;
1179 if (bStop)
1181 nStopOnMismatch |= nStopOnMismatchOccurred;
1182 return false;
1185 else
1186 IncPos();
1188 bFirstStringIgnore = false;
1192 bool ScQueryCellIterator::GetFirst()
1194 if (nTab >= pDoc->GetTableCount())
1195 OSL_FAIL("try to access index out of bounds, FIX IT");
1196 nCol = mpParam->nCol1;
1197 InitPos();
1198 return GetThis();
1201 bool ScQueryCellIterator::GetNext()
1203 IncPos();
1204 if ( nStopOnMismatch )
1205 nStopOnMismatch = nStopOnMismatchEnabled;
1206 if ( nTestEqualCondition )
1207 nTestEqualCondition = nTestEqualConditionEnabled;
1208 return GetThis();
1211 void ScQueryCellIterator::AdvanceQueryParamEntryField()
1213 SCSIZE nEntries = mpParam->GetEntryCount();
1214 for ( SCSIZE j = 0; j < nEntries; j++ )
1216 ScQueryEntry& rEntry = mpParam->GetEntry( j );
1217 if ( rEntry.bDoQuery )
1219 if ( rEntry.nField < MAXCOL )
1220 rEntry.nField++;
1221 else
1223 OSL_FAIL( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" );
1226 else
1227 break; // for
1231 bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
1232 SCROW& nFoundRow, bool bSearchForEqualAfterMismatch,
1233 bool bIgnoreMismatchOnLeadingStringsP )
1235 // Set and automatically reset mpParam->mbRangeLookup when returning. We
1236 // could use comphelper::FlagRestorationGuard, but really, that one is
1237 // overengineered for this simple purpose here.
1238 struct BoolResetter
1240 bool& mr;
1241 bool mb;
1242 BoolResetter( bool& r, bool b ) : mr(r), mb(r) { r = b; }
1243 ~BoolResetter() { mr = mb; }
1244 } aRangeLookupResetter( mpParam->mbRangeLookup, true);
1246 nFoundCol = MAXCOL+1;
1247 nFoundRow = MAXROW+1;
1248 SetStopOnMismatch( true ); // assume sorted keys
1249 SetTestEqualCondition( true );
1250 bIgnoreMismatchOnLeadingStrings = bIgnoreMismatchOnLeadingStringsP;
1251 bool bRegExp = mpParam->bRegExp && mpParam->GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString;
1252 bool bBinary = !bRegExp && mpParam->bByRow && (mpParam->GetEntry(0).eOp ==
1253 SC_LESS_EQUAL || mpParam->GetEntry(0).eOp == SC_GREATER_EQUAL);
1254 bool bFound = false;
1255 if (bBinary)
1257 if (BinarySearch())
1259 // BinarySearch() already positions correctly and only needs real
1260 // query comparisons afterwards, skip the verification check below.
1261 mpParam->mbRangeLookup = false;
1262 bFound = GetThis();
1265 else
1267 bFound = GetFirst();
1269 if (bFound)
1271 // First equal entry or last smaller than (greater than) entry.
1272 PositionType aPosSave;
1273 bool bNext = false;
1276 nFoundCol = GetCol();
1277 nFoundRow = GetRow();
1278 aPosSave = maCurPos;
1280 while ( !IsEqualConditionFulfilled() && (bNext = GetNext()));
1282 // There may be no pNext but equal condition fulfilled if regular
1283 // expressions are involved. Keep the found entry and proceed.
1284 if (!bNext && !IsEqualConditionFulfilled())
1286 // Step back to last in range and adjust position markers for
1287 // GetNumberFormat() or similar.
1288 SCCOL nColDiff = nCol - nFoundCol;
1289 nCol = nFoundCol;
1290 nRow = nFoundRow;
1291 maCurPos = aPosSave;
1292 if (mpParam->mbRangeLookup)
1294 // Verify that the found entry does not only fulfill the range
1295 // lookup but also the real query, i.e. not numeric was found
1296 // if query is ByString and vice versa.
1297 mpParam->mbRangeLookup = false;
1298 // Step back the last field advance if GetNext() did one.
1299 if (bAdvanceQuery && nColDiff)
1301 SCSIZE nEntries = mpParam->GetEntryCount();
1302 for (SCSIZE j=0; j < nEntries; ++j)
1304 ScQueryEntry& rEntry = mpParam->GetEntry( j );
1305 if (rEntry.bDoQuery)
1307 if (rEntry.nField - nColDiff >= 0)
1308 rEntry.nField -= nColDiff;
1309 else
1311 assert(!"FindEqualOrSortedLastInRange: rEntry.nField -= nColDiff < 0");
1314 else
1315 break; // for
1318 // Check it.
1319 if (!GetThis())
1321 nFoundCol = MAXCOL+1;
1322 nFoundRow = MAXROW+1;
1327 if ( IsEqualConditionFulfilled() )
1329 // Position on last equal entry.
1330 SCSIZE nEntries = mpParam->GetEntryCount();
1331 for ( SCSIZE j = 0; j < nEntries; j++ )
1333 ScQueryEntry& rEntry = mpParam->GetEntry( j );
1334 if ( rEntry.bDoQuery )
1336 switch ( rEntry.eOp )
1338 case SC_LESS_EQUAL :
1339 case SC_GREATER_EQUAL :
1340 rEntry.eOp = SC_EQUAL;
1341 break;
1342 default:
1344 // added to avoid warnings
1348 else
1349 break; // for
1351 PositionType aPosSave;
1352 bIgnoreMismatchOnLeadingStrings = false;
1353 SetTestEqualCondition( false );
1356 nFoundCol = GetCol();
1357 nFoundRow = GetRow();
1358 aPosSave = maCurPos;
1359 } while (GetNext());
1361 // Step back conditions are the same as above
1362 nCol = nFoundCol;
1363 nRow = nFoundRow;
1364 maCurPos = aPosSave;
1365 return true;
1367 if ( (bSearchForEqualAfterMismatch || mpParam->bRegExp) &&
1368 StoppedOnMismatch() )
1370 // Assume found entry to be the last value less than respectively
1371 // greater than the query. But keep on searching for an equal match.
1372 SCSIZE nEntries = mpParam->GetEntryCount();
1373 for ( SCSIZE j = 0; j < nEntries; j++ )
1375 ScQueryEntry& rEntry = mpParam->GetEntry( j );
1376 if ( rEntry.bDoQuery )
1378 switch ( rEntry.eOp )
1380 case SC_LESS_EQUAL :
1381 case SC_GREATER_EQUAL :
1382 rEntry.eOp = SC_EQUAL;
1383 break;
1384 default:
1386 // added to avoid warnings
1390 else
1391 break; // for
1393 SetStopOnMismatch( false );
1394 SetTestEqualCondition( false );
1395 if (GetNext())
1397 // Last of a consecutive area, avoid searching the entire parameter
1398 // range as it is a real performance bottleneck in case of regular
1399 // expressions.
1400 PositionType aPosSave;
1403 nFoundCol = GetCol();
1404 nFoundRow = GetRow();
1405 aPosSave = maCurPos;
1406 SetStopOnMismatch( true );
1407 } while (GetNext());
1408 nCol = nFoundCol;
1409 nRow = nFoundRow;
1410 maCurPos = aPosSave;
1413 return (nFoundCol <= MAXCOL) && (nFoundRow <= MAXROW);
1416 namespace {
1419 * This class sequentially indexes non-empty cells in order, from the top of
1420 * the block where the start row position is, to the bottom of the block
1421 * where the end row position is. It skips all empty blocks that may be
1422 * present in between.
1424 * The index value is an offset from the first element of the first block
1425 * disregarding all empty cell blocks.
1427 class NonEmptyCellIndexer
1429 typedef std::map<size_t, sc::CellStoreType::const_iterator> BlockMapType;
1431 BlockMapType maBlockMap;
1433 const sc::CellStoreType& mrCells;
1435 size_t mnLowIndex;
1436 size_t mnHighIndex;
1438 bool mbValid;
1440 public:
1442 typedef std::pair<ScRefCellValue, SCROW> CellType;
1445 * @param rCells cell storage container
1446 * @param nStartRow logical start row position
1447 * @param nEndRow logical end row position, inclusive.
1448 * @param bSkipTopStrBlock when true, skip all leading string cells.
1450 NonEmptyCellIndexer(
1451 const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow, bool bSkipTopStrBlock ) :
1452 mrCells(rCells), mnLowIndex(0), mnHighIndex(0), mbValid(true)
1454 if (nEndRow < nStartRow)
1456 mbValid = false;
1457 return;
1460 // Find the low position.
1462 sc::CellStoreType::const_position_type aLoPos = mrCells.position(nStartRow);
1463 if (aLoPos.first->type == sc::element_type_empty)
1464 incBlock(aLoPos);
1466 if (aLoPos.first == rCells.end())
1468 mbValid = false;
1469 return;
1472 if (bSkipTopStrBlock)
1474 // Skip all leading string or empty blocks.
1475 while (aLoPos.first->type == sc::element_type_string ||
1476 aLoPos.first->type == sc::element_type_edittext ||
1477 aLoPos.first->type == sc::element_type_empty)
1479 incBlock(aLoPos);
1480 if (aLoPos.first == rCells.end())
1482 mbValid = false;
1483 return;
1488 SCROW nFirstRow = aLoPos.first->position;
1489 SCROW nLastRow = aLoPos.first->position + aLoPos.first->size - 1;
1491 if (nFirstRow > nEndRow)
1493 // Both start and end row positions are within the leading skipped
1494 // blocks.
1495 mbValid = false;
1496 return;
1499 // Calculate the index of the low position.
1500 if (nFirstRow < nStartRow)
1501 mnLowIndex = nStartRow - nFirstRow;
1502 else
1504 // Start row is within the skipped block(s). Set it to the first
1505 // element of the low block.
1506 mnLowIndex = 0;
1509 if (nEndRow < nLastRow)
1511 assert(nEndRow >= nFirstRow);
1512 mnHighIndex = nEndRow - nFirstRow;
1514 maBlockMap.insert(BlockMapType::value_type(aLoPos.first->size, aLoPos.first));
1515 return;
1518 // Find the high position.
1520 sc::CellStoreType::const_position_type aHiPos = mrCells.position(aLoPos.first, nEndRow);
1521 if (aHiPos.first->type == sc::element_type_empty)
1523 // Move to the last position of the previous block.
1524 decBlock(aHiPos);
1526 // Check the row position of the end of the previous block, and make sure it's valid.
1527 SCROW nBlockEndRow = aHiPos.first->position + aHiPos.first->size - 1;
1528 if (nBlockEndRow < nStartRow)
1530 mbValid = false;
1531 return;
1535 // Tag the start and end blocks, and all blocks in between in order
1536 // but skip all empty blocks.
1538 size_t nPos = 0;
1539 sc::CellStoreType::const_iterator itBlk = aLoPos.first;
1540 while (itBlk != aHiPos.first)
1542 if (itBlk->type == sc::element_type_empty)
1544 ++itBlk;
1545 continue;
1548 nPos += itBlk->size;
1549 maBlockMap.insert(BlockMapType::value_type(nPos, itBlk));
1550 ++itBlk;
1552 if (itBlk->type == sc::element_type_empty)
1553 ++itBlk;
1555 assert(itBlk != mrCells.end());
1558 assert(itBlk == aHiPos.first);
1559 nPos += itBlk->size;
1560 maBlockMap.insert(BlockMapType::value_type(nPos, itBlk));
1562 // Calculate the high index.
1563 BlockMapType::const_reverse_iterator ri = maBlockMap.rbegin();
1564 mnHighIndex = ri->first;
1565 mnHighIndex -= ri->second->size;
1566 mnHighIndex += aHiPos.second;
1569 sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
1571 assert(mbValid);
1572 assert(mnLowIndex <= nIndex);
1573 assert(nIndex <= mnHighIndex);
1575 sc::CellStoreType::const_position_type aRet(mrCells.end(), 0);
1577 BlockMapType::const_iterator it = maBlockMap.upper_bound(nIndex);
1578 if (it == maBlockMap.end())
1579 return aRet;
1581 sc::CellStoreType::const_iterator itBlk = it->second;
1582 size_t nBlkIndex = it->first - itBlk->size; // index of the first element of the block.
1583 assert(nBlkIndex <= nIndex);
1584 assert(nIndex < it->first);
1586 size_t nOffset = nIndex - nBlkIndex;
1587 aRet.first = itBlk;
1588 aRet.second = nOffset;
1589 return aRet;
1592 CellType getCell( size_t nIndex ) const
1594 std::pair<ScRefCellValue, SCROW> aRet;
1595 aRet.second = -1;
1597 sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
1598 if (aPos.first == mrCells.end())
1599 return aRet;
1601 aRet.first = sc::toRefCell(aPos.first, aPos.second);
1602 aRet.second = aPos.first->position + aPos.second;
1603 return aRet;
1606 size_t getLowIndex() const { return mnLowIndex; }
1608 size_t getHighIndex() const { return mnHighIndex; }
1610 bool isValid() const { return mbValid; }
1615 bool ScQueryCellIterator::BinarySearch()
1617 // TODO: This will be extremely slow with mdds::multi_type_vector.
1619 if (nTab >= pDoc->GetTableCount())
1620 OSL_FAIL("try to access index out of bounds, FIX IT");
1621 nCol = mpParam->nCol1;
1622 ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
1623 if (pCol->IsEmptyData())
1624 return false;
1626 CollatorWrapper* pCollator = (mpParam->bCaseSens ? ScGlobal::GetCaseCollator() :
1627 ScGlobal::GetCollator());
1628 SvNumberFormatter& rFormatter = *(pDoc->GetFormatTable());
1629 const ScQueryEntry& rEntry = mpParam->GetEntry(0);
1630 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
1631 bool bLessEqual = rEntry.eOp == SC_LESS_EQUAL;
1632 bool bByString = rItem.meType == ScQueryEntry::ByString;
1633 bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !bByString;
1634 bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
1635 !mpParam->bHasHeader && bByString;
1637 nRow = mpParam->nRow1;
1638 if (mpParam->bHasHeader)
1639 ++nRow;
1641 ScRefCellValue aCell;
1642 if (bFirstStringIgnore)
1644 sc::CellStoreType::const_position_type aPos = pCol->maCells.position(nRow);
1645 if (aPos.first->type == sc::element_type_string || aPos.first->type == sc::element_type_edittext)
1647 aCell = sc::toRefCell(aPos.first, aPos.second);
1648 sal_uLong nFormat = pCol->GetNumberFormat(nRow);
1649 OUString aCellStr;
1650 ScCellFormat::GetInputString(aCell, nFormat, aCellStr, rFormatter, pDoc);
1651 sal_Int32 nTmp = pCollator->compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
1652 if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) ||
1653 (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) ||
1654 (rEntry.eOp == SC_EQUAL && nTmp != 0))
1655 ++nRow;
1659 NonEmptyCellIndexer aIndexer(pCol->maCells, nRow, mpParam->nRow2, bAllStringIgnore);
1660 if (!aIndexer.isValid())
1661 return false;
1663 size_t nLo = aIndexer.getLowIndex();
1664 size_t nHi = aIndexer.getHighIndex();
1665 NonEmptyCellIndexer::CellType aCellData;
1667 // Bookkeeping values for breaking up the binary search in case the data
1668 // range isn't strictly sorted.
1669 size_t nLastInRange = nLo;
1670 size_t nFirstLastInRange = nLastInRange;
1671 double fLastInRangeValue = bLessEqual ?
1672 -(::std::numeric_limits<double>::max()) :
1673 ::std::numeric_limits<double>::max();
1674 OUString aLastInRangeString;
1675 if (!bLessEqual)
1676 aLastInRangeString = OUString(sal_Unicode(0xFFFF));
1678 aCellData = aIndexer.getCell(nLastInRange);
1679 aCell = aCellData.first;
1680 if (aCell.hasString())
1682 sal_uLong nFormat = pCol->GetNumberFormat(aCellData.second);
1683 OUString aStr;
1684 ScCellFormat::GetInputString(aCell, nFormat, aStr, rFormatter, pDoc);
1685 aLastInRangeString = aStr;
1687 else
1689 switch (aCell.meType)
1691 case CELLTYPE_VALUE :
1692 fLastInRangeValue = aCell.mfValue;
1693 break;
1694 case CELLTYPE_FORMULA :
1695 fLastInRangeValue = aCell.mpFormula->GetValue();
1696 break;
1697 default:
1699 // added to avoid warnings
1704 sal_Int32 nRes = 0;
1705 bool bFound = false;
1706 bool bDone = false;
1707 while (nLo <= nHi && !bDone)
1709 size_t nMid = (nLo+nHi)/2;
1710 size_t i = nMid;
1711 if (i > nHi)
1713 if (nMid > 0)
1714 nHi = nMid - 1;
1715 else
1716 bDone = true;
1717 continue; // while
1720 aCellData = aIndexer.getCell(i);
1721 aCell = aCellData.first;
1722 bool bStr = aCell.hasString();
1723 nRes = 0;
1725 // compares are content<query:-1, content>query:1
1726 // Cell value comparison similar to ScTable::ValidQuery()
1727 if (!bStr && !bByString)
1729 double nCellVal;
1730 switch (aCell.meType)
1732 case CELLTYPE_VALUE :
1733 case CELLTYPE_FORMULA :
1734 nCellVal = aCell.getValue();
1735 break;
1736 default:
1737 nCellVal = 0.0;
1739 if ((nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(
1740 nCellVal, rItem.mfVal))
1742 nRes = -1;
1743 if (bLessEqual)
1745 if (fLastInRangeValue < nCellVal)
1747 fLastInRangeValue = nCellVal;
1748 nLastInRange = i;
1750 else if (fLastInRangeValue > nCellVal)
1752 // not strictly sorted, continue with GetThis()
1753 nLastInRange = nFirstLastInRange;
1754 bDone = true;
1758 else if ((nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(
1759 nCellVal, rItem.mfVal))
1761 nRes = 1;
1762 if (!bLessEqual)
1764 if (fLastInRangeValue > nCellVal)
1766 fLastInRangeValue = nCellVal;
1767 nLastInRange = i;
1769 else if (fLastInRangeValue < nCellVal)
1771 // not strictly sorted, continue with GetThis()
1772 nLastInRange = nFirstLastInRange;
1773 bDone = true;
1778 else if (bStr && bByString)
1780 OUString aCellStr;
1781 sal_uLong nFormat = pCol->GetNumberFormat(aCellData.second);
1782 ScCellFormat::GetInputString(aCell, nFormat, aCellStr, rFormatter, pDoc);
1784 nRes = pCollator->compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
1785 if (nRes < 0 && bLessEqual)
1787 sal_Int32 nTmp = pCollator->compareString( aLastInRangeString,
1788 aCellStr);
1789 if (nTmp < 0)
1791 aLastInRangeString = aCellStr;
1792 nLastInRange = i;
1794 else if (nTmp > 0)
1796 // not strictly sorted, continue with GetThis()
1797 nLastInRange = nFirstLastInRange;
1798 bDone = true;
1801 else if (nRes > 0 && !bLessEqual)
1803 sal_Int32 nTmp = pCollator->compareString( aLastInRangeString,
1804 aCellStr);
1805 if (nTmp > 0)
1807 aLastInRangeString = aCellStr;
1808 nLastInRange = i;
1810 else if (nTmp < 0)
1812 // not strictly sorted, continue with GetThis()
1813 nLastInRange = nFirstLastInRange;
1814 bDone = true;
1818 else if (!bStr && bByString)
1820 nRes = -1; // numeric < string
1821 if (bLessEqual)
1822 nLastInRange = i;
1824 else // if (bStr && !bByString)
1826 nRes = 1; // string > numeric
1827 if (!bLessEqual)
1828 nLastInRange = i;
1830 if (nRes < 0)
1832 if (bLessEqual)
1833 nLo = nMid + 1;
1834 else // assumed to be SC_GREATER_EQUAL
1836 if (nMid > 0)
1837 nHi = nMid - 1;
1838 else
1839 bDone = true;
1842 else if (nRes > 0)
1844 if (bLessEqual)
1846 if (nMid > 0)
1847 nHi = nMid - 1;
1848 else
1849 bDone = true;
1851 else // assumed to be SC_GREATER_EQUAL
1852 nLo = nMid + 1;
1854 else
1856 nLo = i;
1857 bDone = bFound = true;
1861 if (!bFound)
1863 // If all hits didn't result in a moving limit there's something
1864 // strange, e.g. data range not properly sorted, or only identical
1865 // values encountered, which doesn't mean there aren't any others in
1866 // between.. leave it to GetThis(). The condition for this would be
1867 // if (nLastInRange == nFirstLastInRange) nLo = nFirstLastInRange;
1868 // Else, in case no exact match was found, we step back for a
1869 // subsequent GetThis() to find the last in range. Effectively this is
1870 // --nLo with nLastInRange == nLo-1. Both conditions combined yield:
1871 nLo = nLastInRange;
1874 aCellData = aIndexer.getCell(nLo);
1875 if (nLo <= nHi && aCellData.second <= mpParam->nRow2)
1877 nRow = aCellData.second;
1878 maCurPos = aIndexer.getPosition(nLo);
1879 return true;
1881 else
1883 nRow = mpParam->nRow2 + 1;
1884 // Set current position to the last possible row.
1885 maCurPos.first = pCol->maCells.end();
1886 --maCurPos.first;
1887 maCurPos.second = maCurPos.first->size - 1;
1888 return false;
1892 ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable,
1893 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
1894 pDoc( pDocument ),
1895 mnTab( nTable ),
1896 nStartCol( nCol1 ),
1897 nEndCol( nCol2 ),
1898 nStartRow( nRow1 ),
1899 nEndRow( nRow2 ),
1900 mnCol( nCol1 ),
1901 mnRow( nRow1 ),
1902 mbMore( false )
1904 if (mnTab >= pDoc->GetTableCount())
1905 OSL_FAIL("try to access index out of bounds, FIX IT");
1907 pNextRows = new SCROW[ nCol2-nCol1+1 ];
1908 pNextIndices = new SCSIZE[ nCol2-nCol1+1 ];
1909 maColPositions.reserve( nCol2-nCol1+1 );
1911 SetTab( mnTab );
1914 ScHorizontalCellIterator::~ScHorizontalCellIterator()
1916 delete [] pNextRows;
1917 delete [] pNextIndices;
1920 void ScHorizontalCellIterator::SetTab( SCTAB nTabP )
1922 mbMore = false;
1923 mnTab = nTabP;
1924 mnRow = nStartRow;
1925 mnCol = nStartCol;
1926 maColPositions.resize(0);
1928 // Set the start position in each column.
1929 for (SCCOL i = nStartCol; i <= nEndCol; ++i)
1931 ScColumn* pCol = &pDoc->maTabs[mnTab]->aCol[i];
1932 ColParam aParam;
1933 aParam.maPos = pCol->maCells.position(nStartRow).first;
1934 aParam.maEnd = pCol->maCells.end();
1935 aParam.mnCol = i;
1937 // find first non-empty element.
1938 while (aParam.maPos != aParam.maEnd) {
1939 if (aParam.maPos->type == sc::element_type_empty)
1940 ++aParam.maPos;
1941 else
1943 maColPositions.push_back( aParam );
1944 break;
1949 if (maColPositions.size() == 0)
1950 return;
1952 maColPos = maColPositions.begin();
1953 mbMore = true;
1954 SkipInvalid();
1957 ScRefCellValue* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow )
1959 if (!mbMore)
1961 debugiter("no more !\n");
1962 return NULL;
1965 // Return the current non-empty cell, and move the cursor to the next one.
1966 ColParam& r = *maColPos;
1968 rCol = mnCol = r.mnCol;
1969 rRow = mnRow;
1970 debugiter("return col %d row %d\n", (int)rCol, (int)rRow);
1972 size_t nOffset = static_cast<size_t>(mnRow) - r.maPos->position;
1973 maCurCell = sc::toRefCell(r.maPos, nOffset);
1974 Advance();
1975 debugiter("advance to: col %d row %d\n", (int)maColPos->mnCol, (int)mnRow);
1977 return &maCurCell;
1980 bool ScHorizontalCellIterator::GetPos( SCCOL& rCol, SCROW& rRow )
1982 rCol = mnCol;
1983 rRow = mnRow;
1984 return mbMore;
1987 // Skip any invalid / empty cells across the current row,
1988 // we only advance the cursor if the current entry is invalid.
1989 // if we return true we have a valid cursor (or hit the end)
1990 bool ScHorizontalCellIterator::SkipInvalidInRow()
1992 assert (mbMore);
1993 assert (maColPos != maColPositions.end());
1995 // Find the next non-empty cell in the current row.
1996 while( maColPos != maColPositions.end() )
1998 ColParam& r = *maColPos;
1999 assert (r.maPos != r.maEnd);
2001 size_t nRow = static_cast<size_t>(mnRow);
2003 if (nRow >= r.maPos->position)
2005 if (nRow < r.maPos->position + r.maPos->size)
2007 mnCol = maColPos->mnCol;
2008 debugiter("found valid cell at column %d, row %d\n",
2009 (int)mnCol, (int)mnRow);
2010 assert(r.maPos->type != sc::element_type_empty);
2011 return true;
2013 else
2015 bool bMoreBlocksInColumn = false;
2016 // This block is behind the current row position. Advance the block.
2017 for (++r.maPos; r.maPos != r.maEnd; ++r.maPos)
2019 if (nRow < r.maPos->position + r.maPos->size &&
2020 r.maPos->type != sc::element_type_empty)
2022 bMoreBlocksInColumn = true;
2023 break;
2026 if (!bMoreBlocksInColumn)
2028 debugiter("remove column %d at row %d\n",
2029 (int)maColPos->mnCol, (int)nRow);
2030 maColPos = maColPositions.erase(maColPos);
2031 if (maColPositions.size() == 0)
2033 debugiter("no more columns\n");
2034 mbMore = false;
2037 else
2039 debugiter("advanced column %d to block starting row %d, retying\n",
2040 (int)maColPos->mnCol, r.maPos->position);
2044 else
2046 debugiter("skip empty cells at column %d, row %d\n",
2047 (int)maColPos->mnCol, (int)nRow);
2048 ++maColPos;
2052 // No more columns with anything interesting in them ?
2053 if (maColPositions.size() == 0)
2055 debugiter("no more live columns left - done\n");
2056 mbMore = false;
2057 return true;
2060 return false;
2063 /// Find the next row that has some real content in one of it's columns.
2064 SCROW ScHorizontalCellIterator::FindNextNonEmptyRow()
2066 size_t nNextRow = MAXROW+1;
2068 for (std::vector<ColParam>::iterator it = maColPositions.begin();
2069 it != maColPositions.end(); ++it)
2071 ColParam& r = *it;
2073 assert(static_cast<size_t>(mnRow) <= r.maPos->position);
2074 nNextRow = std::min (nNextRow, static_cast<size_t>(r.maPos->position));
2077 SCROW nRow = std::max(static_cast<SCROW>(nNextRow), mnRow);
2078 debugiter("Next non empty row is %d\n", (int) nRow);
2079 return nRow;
2082 void ScHorizontalCellIterator::Advance()
2084 assert (mbMore);
2085 assert (maColPos != maColPositions.end());
2087 ++maColPos;
2089 SkipInvalid();
2092 void ScHorizontalCellIterator::SkipInvalid()
2094 if (maColPos == maColPositions.end() ||
2095 !SkipInvalidInRow())
2097 mnRow++;
2099 if (mnRow > nEndRow)
2101 mbMore = false;
2102 return;
2105 maColPos = maColPositions.begin();
2106 debugiter("moving to next row\n");
2107 if (SkipInvalidInRow())
2109 debugiter("moved to valid cell in next row (or end)\n");
2110 return;
2113 mnRow = FindNextNonEmptyRow();
2114 maColPos = maColPositions.begin();
2115 bool bCorrect = SkipInvalidInRow();
2116 assert (bCorrect); (void) bCorrect;
2119 if (mnRow > nEndRow)
2120 mbMore = false;
2123 ScHorizontalValueIterator::ScHorizontalValueIterator( ScDocument* pDocument,
2124 const ScRange& rRange, bool bTextZero ) :
2125 pDoc( pDocument ),
2126 nNumFmtIndex(0),
2127 nEndTab( rRange.aEnd.Tab() ),
2128 nNumFmtType( css::util::NumberFormat::UNDEFINED ),
2129 bNumValid( false ),
2130 bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ),
2131 bTextAsZero( bTextZero )
2133 SCCOL nStartCol = rRange.aStart.Col();
2134 SCROW nStartRow = rRange.aStart.Row();
2135 SCTAB nStartTab = rRange.aStart.Tab();
2136 SCCOL nEndCol = rRange.aEnd.Col();
2137 SCROW nEndRow = rRange.aEnd.Row();
2138 PutInOrder( nStartCol, nEndCol);
2139 PutInOrder( nStartRow, nEndRow);
2140 PutInOrder( nStartTab, nEndTab );
2142 if (!ValidCol(nStartCol)) nStartCol = MAXCOL;
2143 if (!ValidCol(nEndCol)) nEndCol = MAXCOL;
2144 if (!ValidRow(nStartRow)) nStartRow = MAXROW;
2145 if (!ValidRow(nEndRow)) nEndRow = MAXROW;
2146 if (!ValidTab(nStartTab)) nStartTab = MAXTAB;
2147 if (!ValidTab(nEndTab)) nEndTab = MAXTAB;
2149 nCurCol = nStartCol;
2150 nCurRow = nStartRow;
2151 nCurTab = nStartTab;
2153 nNumFormat = 0; // Will be initialized in GetNumberFormat()
2154 pAttrArray = 0;
2155 nAttrEndRow = 0;
2157 pCellIter = new ScHorizontalCellIterator( pDoc, nStartTab, nStartCol,
2158 nStartRow, nEndCol, nEndRow );
2161 ScHorizontalValueIterator::~ScHorizontalValueIterator()
2163 delete pCellIter;
2166 bool ScHorizontalValueIterator::GetNext( double& rValue, sal_uInt16& rErr )
2168 bool bFound = false;
2169 while ( !bFound )
2171 ScRefCellValue* pCell = pCellIter->GetNext( nCurCol, nCurRow );
2172 while ( !pCell )
2174 if ( nCurTab < nEndTab )
2176 pCellIter->SetTab( ++nCurTab);
2177 pCell = pCellIter->GetNext( nCurCol, nCurRow );
2179 else
2180 return false;
2182 switch (pCell->meType)
2184 case CELLTYPE_VALUE:
2186 bNumValid = false;
2187 rValue = pCell->mfValue;
2188 rErr = 0;
2189 if ( bCalcAsShown )
2191 ScColumn* pCol = &pDoc->maTabs[nCurTab]->aCol[nCurCol];
2192 ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray,
2193 nAttrEndRow, pCol->pAttrArray, nCurRow, pDoc );
2194 rValue = pDoc->RoundValueAsShown( rValue, nNumFormat );
2196 bFound = true;
2198 break;
2199 case CELLTYPE_FORMULA:
2201 rErr = pCell->mpFormula->GetErrCode();
2202 if (rErr || pCell->mpFormula->IsValue())
2204 rValue = pCell->mpFormula->GetValue();
2205 bNumValid = false;
2206 bFound = true;
2208 else if ( bTextAsZero )
2210 rValue = 0.0;
2211 bNumValid = false;
2212 bFound = true;
2215 break;
2216 case CELLTYPE_STRING :
2217 case CELLTYPE_EDIT :
2219 if ( bTextAsZero )
2221 rErr = 0;
2222 rValue = 0.0;
2223 nNumFmtType = css::util::NumberFormat::NUMBER;
2224 nNumFmtIndex = 0;
2225 bNumValid = true;
2226 bFound = true;
2229 break;
2230 default: ; // nothing
2233 return bFound;
2236 ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument* pDocument, SCTAB nTable,
2237 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
2238 pDoc( pDocument ),
2239 nTab( nTable ),
2240 nStartCol( nCol1 ),
2241 nStartRow( nRow1 ),
2242 nEndCol( nCol2 ),
2243 nEndRow( nRow2 )
2245 if (nTab >= pDoc->GetTableCount())
2246 OSL_FAIL("try to access index out of bounds, FIX IT");
2247 OSL_ENSURE( pDoc->maTabs[nTab], "Table does not exist" );
2249 nRow = nStartRow;
2250 nCol = nStartCol;
2251 bRowEmpty = false;
2252 bRepeatedRow = false;
2254 pIndices = new SCSIZE[nEndCol-nStartCol+1];
2255 pNextEnd = new SCROW[nEndCol-nStartCol+1];
2256 pHorizEnd = new SCCOL[nEndCol-nStartCol+1];
2257 ppPatterns = new const ScPatternAttr*[nEndCol-nStartCol+1];
2259 InitForNextRow(true);
2262 ScHorizontalAttrIterator::~ScHorizontalAttrIterator()
2264 delete[] ppPatterns;
2265 delete[] pHorizEnd;
2266 delete[] pNextEnd;
2267 delete[] pIndices;
2270 void ScHorizontalAttrIterator::InitForNextRow(bool bInitialization)
2272 bool bEmpty = true;
2273 nMinNextEnd = MAXROW;
2274 SCCOL nThisHead = 0;
2276 for (SCCOL i=nStartCol; i<=nEndCol; i++)
2278 SCCOL nPos = i - nStartCol;
2279 if ( bInitialization || pNextEnd[nPos] < nRow )
2281 const ScAttrArray* pArray = pDoc->maTabs[nTab]->aCol[i].pAttrArray;
2282 OSL_ENSURE( pArray, "pArray == 0" );
2284 SCSIZE nIndex;
2285 if (bInitialization)
2287 pArray->Search( nStartRow, nIndex );
2288 pIndices[nPos] = nIndex;
2289 pHorizEnd[nPos] = MAXCOL+1; // only for OSL_ENSURE
2291 else
2292 nIndex = ++pIndices[nPos];
2294 if ( nIndex < pArray->nCount )
2296 const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern;
2297 SCROW nThisEnd = pArray->pData[nIndex].nRow;
2299 if ( IsDefaultItem( pPattern ) )
2300 pPattern = NULL;
2301 else
2302 bEmpty = false; // Found attributes
2304 pNextEnd[nPos] = nThisEnd;
2305 OSL_ENSURE( pNextEnd[nPos] >= nRow, "Sequence out of order" );
2306 ppPatterns[nPos] = pPattern;
2308 else
2310 OSL_FAIL("AttrArray does not range to MAXROW");
2311 pNextEnd[nPos] = MAXROW;
2312 ppPatterns[nPos] = NULL;
2315 else if ( ppPatterns[nPos] )
2316 bEmpty = false; // Area not at the end yet
2318 if ( nMinNextEnd > pNextEnd[nPos] )
2319 nMinNextEnd = pNextEnd[nPos];
2321 // store positions of ScHorizontalAttrIterator elements (minimizing expensive ScPatternAttr comparisons)
2322 if (i > nStartCol && ppPatterns[nThisHead] != ppPatterns[nPos])
2324 pHorizEnd[nThisHead] = i - 1;
2325 nThisHead = nPos; // start position of the next horizontal group
2329 if (bEmpty)
2330 nRow = nMinNextEnd; // Skip until end of next section
2331 else
2332 pHorizEnd[nThisHead] = nEndCol; // set the end position of the last horizontal group, too
2333 bRowEmpty = bEmpty;
2336 bool ScHorizontalAttrIterator::InitForNextAttr()
2338 if ( !ppPatterns[nCol-nStartCol] ) // Skip default items
2340 OSL_ENSURE( pHorizEnd[nCol-nStartCol] < MAXCOL+1, "missing stored data" );
2341 nCol = pHorizEnd[nCol-nStartCol] + 1;
2342 if ( nCol > nEndCol )
2343 return false;
2346 return true;
2349 const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow )
2351 if (nTab >= pDoc->GetTableCount())
2352 OSL_FAIL("try to access index out of bounds, FIX IT");
2353 for (;;)
2355 if ( !bRowEmpty && nCol <= nEndCol && InitForNextAttr() )
2357 const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol];
2358 rRow = nRow;
2359 rCol1 = nCol;
2360 OSL_ENSURE( pHorizEnd[nCol-nStartCol] < MAXCOL+1, "missing stored data" );
2361 nCol = pHorizEnd[nCol-nStartCol];
2362 rCol2 = nCol;
2363 ++nCol; // Count up for next call
2364 return pPat; // Found it!
2367 // Next row
2368 ++nRow;
2369 if ( nRow > nEndRow ) // Already at the end?
2370 return NULL; // Found nothing
2371 nCol = nStartCol; // Start at the left again
2373 if ( bRowEmpty || nRow > nMinNextEnd )
2374 InitForNextRow(false);
2378 inline bool IsGreater( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2380 return ( nRow1 > nRow2 ) || ( nRow1 == nRow2 && nCol1 > nCol2 );
2383 ScUsedAreaIterator::ScUsedAreaIterator( ScDocument* pDocument, SCTAB nTable,
2384 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2385 : aCellIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 )
2386 , aAttrIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 )
2387 , nNextCol( nCol1 )
2388 , nNextRow( nRow1 )
2389 , nCellCol( 0 )
2390 , nCellRow( 0 )
2391 , nAttrCol1( 0 )
2392 , nAttrCol2( 0 )
2393 , nAttrRow( 0 )
2394 , nFoundStartCol( 0 )
2395 , nFoundEndCol( 0 )
2396 , nFoundRow( 0 )
2397 , pFoundPattern( NULL )
2399 pCell = aCellIter.GetNext( nCellCol, nCellRow );
2400 pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow );
2403 ScUsedAreaIterator::~ScUsedAreaIterator()
2407 bool ScUsedAreaIterator::GetNext()
2409 // Forward iterators
2410 if ( pCell && IsGreater( nNextCol, nNextRow, nCellCol, nCellRow ) )
2411 pCell = aCellIter.GetNext( nCellCol, nCellRow );
2413 while (pCell && pCell->isEmpty())
2414 pCell = aCellIter.GetNext( nCellCol, nCellRow );
2416 if ( pPattern && IsGreater( nNextCol, nNextRow, nAttrCol2, nAttrRow ) )
2417 pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow );
2419 if ( pPattern && nAttrRow == nNextRow && nAttrCol1 < nNextCol )
2420 nAttrCol1 = nNextCol;
2422 // Find next area
2423 bool bFound = true;
2424 bool bUseCell = false;
2426 if ( pCell && pPattern )
2428 if ( IsGreater( nCellCol, nCellRow, nAttrCol1, nAttrRow ) ) // Only attributes at the beginning?
2430 maFoundCell.clear();
2431 pFoundPattern = pPattern;
2432 nFoundRow = nAttrRow;
2433 nFoundStartCol = nAttrCol1;
2434 if ( nCellRow == nAttrRow && nCellCol <= nAttrCol2 ) // Area also contains cell?
2435 nFoundEndCol = nCellCol - 1; // Only until right before the cell
2436 else
2437 nFoundEndCol = nAttrCol2; // Everything
2439 else
2441 bUseCell = true;
2442 if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attributes on the cell?
2443 pFoundPattern = pPattern;
2444 else
2445 pFoundPattern = NULL;
2448 else if ( pCell ) // Just a cell -> take over right away
2450 pFoundPattern = NULL;
2451 bUseCell = true; // Cell position
2453 else if ( pPattern ) // Just attributes -> take over right away
2455 maFoundCell.clear();
2456 pFoundPattern = pPattern;
2457 nFoundRow = nAttrRow;
2458 nFoundStartCol = nAttrCol1;
2459 nFoundEndCol = nAttrCol2;
2461 else // Nothing
2462 bFound = false;
2464 if ( bUseCell ) // Cell position
2466 if (pCell)
2467 maFoundCell = *pCell;
2468 else
2469 maFoundCell.clear();
2471 nFoundRow = nCellRow;
2472 nFoundStartCol = nFoundEndCol = nCellCol;
2475 if (bFound)
2477 nNextRow = nFoundRow;
2478 nNextCol = nFoundEndCol + 1;
2481 return bFound;
2484 ScDocAttrIterator::ScDocAttrIterator(ScDocument* pDocument, SCTAB nTable,
2485 SCCOL nCol1, SCROW nRow1,
2486 SCCOL nCol2, SCROW nRow2) :
2487 pDoc( pDocument ),
2488 nTab( nTable ),
2489 nEndCol( nCol2 ),
2490 nStartRow( nRow1 ),
2491 nEndRow( nRow2 ),
2492 nCol( nCol1 )
2494 if ( ValidTab(nTab) && nTab < pDoc->GetTableCount() && pDoc->maTabs[nTab] )
2495 pColIter = pDoc->maTabs[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow );
2496 else
2497 pColIter = NULL;
2500 ScDocAttrIterator::~ScDocAttrIterator()
2502 delete pColIter;
2505 const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 )
2507 while ( pColIter )
2509 const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 );
2510 if ( pPattern )
2512 rCol = nCol;
2513 return pPattern;
2516 delete pColIter;
2517 ++nCol;
2518 if ( nCol <= nEndCol )
2519 pColIter = pDoc->maTabs[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow );
2520 else
2521 pColIter = NULL;
2523 return NULL; // Nothing anymore
2526 ScDocRowHeightUpdater::TabRanges::TabRanges(SCTAB nTab) :
2527 mnTab(nTab), mpRanges(new ScFlatBoolRowSegments)
2531 ScDocRowHeightUpdater::ScDocRowHeightUpdater(ScDocument& rDoc, OutputDevice* pOutDev, double fPPTX, double fPPTY, const vector<TabRanges>* pTabRangesArray) :
2532 mrDoc(rDoc), mpOutDev(pOutDev), mfPPTX(fPPTX), mfPPTY(fPPTY), mpTabRangesArray(pTabRangesArray)
2536 void ScDocRowHeightUpdater::update()
2538 if (!mpTabRangesArray || mpTabRangesArray->empty())
2540 // No ranges defined. Update all rows in all tables.
2541 updateAll();
2542 return;
2545 sal_uInt32 nCellCount = 0;
2546 vector<TabRanges>::const_iterator itr = mpTabRangesArray->begin(), itrEnd = mpTabRangesArray->end();
2547 for (; itr != itrEnd; ++itr)
2549 ScFlatBoolRowSegments::RangeData aData;
2550 ScFlatBoolRowSegments::RangeIterator aRangeItr(*itr->mpRanges);
2551 for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
2553 if (!aData.mbValue)
2554 continue;
2556 nCellCount += aData.mnRow2 - aData.mnRow1 + 1;
2560 ScProgress aProgress(mrDoc.GetDocumentShell(), ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING), nCellCount);
2562 Fraction aZoom(1, 1);
2563 itr = mpTabRangesArray->begin();
2564 sal_uInt32 nProgressStart = 0;
2565 sc::RowHeightContext aCxt(mfPPTX, mfPPTY, aZoom, aZoom, mpOutDev);
2566 for (; itr != itrEnd; ++itr)
2568 SCTAB nTab = itr->mnTab;
2569 if (!ValidTab(nTab) || nTab >= mrDoc.GetTableCount() || !mrDoc.maTabs[nTab])
2570 continue;
2572 ScFlatBoolRowSegments::RangeData aData;
2573 ScFlatBoolRowSegments::RangeIterator aRangeItr(*itr->mpRanges);
2574 for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
2576 if (!aData.mbValue)
2577 continue;
2579 mrDoc.maTabs[nTab]->SetOptimalHeight(
2580 aCxt, aData.mnRow1, aData.mnRow2, &aProgress, nProgressStart);
2582 nProgressStart += aData.mnRow2 - aData.mnRow1 + 1;
2587 void ScDocRowHeightUpdater::updateAll()
2589 sal_uInt32 nCellCount = 0;
2590 for (SCTAB nTab = 0; nTab < mrDoc.GetTableCount(); ++nTab)
2592 if (!ValidTab(nTab) || !mrDoc.maTabs[nTab])
2593 continue;
2595 nCellCount += mrDoc.maTabs[nTab]->GetWeightedCount();
2598 ScProgress aProgress(mrDoc.GetDocumentShell(), ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING), nCellCount);
2600 Fraction aZoom(1, 1);
2601 sc::RowHeightContext aCxt(mfPPTX, mfPPTY, aZoom, aZoom, mpOutDev);
2602 sal_uLong nProgressStart = 0;
2603 for (SCTAB nTab = 0; nTab < mrDoc.GetTableCount(); ++nTab)
2605 if (!ValidTab(nTab) || !mrDoc.maTabs[nTab])
2606 continue;
2608 mrDoc.maTabs[nTab]->SetOptimalHeight(aCxt, 0, MAXROW, &aProgress, nProgressStart);
2609 nProgressStart += mrDoc.maTabs[nTab]->GetWeightedCount();
2613 ScAttrRectIterator::ScAttrRectIterator(ScDocument* pDocument, SCTAB nTable,
2614 SCCOL nCol1, SCROW nRow1,
2615 SCCOL nCol2, SCROW nRow2) :
2616 pDoc( pDocument ),
2617 nTab( nTable ),
2618 nEndCol( nCol2 ),
2619 nStartRow( nRow1 ),
2620 nEndRow( nRow2 ),
2621 nIterStartCol( nCol1 ),
2622 nIterEndCol( nCol1 )
2624 if ( ValidTab(nTab) && nTab < pDoc->GetTableCount() && pDoc->maTabs[nTab] )
2626 pColIter = pDoc->maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow );
2627 while ( nIterEndCol < nEndCol &&
2628 pDoc->maTabs[nTab]->aCol[nIterEndCol].IsAllAttrEqual(
2629 pDoc->maTabs[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) )
2630 ++nIterEndCol;
2632 else
2633 pColIter = NULL;
2636 ScAttrRectIterator::~ScAttrRectIterator()
2638 delete pColIter;
2641 void ScAttrRectIterator::DataChanged()
2643 if (pColIter)
2645 SCROW nNextRow = pColIter->GetNextRow();
2646 delete pColIter;
2647 pColIter = pDoc->maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( nNextRow, nEndRow );
2651 const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2,
2652 SCROW& rRow1, SCROW& rRow2 )
2654 while ( pColIter )
2656 const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 );
2657 if ( pPattern )
2659 rCol1 = nIterStartCol;
2660 rCol2 = nIterEndCol;
2661 return pPattern;
2664 delete pColIter;
2665 nIterStartCol = nIterEndCol+1;
2666 if ( nIterStartCol <= nEndCol )
2668 nIterEndCol = nIterStartCol;
2669 pColIter = pDoc->maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow );
2670 while ( nIterEndCol < nEndCol &&
2671 pDoc->maTabs[nTab]->aCol[nIterEndCol].IsAllAttrEqual(
2672 pDoc->maTabs[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) )
2673 ++nIterEndCol;
2675 else
2676 pColIter = NULL;
2678 return NULL; // Nothing anymore
2681 SCROW ScRowBreakIterator::NOT_FOUND = -1;
2683 ScRowBreakIterator::ScRowBreakIterator(set<SCROW>& rBreaks) :
2684 mrBreaks(rBreaks),
2685 maItr(rBreaks.begin()), maEnd(rBreaks.end())
2689 SCROW ScRowBreakIterator::first()
2691 maItr = mrBreaks.begin();
2692 return maItr == maEnd ? NOT_FOUND : *maItr;
2695 SCROW ScRowBreakIterator::next()
2697 ++maItr;
2698 return maItr == maEnd ? NOT_FOUND : *maItr;
2701 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */