Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / dociter.cxx
blob00309ceb871b555993acec7b6c1c9d8e9ff9bfc9
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/numformat.hxx>
21 #include <svl/zforlist.hxx>
23 #include <global.hxx>
24 #include <dociter.hxx>
25 #include <document.hxx>
26 #include <docsh.hxx>
27 #include <table.hxx>
28 #include <column.hxx>
29 #include <formulacell.hxx>
30 #include <attarray.hxx>
31 #include <patattr.hxx>
32 #include <docoptio.hxx>
33 #include <segmenttree.hxx>
34 #include <progress.hxx>
35 #include <queryparam.hxx>
36 #include <queryentry.hxx>
37 #include <globstr.hrc>
38 #include <scresid.hxx>
39 #include <cellvalue.hxx>
40 #include <scmatrix.hxx>
41 #include <rowheightcontext.hxx>
42 #include <queryevaluator.hxx>
44 #include <o3tl/safeint.hxx>
45 #include <tools/fract.hxx>
46 #include <editeng/editobj.hxx>
47 #include <svl/sharedstring.hxx>
48 #include <unotools/collatorwrapper.hxx>
49 #include <sal/log.hxx>
51 #include <algorithm>
52 #include <limits>
53 #include <vector>
55 using ::rtl::math::approxEqual;
56 using ::std::vector;
57 using ::std::set;
59 // iterators have very high frequency use -> custom debug.
60 // #define debugiter(...) fprintf(stderr, __VA_ARGS__)
61 #define debugiter(...)
63 static void ScAttrArray_IterGetNumberFormat( sal_uInt32& nFormat, const ScAttrArray*& rpArr,
64 SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow,
65 const ScDocument& rDoc, const ScInterpreterContext* pContext = nullptr )
67 if ( rpArr == pNewArr && nAttrEndRow >= nRow )
68 return;
70 SCROW nRowStart = 0;
71 SCROW nRowEnd = rDoc.MaxRow();
72 const ScPatternAttr* pPattern = pNewArr->GetPatternRange( nRowStart, nRowEnd, nRow );
73 if( !pPattern )
75 pPattern = &rDoc.getCellAttributeHelper().getDefaultCellAttribute();
76 nRowEnd = rDoc.MaxRow();
79 if (pContext)
80 nFormat = pPattern->GetNumberFormat(*pContext);
81 else
82 nFormat = pPattern->GetNumberFormat(rDoc.GetFormatTable());
83 rpArr = pNewArr;
84 nAttrEndRow = nRowEnd;
87 ScValueIterator::ScValueIterator(ScInterpreterContext& rContext, const ScRange& rRange,
88 SubtotalFlags nSubTotalFlags, bool bTextZero )
89 : mrDoc(*rContext.mpDoc)
90 , mrContext(rContext)
91 , pAttrArray(nullptr)
92 , nNumFormat(0) // Initialized in GetNumberFormat
93 , nNumFmtIndex(0)
94 , maStartPos(rRange.aStart)
95 , maEndPos(rRange.aEnd)
96 , mnCol(0)
97 , mnTab(0)
98 , nAttrEndRow(0)
99 , mnSubTotalFlags(nSubTotalFlags)
100 , nNumFmtType(SvNumFormatType::UNDEFINED)
101 , bNumValid(false)
102 , bCalcAsShown((*rContext.mpDoc).GetDocOptions().IsCalcAsShown())
103 , bTextAsZero(bTextZero)
104 , mpCells(nullptr)
106 SCTAB nDocMaxTab = mrDoc.GetTableCount() - 1;
108 if (!mrDoc.ValidCol(maStartPos.Col())) maStartPos.SetCol(mrDoc.MaxCol());
109 if (!mrDoc.ValidCol(maEndPos.Col())) maEndPos.SetCol(mrDoc.MaxCol());
110 if (!mrDoc.ValidRow(maStartPos.Row())) maStartPos.SetRow(mrDoc.MaxRow());
111 if (!mrDoc.ValidRow(maEndPos.Row())) maEndPos.SetRow(mrDoc.MaxRow());
112 if (!ValidTab(maStartPos.Tab()) || maStartPos.Tab() > nDocMaxTab) maStartPos.SetTab(nDocMaxTab);
113 if (!ValidTab(maEndPos.Tab()) || maEndPos.Tab() > nDocMaxTab) maEndPos.SetTab(nDocMaxTab);
116 SCROW ScValueIterator::GetRow() const
118 // Position of the head of the current block + offset within the block
119 // equals the logical element position.
120 return maCurPos.first->position + maCurPos.second;
123 void ScValueIterator::IncBlock()
125 ++maCurPos.first;
126 maCurPos.second = 0;
129 void ScValueIterator::IncPos()
131 if (maCurPos.second + 1 < maCurPos.first->size)
132 // Move within the same block.
133 ++maCurPos.second;
134 else
135 // Move to the next block.
136 IncBlock();
139 bool ScValueIterator::GetThis(double& rValue, FormulaError& rErr)
141 while (true)
143 bool bNextColumn = !mpCells || maCurPos.first == mpCells->end();
144 if (!bNextColumn)
146 if (GetRow() > maEndPos.Row())
147 bNextColumn = true;
150 ScColumn* pCol;
151 if (!bNextColumn)
152 pCol = &(mrDoc.maTabs[mnTab])->aCol[mnCol];
153 else
155 // Find the next available column.
158 ++mnCol;
159 while (mnCol > maEndPos.Col() || mnCol >= mrDoc.maTabs[mnTab]->GetAllocatedColumnsCount())
161 mnCol = maStartPos.Col();
162 ++mnTab;
163 if (mnTab > maEndPos.Tab())
165 rErr = FormulaError::NONE;
166 return false;
169 pCol = &(mrDoc.maTabs[mnTab])->aCol[mnCol];
171 while (pCol->IsEmptyData());
173 mpCells = &pCol->maCells;
174 maCurPos = mpCells->position(maStartPos.Row());
177 SCROW nCurRow = GetRow();
178 SCROW nLastRow;
179 // Skip all filtered or hidden rows, depending on mnSubTotalFlags
180 if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) &&
181 mrDoc.RowFiltered( nCurRow, mnTab, nullptr, &nLastRow ) ) ||
182 ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) &&
183 mrDoc.RowHidden( nCurRow, mnTab, nullptr, &nLastRow ) ) )
185 maCurPos = mpCells->position(maCurPos.first, nLastRow+1);
186 continue;
189 switch (maCurPos.first->type)
191 case sc::element_type_numeric:
193 bNumValid = false;
194 rValue = sc::numeric_block::at(*maCurPos.first->data, maCurPos.second);
195 rErr = FormulaError::NONE;
196 if (bCalcAsShown)
198 ScAttrArray_IterGetNumberFormat(nNumFormat, pAttrArray,
199 nAttrEndRow, pCol->pAttrArray.get(), nCurRow, mrDoc, &mrContext);
200 rValue = mrDoc.RoundValueAsShown(rValue, nNumFormat, &mrContext);
202 return true; // Found it!
204 break;
205 case sc::element_type_formula:
207 ScFormulaCell& rCell = *sc::formula_block::at(*maCurPos.first->data, maCurPos.second);
208 if ( ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) && rCell.IsSubTotal() )
210 // Skip subtotal formula cells.
211 IncPos();
212 break;
215 if (rCell.GetErrorOrValue(rErr, rValue))
217 if ( rErr != FormulaError::NONE && ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
219 IncPos();
220 break;
222 bNumValid = false;
223 return true; // Found it!
225 else if (bTextAsZero)
227 rValue = 0.0;
228 bNumValid = false;
229 return true;
231 IncPos();
233 break;
234 case sc::element_type_string :
235 case sc::element_type_edittext :
237 if (bTextAsZero)
239 rErr = FormulaError::NONE;
240 rValue = 0.0;
241 nNumFmtType = SvNumFormatType::NUMBER;
242 nNumFmtIndex = 0;
243 bNumValid = true;
244 return true;
246 IncBlock();
248 break;
249 case sc::element_type_empty:
250 default:
251 // Skip the whole block.
252 IncBlock();
257 void ScValueIterator::GetCurNumFmtInfo( SvNumFormatType& nType, sal_uInt32& nIndex )
259 if (!bNumValid && mnTab < mrDoc.GetTableCount())
261 SCROW nCurRow = GetRow();
262 const ScColumn* pCol = &(mrDoc.maTabs[mnTab])->aCol[mnCol];
263 nNumFmtIndex = pCol->GetNumberFormat(mrContext, nCurRow);
264 nNumFmtType = mrContext.NFGetType(nNumFmtIndex);
265 bNumValid = true;
268 nType = nNumFmtType;
269 nIndex = nNumFmtIndex;
272 bool ScValueIterator::GetFirst(double& rValue, FormulaError& rErr)
274 mnCol = maStartPos.Col();
275 mnTab = maStartPos.Tab();
277 const ScTable* pTab = mrDoc.FetchTable(mnTab);
278 if (!pTab)
279 return false;
281 nNumFormat = 0; // Initialized in GetNumberFormat
282 pAttrArray = nullptr;
283 nAttrEndRow = 0;
285 auto nCol = maStartPos.Col();
286 if (nCol < pTab->GetAllocatedColumnsCount())
288 mpCells = &pTab->aCol[nCol].maCells;
289 maCurPos = mpCells->position(maStartPos.Row());
291 else
292 mpCells = nullptr;
293 return GetThis(rValue, rErr);
296 bool ScValueIterator::GetNext(double& rValue, FormulaError& rErr)
298 IncPos();
299 return GetThis(rValue, rErr);
302 ScDBQueryDataIterator::DataAccess::DataAccess()
306 ScDBQueryDataIterator::DataAccess::~DataAccess()
310 const sc::CellStoreType* ScDBQueryDataIterator::GetColumnCellStore(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
312 ScTable* pTab = rDoc.FetchTable(nTab);
313 if (!pTab)
314 return nullptr;
315 if (nCol >= pTab->GetAllocatedColumnsCount())
316 return nullptr;
317 return &pTab->aCol[nCol].maCells;
320 const ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
322 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
323 ScColumn* pCol = &rDoc.maTabs[nTab]->aCol[nCol];
324 return pCol->pAttrArray.get();
327 bool ScDBQueryDataIterator::IsQueryValid(
328 ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, const ScRefCellValue* pCell)
330 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
331 ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], rParam);
332 return queryEvaluator.ValidQuery(nRow, pCell);
335 ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(ScDBQueryParamInternal* pParam, ScDocument& rDoc, const ScInterpreterContext& rContext)
336 : mpCells(nullptr)
337 , mpParam(pParam)
338 , mrDoc(rDoc)
339 , mrContext(rContext)
340 , pAttrArray(nullptr)
341 , nNumFormat(0) // Initialized in GetNumberFormat
342 , nNumFmtIndex(0)
343 , nCol(mpParam->mnField)
344 , nRow(mpParam->nRow1)
345 , nAttrEndRow(0)
346 , nTab(mpParam->nTab)
347 , nNumFmtType(SvNumFormatType::ALL)
348 , bCalcAsShown(rDoc.GetDocOptions().IsCalcAsShown())
350 SCSIZE i;
351 SCSIZE nCount = mpParam->GetEntryCount();
352 for (i=0; (i<nCount) && (mpParam->GetEntry(i).bDoQuery); i++)
354 ScQueryEntry& rEntry = mpParam->GetEntry(i);
355 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
356 rItems.resize(1);
357 ScQueryEntry::Item& rItem = rItems.front();
358 sal_uInt32 nIndex = 0;
359 bool bNumber = mrDoc.GetFormatTable()->IsNumberFormat(
360 rItem.maString.getString(), nIndex, rItem.mfVal);
361 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
365 ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal()
369 bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue)
371 // Start with the current row position, and find the first row position
372 // that satisfies the query.
374 // If the query starts in the same column as the result vector we can
375 // prefetch the cell which saves us one fetch in the success case.
376 SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField;
377 ScRefCellValue aCell;
379 while (true)
381 if (maCurPos.first == mpCells->end() || nRow > mpParam->nRow2)
383 // Bottom of the range reached. Bail out.
384 rValue.mnError = FormulaError::NONE;
385 return false;
388 if (maCurPos.first->type == sc::element_type_empty)
390 // Skip the whole empty block.
391 incBlock();
392 continue;
395 ScRefCellValue* pCell = nullptr;
396 if (nCol == static_cast<SCCOL>(nFirstQueryField))
398 aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
399 pCell = &aCell;
402 if (ScDBQueryDataIterator::IsQueryValid(mrDoc, *mpParam, nTab, nRow, pCell))
404 if (!pCell)
405 aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
406 switch (aCell.getType())
408 case CELLTYPE_VALUE:
410 rValue.mfValue = aCell.getDouble();
411 rValue.mbIsNumber = true;
412 if ( bCalcAsShown )
414 const ScAttrArray* pNewAttrArray =
415 ScDBQueryDataIterator::GetAttrArrayByCol(mrDoc, nTab, nCol);
416 ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray,
417 nAttrEndRow, pNewAttrArray, nRow, mrDoc );
418 rValue.mfValue = mrDoc.RoundValueAsShown( rValue.mfValue, nNumFormat );
420 nNumFmtType = SvNumFormatType::NUMBER;
421 nNumFmtIndex = 0;
422 rValue.mnError = FormulaError::NONE;
423 return true; // Found it!
426 case CELLTYPE_FORMULA:
428 if (aCell.getFormula()->IsValue())
430 rValue.mfValue = aCell.getFormula()->GetValue();
431 rValue.mbIsNumber = true;
432 mrDoc.GetNumberFormatInfo(
433 mrContext, nNumFmtType, nNumFmtIndex, ScAddress(nCol, nRow, nTab));
434 rValue.mnError = aCell.getFormula()->GetErrCode();
435 return true; // Found it!
437 else if(mpParam->mbSkipString)
438 incPos();
439 else
441 rValue.maString = aCell.getFormula()->GetString().getString();
442 rValue.mfValue = 0.0;
443 rValue.mnError = aCell.getFormula()->GetErrCode();
444 rValue.mbIsNumber = false;
445 return true;
448 break;
449 case CELLTYPE_STRING:
450 case CELLTYPE_EDIT:
451 if (mpParam->mbSkipString)
452 incPos();
453 else
455 rValue.maString = aCell.getString(&mrDoc);
456 rValue.mfValue = 0.0;
457 rValue.mnError = FormulaError::NONE;
458 rValue.mbIsNumber = false;
459 return true;
461 break;
462 default:
463 incPos();
466 else
467 incPos();
469 // statement unreachable
472 bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue)
474 if (mpParam->bHasHeader)
475 ++nRow;
477 mpCells = ScDBQueryDataIterator::GetColumnCellStore(mrDoc, nTab, nCol);
478 if (!mpCells)
479 return false;
481 maCurPos = mpCells->position(nRow);
482 return getCurrent(rValue);
485 bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue)
487 if (!mpCells || maCurPos.first == mpCells->end())
488 return false;
490 incPos();
491 return getCurrent(rValue);
494 void ScDBQueryDataIterator::DataAccessInternal::incBlock()
496 ++maCurPos.first;
497 maCurPos.second = 0;
499 nRow = maCurPos.first->position;
502 void ScDBQueryDataIterator::DataAccessInternal::incPos()
504 if (maCurPos.second + 1 < maCurPos.first->size)
506 // Move within the same block.
507 ++maCurPos.second;
508 ++nRow;
510 else
511 // Move to the next block.
512 incBlock();
515 ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(ScDBQueryParamMatrix* pParam)
516 : mpParam(pParam)
517 , mnCurRow(0)
519 SCSIZE nC, nR;
520 mpParam->mpMatrix->GetDimensions(nC, nR);
521 mnRows = static_cast<SCROW>(nR);
524 ScDBQueryDataIterator::DataAccessMatrix::~DataAccessMatrix()
528 bool ScDBQueryDataIterator::DataAccessMatrix::getCurrent(Value& rValue)
530 // Starting from row == mnCurRow, get the first row that satisfies all the
531 // query parameters.
532 for ( ;mnCurRow < mnRows; ++mnCurRow)
534 const ScMatrix& rMat = *mpParam->mpMatrix;
535 if (rMat.IsEmpty(mpParam->mnField, mnCurRow))
536 // Don't take empty values into account.
537 continue;
539 bool bIsStrVal = rMat.IsStringOrEmpty(mpParam->mnField, mnCurRow);
540 if (bIsStrVal && mpParam->mbSkipString)
541 continue;
543 if (isValidQuery(mnCurRow, rMat))
545 rValue.maString = rMat.GetString(mpParam->mnField, mnCurRow).getString();
546 rValue.mfValue = rMat.GetDouble(mpParam->mnField, mnCurRow);
547 rValue.mbIsNumber = !bIsStrVal;
548 rValue.mnError = FormulaError::NONE;
549 return true;
552 return false;
555 bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value& rValue)
557 mnCurRow = mpParam->bHasHeader ? 1 : 0;
558 return getCurrent(rValue);
561 bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value& rValue)
563 ++mnCurRow;
564 return getCurrent(rValue);
567 namespace {
569 bool isQueryByValue(const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
571 if (rItem.meType == ScQueryEntry::ByString)
572 return false;
574 if (!rMat.IsValueOrEmpty(nCol, nRow))
575 return false;
577 return true;
580 bool isQueryByString(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
582 switch (rEntry.eOp)
584 case SC_EQUAL:
585 case SC_NOT_EQUAL:
586 case SC_CONTAINS:
587 case SC_DOES_NOT_CONTAIN:
588 case SC_BEGINS_WITH:
589 case SC_ENDS_WITH:
590 case SC_DOES_NOT_BEGIN_WITH:
591 case SC_DOES_NOT_END_WITH:
592 return true;
593 default:
597 return rItem.meType == ScQueryEntry::ByString && rMat.IsStringOrEmpty(nCol, nRow);
602 bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow, const ScMatrix& rMat) const
604 SCSIZE nEntryCount = mpParam->GetEntryCount();
605 vector<bool> aResults;
606 aResults.reserve(nEntryCount);
608 const CollatorWrapper& rCollator = ScGlobal::GetCollator(mpParam->bCaseSens);
610 for (SCSIZE i = 0; i < nEntryCount; ++i)
612 const ScQueryEntry& rEntry = mpParam->GetEntry(i);
613 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
614 if (!rEntry.bDoQuery)
615 continue;
617 switch (rEntry.eOp)
619 case SC_EQUAL:
620 case SC_LESS:
621 case SC_GREATER:
622 case SC_LESS_EQUAL:
623 case SC_GREATER_EQUAL:
624 case SC_NOT_EQUAL:
625 break;
626 default:
627 // Only the above operators are supported.
628 SAL_WARN("sc.core", "Unsupported operator " << rEntry.eOp
629 << " in ScDBQueryDataIterator::DataAccessMatrix::isValidQuery()");
630 continue;
633 bool bValid = false;
635 SCSIZE nField = static_cast<SCSIZE>(rEntry.nField);
636 if (isQueryByValue(rItem, rMat, nField, nRow))
638 // By value
639 double fMatVal = rMat.GetDouble(nField, nRow);
640 bool bEqual = approxEqual(fMatVal, rItem.mfVal);
641 switch (rEntry.eOp)
643 case SC_EQUAL:
644 bValid = bEqual;
645 break;
646 case SC_LESS:
647 bValid = (fMatVal < rItem.mfVal) && !bEqual;
648 break;
649 case SC_GREATER:
650 bValid = (fMatVal > rItem.mfVal) && !bEqual;
651 break;
652 case SC_LESS_EQUAL:
653 bValid = (fMatVal < rItem.mfVal) || bEqual;
654 break;
655 case SC_GREATER_EQUAL:
656 bValid = (fMatVal > rItem.mfVal) || bEqual;
657 break;
658 case SC_NOT_EQUAL:
659 bValid = !bEqual;
660 break;
661 default:
665 else if (isQueryByString(rEntry, rItem, rMat, nField, nRow))
667 // By string
670 // Equality check first.
671 svl::SharedString aMatStr = rMat.GetString(nField, nRow);
672 svl::SharedString aQueryStr = rEntry.GetQueryItem().maString;
673 bool bDone = false;
674 rtl_uString* p1 = mpParam->bCaseSens ? aMatStr.getData() : aMatStr.getDataIgnoreCase();
675 rtl_uString* p2 = mpParam->bCaseSens ? aQueryStr.getData() : aQueryStr.getDataIgnoreCase();
676 switch (rEntry.eOp)
678 case SC_EQUAL:
679 bValid = (p1 == p2);
680 bDone = true;
681 break;
682 case SC_NOT_EQUAL:
683 bValid = (p1 != p2);
684 bDone = true;
685 break;
686 default:
690 if (bDone)
691 break;
693 // Unequality check using collator.
694 sal_Int32 nCompare = rCollator.compareString(aMatStr.getString(), aQueryStr.getString());
695 switch (rEntry.eOp)
697 case SC_LESS :
698 bValid = (nCompare < 0);
699 break;
700 case SC_GREATER :
701 bValid = (nCompare > 0);
702 break;
703 case SC_LESS_EQUAL :
704 bValid = (nCompare <= 0);
705 break;
706 case SC_GREATER_EQUAL :
707 bValid = (nCompare >= 0);
708 break;
709 default:
713 while (false);
716 if (aResults.empty())
717 // First query entry.
718 aResults.push_back(bValid);
719 else if (rEntry.eConnect == SC_AND)
721 // For AND op, tuck the result into the last result value.
722 size_t n = aResults.size();
723 aResults[n-1] = aResults[n-1] && bValid;
725 else
726 // For OR op, store its own result.
727 aResults.push_back(bValid);
730 // Row is valid as long as there is at least one result being true.
731 return std::find(aResults.begin(), aResults.end(), true) != aResults.end();
734 ScDBQueryDataIterator::Value::Value()
735 : mfValue(std::numeric_limits<double>::quiet_NaN())
736 , mnError(FormulaError::NONE), mbIsNumber(true)
740 ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument& rDocument, const ScInterpreterContext& rContext, std::unique_ptr<ScDBQueryParamBase> pParam) :
741 mpParam (std::move(pParam))
743 switch (mpParam->GetType())
745 case ScDBQueryParamBase::INTERNAL:
747 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(mpParam.get());
748 mpData.reset(new DataAccessInternal(p, rDocument, rContext));
750 break;
751 case ScDBQueryParamBase::MATRIX:
753 ScDBQueryParamMatrix* p = static_cast<ScDBQueryParamMatrix*>(mpParam.get());
754 mpData.reset(new DataAccessMatrix(p));
759 bool ScDBQueryDataIterator::GetFirst(Value& rValue)
761 return mpData->getFirst(rValue);
764 bool ScDBQueryDataIterator::GetNext(Value& rValue)
766 return mpData->getNext(rValue);
769 ScFormulaGroupIterator::ScFormulaGroupIterator( ScDocument& rDoc ) :
770 mrDoc(rDoc),
771 mnTab(0),
772 mnCol(0),
773 mnIndex(0)
775 ScTable *pTab = mrDoc.FetchTable(mnTab);
776 ScColumn *pCol = pTab ? pTab->FetchColumn(mnCol) : nullptr;
777 if (pCol)
779 mbNullCol = false;
780 maEntries = pCol->GetFormulaGroupEntries();
782 else
783 mbNullCol = true;
786 sc::FormulaGroupEntry* ScFormulaGroupIterator::first()
788 return next();
791 sc::FormulaGroupEntry* ScFormulaGroupIterator::next()
793 if (mnIndex >= maEntries.size() || mbNullCol)
795 while (mnIndex >= maEntries.size() || mbNullCol)
797 mnIndex = 0;
798 mnCol++;
799 if (mnCol > mrDoc.MaxCol())
801 mnCol = 0;
802 mnTab++;
803 if (mnTab >= mrDoc.GetTableCount())
804 return nullptr;
806 ScTable *pTab = mrDoc.FetchTable(mnTab);
807 ScColumn *pCol = (pTab && pTab->IsColValid(mnCol)) ? pTab->FetchColumn(mnCol) : nullptr;
808 if (pCol)
810 mbNullCol = false;
811 maEntries = pCol->GetFormulaGroupEntries();
813 else
814 mbNullCol = true;
818 return &maEntries[mnIndex++];
821 ScCellIterator::ScCellIterator( ScDocument& rDoc, const ScRange& rRange, SubtotalFlags nSubTotalFlags ) :
822 mrDoc(rDoc),
823 maStartPos(rRange.aStart),
824 maEndPos(rRange.aEnd),
825 mnSubTotalFlags(nSubTotalFlags)
827 init();
830 void ScCellIterator::incBlock()
832 ++maCurColPos.first;
833 maCurColPos.second = 0;
835 maCurPos.SetRow(maCurColPos.first->position);
838 void ScCellIterator::incPos()
840 if (maCurColPos.second + 1 < maCurColPos.first->size)
842 // Move within the same block.
843 ++maCurColPos.second;
844 maCurPos.IncRow();
846 else
847 // Move to the next block.
848 incBlock();
851 void ScCellIterator::setPos(size_t nPos)
853 maCurColPos = getColumn()->maCells.position(maCurColPos.first, nPos);
854 maCurPos.SetRow(nPos);
857 const ScColumn* ScCellIterator::getColumn() const
859 return &mrDoc.maTabs[maCurPos.Tab()]->aCol[maCurPos.Col()];
862 void ScCellIterator::init()
864 SCTAB nDocMaxTab = mrDoc.GetTableCount() - 1;
866 PutInOrder(maStartPos, maEndPos);
868 if (!mrDoc.ValidCol(maStartPos.Col())) maStartPos.SetCol(mrDoc.MaxCol());
869 if (!mrDoc.ValidCol(maEndPos.Col())) maEndPos.SetCol(mrDoc.MaxCol());
870 if (!mrDoc.ValidRow(maStartPos.Row())) maStartPos.SetRow(mrDoc.MaxRow());
871 if (!mrDoc.ValidRow(maEndPos.Row())) maEndPos.SetRow(mrDoc.MaxRow());
872 if (!ValidTab(maStartPos.Tab(), nDocMaxTab)) maStartPos.SetTab(nDocMaxTab);
873 if (!ValidTab(maEndPos.Tab(), nDocMaxTab)) maEndPos.SetTab(nDocMaxTab);
875 while (maEndPos.Tab() > 0 && !mrDoc.maTabs[maEndPos.Tab()])
876 maEndPos.IncTab(-1); // Only the tables in use
878 if (maStartPos.Tab() > maEndPos.Tab())
879 maStartPos.SetTab(maEndPos.Tab());
881 if (!mrDoc.maTabs[maStartPos.Tab()])
883 assert(!"Table not found");
884 maStartPos = ScAddress(mrDoc.MaxCol()+1, mrDoc.MaxRow()+1, MAXTAB+1); // -> Abort on GetFirst.
886 else
888 maStartPos.SetCol(mrDoc.maTabs[maStartPos.Tab()]->ClampToAllocatedColumns(maStartPos.Col()));
891 maCurPos = maStartPos;
894 bool ScCellIterator::getCurrent()
896 const ScColumn* pCol = getColumn();
898 while (true)
900 bool bNextColumn = maCurColPos.first == pCol->maCells.end();
901 if (!bNextColumn)
903 if (maCurPos.Row() > maEndPos.Row())
904 bNextColumn = true;
907 if (bNextColumn)
909 // Move to the next column.
910 maCurPos.SetRow(maStartPos.Row());
913 maCurPos.IncCol();
914 while (maCurPos.Col() >= mrDoc.GetAllocatedColumnsCount(maCurPos.Tab())
915 || maCurPos.Col() > maEndPos.Col())
917 maCurPos.SetCol(maStartPos.Col());
918 maCurPos.IncTab();
919 if (maCurPos.Tab() > maEndPos.Tab())
921 maCurCell.clear();
922 return false;
925 pCol = getColumn();
927 while (pCol->IsEmptyData());
929 maCurColPos = pCol->maCells.position(maCurPos.Row());
932 if (maCurColPos.first->type == sc::element_type_empty)
934 incBlock();
935 continue;
938 SCROW nLastRow;
939 // Skip all filtered or hidden rows, depending on mSubTotalFlags
940 if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) &&
941 pCol->GetDoc().RowFiltered(maCurPos.Row(), maCurPos.Tab(), nullptr, &nLastRow) ) ||
942 ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) &&
943 pCol->GetDoc().RowHidden(maCurPos.Row(), maCurPos.Tab(), nullptr, &nLastRow) ) )
945 setPos(nLastRow+1);
946 continue;
949 if (maCurColPos.first->type == sc::element_type_formula)
951 if ( mnSubTotalFlags != SubtotalFlags::NONE )
953 ScFormulaCell* pCell = sc::formula_block::at(*maCurColPos.first->data, maCurColPos.second);
954 // Skip formula cells with Subtotal formulae or errors, depending on mnSubTotalFlags
955 if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) && pCell->IsSubTotal() ) ||
956 ( ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) && pCell->GetErrCode() != FormulaError::NONE ) )
958 incPos();
959 continue;
964 maCurCell = sc::toRefCell(maCurColPos.first, maCurColPos.second);
965 return true;
967 return false;
970 OUString ScCellIterator::getString() const
972 return maCurCell.getString(&mrDoc);
975 ScCellValue ScCellIterator::getCellValue() const
977 switch (maCurCell.getType())
979 case CELLTYPE_STRING:
980 return ScCellValue(maCurCell.getSharedString());
981 break;
982 case CELLTYPE_EDIT:
983 return ScCellValue(maCurCell.getEditText()->Clone());
984 break;
985 case CELLTYPE_VALUE:
986 return ScCellValue(maCurCell.getDouble());
987 break;
988 case CELLTYPE_FORMULA:
989 return ScCellValue(maCurCell.getFormula()->Clone());
990 break;
991 default:
992 return ScCellValue();
996 bool ScCellIterator::hasString() const
998 return maCurCell.hasString();
1001 bool ScCellIterator::isEmpty() const
1003 return maCurCell.isEmpty();
1006 bool ScCellIterator::equalsWithoutFormat( const ScAddress& rPos ) const
1008 ScRefCellValue aOther(mrDoc, rPos);
1009 return maCurCell.equalsWithoutFormat(aOther);
1012 bool ScCellIterator::first()
1014 if (!ValidTab(maCurPos.Tab()))
1015 return false;
1017 maCurPos = maStartPos;
1018 const ScColumn* pCol = getColumn();
1020 maCurColPos = pCol->maCells.position(maCurPos.Row());
1021 return getCurrent();
1024 bool ScCellIterator::next()
1026 incPos();
1027 return getCurrent();
1030 ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument& rDocument, SCTAB nTable,
1031 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
1032 rDoc( rDocument ),
1033 mnTab( nTable ),
1034 nStartCol( nCol1 ),
1035 nEndCol( nCol2 ),
1036 nStartRow( nRow1 ),
1037 nEndRow( nRow2 ),
1038 mnCol( nCol1 ),
1039 mnRow( nRow1 ),
1040 mbMore( false )
1042 assert(mnTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
1044 const ScTable* pTab = rDoc.FetchTable(mnTab);
1045 if (!pTab)
1046 return;
1048 nEndCol = pTab->ClampToAllocatedColumns(nEndCol);
1049 if (nEndCol < nStartCol) // E.g., somewhere completely outside allocated area
1050 nEndCol = nStartCol - 1; // Empty
1052 maColPositions.reserve( nEndCol-nStartCol+1 );
1054 SetTab( mnTab );
1057 ScHorizontalCellIterator::~ScHorizontalCellIterator()
1061 void ScHorizontalCellIterator::SetTab( SCTAB nTabP )
1063 mbMore = false;
1064 mnTab = nTabP;
1065 mnRow = nStartRow;
1066 mnCol = nStartCol;
1067 maColPositions.resize(0);
1069 // Set the start position in each column.
1070 for (SCCOL i = nStartCol; i <= nEndCol; ++i)
1072 ScColumn* pCol = &rDoc.maTabs[mnTab]->aCol[i];
1073 ColParam aParam;
1074 aParam.maPos = pCol->maCells.position(nStartRow).first;
1075 aParam.maEnd = pCol->maCells.end();
1076 aParam.mnCol = i;
1078 // find first non-empty element.
1079 while (aParam.maPos != aParam.maEnd) {
1080 if (aParam.maPos->type == sc::element_type_empty)
1081 ++aParam.maPos;
1082 else
1084 maColPositions.push_back( aParam );
1085 break;
1090 if (maColPositions.empty())
1091 return;
1093 maColPos = maColPositions.begin();
1094 mbMore = true;
1095 SkipInvalid();
1098 ScRefCellValue* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow )
1100 if (!mbMore)
1102 debugiter("no more !\n");
1103 return nullptr;
1106 // Return the current non-empty cell, and move the cursor to the next one.
1107 ColParam& r = *maColPos;
1109 rCol = mnCol = r.mnCol;
1110 rRow = mnRow;
1111 debugiter("return col %d row %d\n", (int)rCol, (int)rRow);
1113 size_t nOffset = static_cast<size_t>(mnRow) - r.maPos->position;
1114 maCurCell = sc::toRefCell(r.maPos, nOffset);
1115 Advance();
1116 debugiter("advance to: col %d row %d\n", (int)maColPos->mnCol, (int)mnRow);
1118 return &maCurCell;
1121 bool ScHorizontalCellIterator::GetPos( SCCOL& rCol, SCROW& rRow )
1123 rCol = mnCol;
1124 rRow = mnRow;
1125 return mbMore;
1128 // Skip any invalid / empty cells across the current row,
1129 // we only advance the cursor if the current entry is invalid.
1130 // if we return true we have a valid cursor (or hit the end)
1131 bool ScHorizontalCellIterator::SkipInvalidInRow()
1133 assert (mbMore);
1134 assert (maColPos != maColPositions.end());
1136 // Find the next non-empty cell in the current row.
1137 while( maColPos != maColPositions.end() )
1139 ColParam& r = *maColPos;
1140 assert (r.maPos != r.maEnd);
1142 size_t nRow = static_cast<size_t>(mnRow);
1144 if (nRow >= r.maPos->position)
1146 if (nRow < r.maPos->position + r.maPos->size)
1148 mnCol = maColPos->mnCol;
1149 debugiter("found valid cell at column %d, row %d\n",
1150 (int)mnCol, (int)mnRow);
1151 assert(r.maPos->type != sc::element_type_empty);
1152 return true;
1154 else
1156 bool bMoreBlocksInColumn = false;
1157 // This block is behind the current row position. Advance the block.
1158 for (++r.maPos; r.maPos != r.maEnd; ++r.maPos)
1160 if (nRow < r.maPos->position + r.maPos->size &&
1161 r.maPos->type != sc::element_type_empty)
1163 bMoreBlocksInColumn = true;
1164 break;
1167 if (!bMoreBlocksInColumn)
1169 debugiter("remove column %d at row %d\n",
1170 (int)maColPos->mnCol, (int)nRow);
1171 maColPos = maColPositions.erase(maColPos);
1172 if (maColPositions.empty())
1174 debugiter("no more columns\n");
1175 mbMore = false;
1178 else
1180 debugiter("advanced column %d to block starting row %d, retrying\n",
1181 (int)maColPos->mnCol, r.maPos->position);
1185 else
1187 debugiter("skip empty cells at column %d, row %d\n",
1188 (int)maColPos->mnCol, (int)nRow);
1189 ++maColPos;
1193 // No more columns with anything interesting in them ?
1194 if (maColPositions.empty())
1196 debugiter("no more live columns left - done\n");
1197 mbMore = false;
1198 return true;
1201 return false;
1204 /// Find the next row that has some real content in one of its columns.
1205 SCROW ScHorizontalCellIterator::FindNextNonEmptyRow()
1207 size_t nNextRow = rDoc.MaxRow()+1;
1209 for (const ColParam& r : maColPositions)
1211 assert(o3tl::make_unsigned(mnRow) <= r.maPos->position);
1212 nNextRow = std::min (nNextRow, static_cast<size_t>(r.maPos->position));
1215 SCROW nRow = std::max(static_cast<SCROW>(nNextRow), mnRow);
1216 debugiter("Next non empty row is %d\n", (int) nRow);
1217 return nRow;
1220 void ScHorizontalCellIterator::Advance()
1222 assert (mbMore);
1223 assert (maColPos != maColPositions.end());
1225 ++maColPos;
1227 SkipInvalid();
1230 void ScHorizontalCellIterator::SkipInvalid()
1232 if (maColPos == maColPositions.end() ||
1233 !SkipInvalidInRow())
1235 mnRow++;
1237 if (mnRow > nEndRow)
1239 mbMore = false;
1240 return;
1243 maColPos = maColPositions.begin();
1244 debugiter("moving to next row\n");
1245 if (SkipInvalidInRow())
1247 debugiter("moved to valid cell in next row (or end)\n");
1248 return;
1251 mnRow = FindNextNonEmptyRow();
1252 maColPos = maColPositions.begin();
1253 bool bCorrect = SkipInvalidInRow();
1254 assert (bCorrect); (void) bCorrect;
1257 if (mnRow > nEndRow)
1258 mbMore = false;
1261 ScHorizontalValueIterator::ScHorizontalValueIterator( ScDocument& rDocument,
1262 const ScRange& rRange ) :
1263 rDoc( rDocument ),
1264 nEndTab( rRange.aEnd.Tab() ),
1265 bCalcAsShown( rDocument.GetDocOptions().IsCalcAsShown() )
1267 SCCOL nStartCol = rRange.aStart.Col();
1268 SCROW nStartRow = rRange.aStart.Row();
1269 SCTAB nStartTab = rRange.aStart.Tab();
1270 SCCOL nEndCol = rRange.aEnd.Col();
1271 SCROW nEndRow = rRange.aEnd.Row();
1272 PutInOrder( nStartCol, nEndCol);
1273 PutInOrder( nStartRow, nEndRow);
1274 PutInOrder( nStartTab, nEndTab );
1276 if (!rDoc.ValidCol(nStartCol)) nStartCol = rDoc.MaxCol();
1277 if (!rDoc.ValidCol(nEndCol)) nEndCol = rDoc.MaxCol();
1278 if (!rDoc.ValidRow(nStartRow)) nStartRow = rDoc.MaxRow();
1279 if (!rDoc.ValidRow(nEndRow)) nEndRow = rDoc.MaxRow();
1280 if (!ValidTab(nStartTab)) nStartTab = MAXTAB;
1281 if (!ValidTab(nEndTab)) nEndTab = MAXTAB;
1283 nCurCol = nStartCol;
1284 nCurRow = nStartRow;
1285 nCurTab = nStartTab;
1287 nNumFormat = 0; // Will be initialized in GetNumberFormat()
1288 pAttrArray = nullptr;
1289 nAttrEndRow = 0;
1291 pCellIter.reset( new ScHorizontalCellIterator( rDoc, nStartTab, nStartCol,
1292 nStartRow, nEndCol, nEndRow ) );
1295 ScHorizontalValueIterator::~ScHorizontalValueIterator()
1299 bool ScHorizontalValueIterator::GetNext( double& rValue, FormulaError& rErr )
1301 bool bFound = false;
1302 while ( !bFound )
1304 ScRefCellValue* pCell = pCellIter->GetNext( nCurCol, nCurRow );
1305 while ( !pCell )
1307 if ( nCurTab < nEndTab )
1309 pCellIter->SetTab( ++nCurTab);
1310 pCell = pCellIter->GetNext( nCurCol, nCurRow );
1312 else
1313 return false;
1315 switch (pCell->getType())
1317 case CELLTYPE_VALUE:
1319 rValue = pCell->getDouble();
1320 rErr = FormulaError::NONE;
1321 if ( bCalcAsShown )
1323 ScColumn* pCol = &rDoc.maTabs[nCurTab]->aCol[nCurCol];
1324 ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray,
1325 nAttrEndRow, pCol->pAttrArray.get(), nCurRow, rDoc );
1326 rValue = rDoc.RoundValueAsShown( rValue, nNumFormat );
1328 bFound = true;
1330 break;
1331 case CELLTYPE_FORMULA:
1333 rErr = pCell->getFormula()->GetErrCode();
1334 if (rErr != FormulaError::NONE || pCell->getFormula()->IsValue())
1336 rValue = pCell->getFormula()->GetValue();
1337 bFound = true;
1340 break;
1341 case CELLTYPE_STRING :
1342 case CELLTYPE_EDIT :
1343 break;
1344 default: ; // nothing
1347 return bFound;
1350 ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument& rDocument, SCTAB nTable,
1351 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
1352 rDoc( rDocument ),
1353 nTab( nTable ),
1354 nStartCol( nCol1 ),
1355 nStartRow( nRow1 ),
1356 nEndCol( nCol2 ),
1357 nEndRow( nRow2 )
1359 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
1360 assert(rDoc.maTabs[nTab]);
1362 nRow = nStartRow;
1363 nCol = nStartCol;
1365 pIndices.reset( new SCSIZE[nEndCol-nStartCol+1] );
1366 pNextEnd.reset( new SCROW[nEndCol-nStartCol+1] );
1367 pHorizEnd.reset( new SCCOL[nEndCol-nStartCol+1] );
1368 ppPatterns.reset( new const ScPatternAttr*[nEndCol-nStartCol+1] );
1370 InitForNextRow(true);
1373 ScHorizontalAttrIterator::~ScHorizontalAttrIterator()
1377 void ScHorizontalAttrIterator::InitForNextRow(bool bInitialization)
1379 nMinNextEnd = rDoc.MaxRow();
1380 SCCOL nThisHead = 0;
1382 for (SCCOL i=nStartCol; i<=nEndCol; i++)
1384 SCCOL nPos = i - nStartCol;
1385 if ( bInitialization || pNextEnd[nPos] < nRow )
1387 const ScAttrArray& pArray = rDoc.maTabs[nTab]->GetColumnData(i).AttrArray();
1389 SCSIZE nIndex;
1390 if (bInitialization)
1392 if ( pArray.Count() )
1393 pArray.Search( nStartRow, nIndex );
1394 else
1395 nIndex = 0;
1396 pIndices[nPos] = nIndex;
1397 pHorizEnd[nPos] = rDoc.MaxCol()+1; // only for assert()
1399 else
1400 nIndex = ++pIndices[nPos];
1402 if ( !nIndex && !pArray.Count() )
1404 pNextEnd[nPos] = rDoc.MaxRow();
1405 assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
1406 ppPatterns[nPos] = &rDoc.getCellAttributeHelper().getDefaultCellAttribute();
1408 else if ( nIndex < pArray.Count() )
1410 const ScPatternAttr* pPattern = pArray.mvData[nIndex].getScPatternAttr();
1411 SCROW nThisEnd = pArray.mvData[nIndex].nEndRow;
1412 pNextEnd[nPos] = nThisEnd;
1413 assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
1414 ppPatterns[nPos] = pPattern;
1416 else
1418 assert(!"AttrArray does not range to MAXROW");
1419 pNextEnd[nPos] = rDoc.MaxRow();
1420 ppPatterns[nPos] = nullptr;
1424 if ( nMinNextEnd > pNextEnd[nPos] )
1425 nMinNextEnd = pNextEnd[nPos];
1427 // store positions of ScHorizontalAttrIterator elements (minimizing expensive ScPatternAttr comparisons)
1428 if (i > nStartCol && !ScPatternAttr::areSame(ppPatterns[nThisHead], ppPatterns[nPos]))
1430 pHorizEnd[nThisHead] = i - 1;
1431 nThisHead = nPos; // start position of the next horizontal group
1435 pHorizEnd[nThisHead] = nEndCol; // set the end position of the last horizontal group, too
1438 const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow )
1440 assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
1441 for (;;)
1443 if ( nCol <= nEndCol )
1445 const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol];
1446 rRow = nRow;
1447 rCol1 = nCol;
1448 assert( pHorizEnd[nCol-nStartCol] < rDoc.MaxCol()+1 && "missing stored data" );
1449 nCol = pHorizEnd[nCol-nStartCol];
1450 rCol2 = nCol;
1451 ++nCol; // Count up for next call
1452 return pPat; // Found it!
1455 // Next row
1456 ++nRow;
1457 if ( nRow > nEndRow ) // Already at the end?
1458 return nullptr; // Found nothing
1459 nCol = nStartCol; // Start at the left again
1461 if ( nRow > nMinNextEnd )
1462 InitForNextRow(false);
1466 static bool IsGreater( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
1468 return ( nRow1 > nRow2 ) || ( nRow1 == nRow2 && nCol1 > nCol2 );
1471 ScUsedAreaIterator::ScUsedAreaIterator( ScDocument& rDocument, SCTAB nTable,
1472 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
1473 : aCellIter( rDocument, nTable, nCol1, nRow1, nCol2, nRow2 )
1474 , aAttrIter( rDocument, nTable, nCol1, nRow1, nCol2, nRow2 )
1475 , nNextCol( nCol1 )
1476 , nNextRow( nRow1 )
1477 , nCellCol( 0 )
1478 , nCellRow( 0 )
1479 , nAttrCol1( 0 )
1480 , nAttrCol2( 0 )
1481 , nAttrRow( 0 )
1482 , nFoundStartCol( 0 )
1483 , nFoundEndCol( 0 )
1484 , nFoundRow( 0 )
1485 , pFoundPattern( nullptr )
1487 pCell = aCellIter.GetNext( nCellCol, nCellRow );
1488 pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow );
1491 ScUsedAreaIterator::~ScUsedAreaIterator()
1495 bool ScUsedAreaIterator::GetNext()
1497 // Forward iterators
1498 if ( pCell && IsGreater( nNextCol, nNextRow, nCellCol, nCellRow ) )
1499 pCell = aCellIter.GetNext( nCellCol, nCellRow );
1501 while (pCell && pCell->isEmpty())
1502 pCell = aCellIter.GetNext( nCellCol, nCellRow );
1504 if ( pPattern && IsGreater( nNextCol, nNextRow, nAttrCol2, nAttrRow ) )
1505 pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow );
1507 if ( pPattern && nAttrRow == nNextRow && nAttrCol1 < nNextCol )
1508 nAttrCol1 = nNextCol;
1510 // Find next area
1511 bool bFound = true;
1512 bool bUseCell = false;
1514 if ( pCell && pPattern )
1516 if ( IsGreater( nCellCol, nCellRow, nAttrCol1, nAttrRow ) ) // Only attributes at the beginning?
1518 maFoundCell.clear();
1519 pFoundPattern = pPattern;
1520 nFoundRow = nAttrRow;
1521 nFoundStartCol = nAttrCol1;
1522 if ( nCellRow == nAttrRow && nCellCol <= nAttrCol2 ) // Area also contains cell?
1523 nFoundEndCol = nCellCol - 1; // Only until right before the cell
1524 else
1525 nFoundEndCol = nAttrCol2; // Everything
1527 else
1529 bUseCell = true;
1530 if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attributes on the cell?
1531 pFoundPattern = pPattern;
1532 else
1533 pFoundPattern = nullptr;
1536 else if ( pCell ) // Just a cell -> take over right away
1538 pFoundPattern = nullptr;
1539 bUseCell = true; // Cell position
1541 else if ( pPattern ) // Just attributes -> take over right away
1543 maFoundCell.clear();
1544 pFoundPattern = pPattern;
1545 nFoundRow = nAttrRow;
1546 nFoundStartCol = nAttrCol1;
1547 nFoundEndCol = nAttrCol2;
1549 else // Nothing
1550 bFound = false;
1552 if ( bUseCell ) // Cell position
1554 if (pCell)
1555 maFoundCell = *pCell;
1556 else
1557 maFoundCell.clear();
1559 nFoundRow = nCellRow;
1560 nFoundStartCol = nFoundEndCol = nCellCol;
1563 if (bFound)
1565 nNextRow = nFoundRow;
1566 nNextCol = nFoundEndCol + 1;
1569 return bFound;
1572 ScDocAttrIterator::ScDocAttrIterator(ScDocument& rDocument, SCTAB nTable,
1573 SCCOL nCol1, SCROW nRow1,
1574 SCCOL nCol2, SCROW nRow2) :
1575 rDoc( rDocument ),
1576 nTab( nTable ),
1577 nEndCol( nCol2 ),
1578 nStartRow( nRow1 ),
1579 nEndRow( nRow2 ),
1580 nCol( nCol1 )
1582 if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
1583 moColIter = rDoc.maTabs[nTab]->GetColumnData(nCol).CreateAttrIterator( nStartRow, nEndRow );
1586 const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 )
1588 while ( moColIter )
1590 const ScPatternAttr* pPattern = moColIter->Next( rRow1, rRow2 );
1591 if ( pPattern )
1593 rCol = nCol;
1594 return pPattern;
1597 ++nCol;
1598 if ( nCol <= nEndCol )
1599 moColIter = rDoc.maTabs[nTab]->GetColumnData(nCol).CreateAttrIterator( nStartRow, nEndRow );
1600 else
1601 moColIter.reset();
1603 return nullptr; // Nothing anymore
1606 ScDocRowHeightUpdater::TabRanges::TabRanges(SCTAB nTab, SCROW nMaxRow) :
1607 mnTab(nTab), maRanges(nMaxRow)
1611 ScDocRowHeightUpdater::ScDocRowHeightUpdater(ScDocument& rDoc, OutputDevice* pOutDev, double fPPTX, double fPPTY, const vector<TabRanges>* pTabRangesArray) :
1612 mrDoc(rDoc), mpOutDev(pOutDev), mfPPTX(fPPTX), mfPPTY(fPPTY), mpTabRangesArray(pTabRangesArray)
1616 void ScDocRowHeightUpdater::update(const bool bOnlyUsedRows)
1618 if (!mpTabRangesArray || mpTabRangesArray->empty())
1620 // No ranges defined. Update all rows in all tables.
1621 updateAll(bOnlyUsedRows);
1622 return;
1625 sal_uInt64 nCellCount = 0;
1626 for (const auto& rTabRanges : *mpTabRangesArray)
1628 const SCTAB nTab = rTabRanges.mnTab;
1629 if (!ValidTab(nTab) || nTab >= mrDoc.GetTableCount() || !mrDoc.maTabs[nTab])
1630 continue;
1632 ScFlatBoolRowSegments::RangeData aData;
1633 ScFlatBoolRowSegments::RangeIterator aRangeItr(rTabRanges.maRanges);
1634 for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
1636 if (!aData.mbValue)
1637 continue;
1639 nCellCount += mrDoc.maTabs[nTab]->GetWeightedCount(aData.mnRow1, aData.mnRow2);
1643 ScProgress aProgress(mrDoc.GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true);
1645 Fraction aZoom(1, 1);
1646 sal_uInt64 nProgressStart = 0;
1647 for (const auto& rTabRanges : *mpTabRangesArray)
1649 const SCTAB nTab = rTabRanges.mnTab;
1650 if (!ValidTab(nTab) || nTab >= mrDoc.GetTableCount() || !mrDoc.maTabs[nTab])
1651 continue;
1653 sc::RowHeightContext aCxt(mrDoc.MaxRow(), mfPPTX, mfPPTY, aZoom, aZoom, mpOutDev);
1654 ScFlatBoolRowSegments::RangeData aData;
1655 ScFlatBoolRowSegments::RangeIterator aRangeItr(rTabRanges.maRanges);
1656 for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
1658 if (!aData.mbValue)
1659 continue;
1661 mrDoc.maTabs[nTab]->SetOptimalHeight(
1662 aCxt, aData.mnRow1, aData.mnRow2, true, &aProgress, nProgressStart);
1664 nProgressStart += mrDoc.maTabs[nTab]->GetWeightedCount(aData.mnRow1, aData.mnRow2);
1669 void ScDocRowHeightUpdater::updateAll(const bool bOnlyUsedRows)
1671 sal_uInt64 nCellCount = 0;
1672 for (SCTAB nTab = 0; nTab < mrDoc.GetTableCount(); ++nTab)
1674 if (!ValidTab(nTab) || !mrDoc.maTabs[nTab])
1675 continue;
1677 nCellCount += mrDoc.maTabs[nTab]->GetWeightedCount();
1680 ScProgress aProgress(mrDoc.GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true);
1682 Fraction aZoom(1, 1);
1683 sal_uInt64 nProgressStart = 0;
1684 for (SCTAB nTab = 0; nTab < mrDoc.GetTableCount(); ++nTab)
1686 if (!ValidTab(nTab) || !mrDoc.maTabs[nTab])
1687 continue;
1689 sc::RowHeightContext aCxt(mrDoc.MaxRow(), mfPPTX, mfPPTY, aZoom, aZoom, mpOutDev);
1690 SCCOL nEndCol = 0;
1691 SCROW nEndRow = mrDoc.MaxRow();
1692 if (!bOnlyUsedRows || mrDoc.GetPrintArea(nTab, nEndCol, nEndRow))
1693 mrDoc.maTabs[nTab]->SetOptimalHeight(aCxt, 0, nEndRow, true, &aProgress, nProgressStart);
1694 nProgressStart += mrDoc.maTabs[nTab]->GetWeightedCount();
1698 ScAttrRectIterator::ScAttrRectIterator(ScDocument& rDocument, SCTAB nTable,
1699 SCCOL nCol1, SCROW nRow1,
1700 SCCOL nCol2, SCROW nRow2) :
1701 rDoc( rDocument ),
1702 nTab( nTable ),
1703 nEndCol( nCol2 ),
1704 nStartRow( nRow1 ),
1705 nEndRow( nRow2 ),
1706 nIterStartCol( nCol1 ),
1707 nIterEndCol( nCol1 )
1709 if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
1711 moColIter = rDoc.maTabs[nTab]->GetColumnData(nIterStartCol).CreateAttrIterator( nStartRow, nEndRow );
1712 while ( nIterEndCol < nEndCol &&
1713 rDoc.maTabs[nTab]->GetColumnData(nIterEndCol).IsAllAttrEqual(
1714 rDoc.maTabs[nTab]->GetColumnData(nIterEndCol+1), nStartRow, nEndRow ) )
1715 ++nIterEndCol;
1719 void ScAttrRectIterator::DataChanged()
1721 if (moColIter)
1723 SCROW nNextRow = moColIter->GetNextRow();
1724 moColIter = rDoc.maTabs[nTab]->GetColumnData(nIterStartCol).CreateAttrIterator( nNextRow, nEndRow );
1728 const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2,
1729 SCROW& rRow1, SCROW& rRow2 )
1731 while ( moColIter )
1733 const ScPatternAttr* pPattern = moColIter->Next( rRow1, rRow2 );
1734 if ( pPattern )
1736 rCol1 = nIterStartCol;
1737 rCol2 = nIterEndCol;
1738 return pPattern;
1741 nIterStartCol = nIterEndCol+1;
1742 if ( nIterStartCol <= nEndCol )
1744 nIterEndCol = nIterStartCol;
1745 moColIter = rDoc.maTabs[nTab]->GetColumnData(nIterStartCol).CreateAttrIterator( nStartRow, nEndRow );
1746 while ( nIterEndCol < nEndCol &&
1747 rDoc.maTabs[nTab]->GetColumnData(nIterEndCol).IsAllAttrEqual(
1748 rDoc.maTabs[nTab]->GetColumnData(nIterEndCol+1), nStartRow, nEndRow ) )
1749 ++nIterEndCol;
1751 else
1752 moColIter.reset();
1754 return nullptr; // Nothing anymore
1757 ScRowBreakIterator::ScRowBreakIterator(set<SCROW>& rBreaks) :
1758 mrBreaks(rBreaks),
1759 maItr(rBreaks.begin()), maEnd(rBreaks.end())
1763 SCROW ScRowBreakIterator::first()
1765 maItr = mrBreaks.begin();
1766 return maItr == maEnd ? NOT_FOUND : *maItr;
1769 SCROW ScRowBreakIterator::next()
1771 ++maItr;
1772 return maItr == maEnd ? NOT_FOUND : *maItr;
1775 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */