1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svl/numformat.hxx>
21 #include <svl/zforlist.hxx>
24 #include <dociter.hxx>
25 #include <document.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>
55 using ::rtl::math::approxEqual
;
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
)
71 SCROW nRowEnd
= rDoc
.MaxRow();
72 const ScPatternAttr
* pPattern
= pNewArr
->GetPatternRange( nRowStart
, nRowEnd
, nRow
);
75 pPattern
= &rDoc
.getCellAttributeHelper().getDefaultCellAttribute();
76 nRowEnd
= rDoc
.MaxRow();
80 nFormat
= pPattern
->GetNumberFormat(*pContext
);
82 nFormat
= pPattern
->GetNumberFormat(rDoc
.GetFormatTable());
84 nAttrEndRow
= nRowEnd
;
87 ScValueIterator::ScValueIterator(ScInterpreterContext
& rContext
, const ScRange
& rRange
,
88 SubtotalFlags nSubTotalFlags
, bool bTextZero
)
89 : mrDoc(*rContext
.mpDoc
)
92 , nNumFormat(0) // Initialized in GetNumberFormat
94 , maStartPos(rRange
.aStart
)
95 , maEndPos(rRange
.aEnd
)
99 , mnSubTotalFlags(nSubTotalFlags
)
100 , nNumFmtType(SvNumFormatType::UNDEFINED
)
102 , bCalcAsShown((*rContext
.mpDoc
).GetDocOptions().IsCalcAsShown())
103 , bTextAsZero(bTextZero
)
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()
129 void ScValueIterator::IncPos()
131 if (maCurPos
.second
+ 1 < maCurPos
.first
->size
)
132 // Move within the same block.
135 // Move to the next block.
139 bool ScValueIterator::GetThis(double& rValue
, FormulaError
& rErr
)
143 bool bNextColumn
= !mpCells
|| maCurPos
.first
== mpCells
->end();
146 if (GetRow() > maEndPos
.Row())
152 pCol
= &(mrDoc
.maTabs
[mnTab
])->aCol
[mnCol
];
155 // Find the next available column.
159 while (mnCol
> maEndPos
.Col() || mnCol
>= mrDoc
.maTabs
[mnTab
]->GetAllocatedColumnsCount())
161 mnCol
= maStartPos
.Col();
163 if (mnTab
> maEndPos
.Tab())
165 rErr
= FormulaError::NONE
;
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();
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);
189 switch (maCurPos
.first
->type
)
191 case sc::element_type_numeric
:
194 rValue
= sc::numeric_block::at(*maCurPos
.first
->data
, maCurPos
.second
);
195 rErr
= FormulaError::NONE
;
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!
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.
215 if (rCell
.GetErrorOrValue(rErr
, rValue
))
217 if ( rErr
!= FormulaError::NONE
&& ( mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
) )
223 return true; // Found it!
225 else if (bTextAsZero
)
234 case sc::element_type_string
:
235 case sc::element_type_edittext
:
239 rErr
= FormulaError::NONE
;
241 nNumFmtType
= SvNumFormatType::NUMBER
;
249 case sc::element_type_empty
:
251 // Skip the whole block.
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
);
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
);
281 nNumFormat
= 0; // Initialized in GetNumberFormat
282 pAttrArray
= nullptr;
285 auto nCol
= maStartPos
.Col();
286 if (nCol
< pTab
->GetAllocatedColumnsCount())
288 mpCells
= &pTab
->aCol
[nCol
].maCells
;
289 maCurPos
= mpCells
->position(maStartPos
.Row());
293 return GetThis(rValue
, rErr
);
296 bool ScValueIterator::GetNext(double& rValue
, FormulaError
& rErr
)
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
);
315 if (nCol
>= pTab
->GetAllocatedColumnsCount())
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
)
339 , mrContext(rContext
)
340 , pAttrArray(nullptr)
341 , nNumFormat(0) // Initialized in GetNumberFormat
343 , nCol(mpParam
->mnField
)
344 , nRow(mpParam
->nRow1
)
346 , nTab(mpParam
->nTab
)
347 , nNumFmtType(SvNumFormatType::ALL
)
348 , bCalcAsShown(rDoc
.GetDocOptions().IsCalcAsShown())
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();
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
;
381 if (maCurPos
.first
== mpCells
->end() || nRow
> mpParam
->nRow2
)
383 // Bottom of the range reached. Bail out.
384 rValue
.mnError
= FormulaError::NONE
;
388 if (maCurPos
.first
->type
== sc::element_type_empty
)
390 // Skip the whole empty block.
395 ScRefCellValue
* pCell
= nullptr;
396 if (nCol
== static_cast<SCCOL
>(nFirstQueryField
))
398 aCell
= sc::toRefCell(maCurPos
.first
, maCurPos
.second
);
402 if (ScDBQueryDataIterator::IsQueryValid(mrDoc
, *mpParam
, nTab
, nRow
, pCell
))
405 aCell
= sc::toRefCell(maCurPos
.first
, maCurPos
.second
);
406 switch (aCell
.getType())
410 rValue
.mfValue
= aCell
.getDouble();
411 rValue
.mbIsNumber
= true;
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
;
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
)
441 rValue
.maString
= aCell
.getFormula()->GetString().getString();
442 rValue
.mfValue
= 0.0;
443 rValue
.mnError
= aCell
.getFormula()->GetErrCode();
444 rValue
.mbIsNumber
= false;
449 case CELLTYPE_STRING
:
451 if (mpParam
->mbSkipString
)
455 rValue
.maString
= aCell
.getString(&mrDoc
);
456 rValue
.mfValue
= 0.0;
457 rValue
.mnError
= FormulaError::NONE
;
458 rValue
.mbIsNumber
= false;
469 // statement unreachable
472 bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value
& rValue
)
474 if (mpParam
->bHasHeader
)
477 mpCells
= ScDBQueryDataIterator::GetColumnCellStore(mrDoc
, nTab
, nCol
);
481 maCurPos
= mpCells
->position(nRow
);
482 return getCurrent(rValue
);
485 bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value
& rValue
)
487 if (!mpCells
|| maCurPos
.first
== mpCells
->end())
491 return getCurrent(rValue
);
494 void ScDBQueryDataIterator::DataAccessInternal::incBlock()
499 nRow
= maCurPos
.first
->position
;
502 void ScDBQueryDataIterator::DataAccessInternal::incPos()
504 if (maCurPos
.second
+ 1 < maCurPos
.first
->size
)
506 // Move within the same block.
511 // Move to the next block.
515 ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(ScDBQueryParamMatrix
* pParam
)
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
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.
539 bool bIsStrVal
= rMat
.IsStringOrEmpty(mpParam
->mnField
, mnCurRow
);
540 if (bIsStrVal
&& mpParam
->mbSkipString
)
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
;
555 bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value
& rValue
)
557 mnCurRow
= mpParam
->bHasHeader
? 1 : 0;
558 return getCurrent(rValue
);
561 bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value
& rValue
)
564 return getCurrent(rValue
);
569 bool isQueryByValue(const ScQueryEntry::Item
& rItem
, const ScMatrix
& rMat
, SCSIZE nCol
, SCSIZE nRow
)
571 if (rItem
.meType
== ScQueryEntry::ByString
)
574 if (!rMat
.IsValueOrEmpty(nCol
, nRow
))
580 bool isQueryByString(const ScQueryEntry
& rEntry
, const ScQueryEntry::Item
& rItem
, const ScMatrix
& rMat
, SCSIZE nCol
, SCSIZE nRow
)
587 case SC_DOES_NOT_CONTAIN
:
590 case SC_DOES_NOT_BEGIN_WITH
:
591 case SC_DOES_NOT_END_WITH
:
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
)
623 case SC_GREATER_EQUAL
:
627 // Only the above operators are supported.
628 SAL_WARN("sc.core", "Unsupported operator " << rEntry
.eOp
629 << " in ScDBQueryDataIterator::DataAccessMatrix::isValidQuery()");
635 SCSIZE nField
= static_cast<SCSIZE
>(rEntry
.nField
);
636 if (isQueryByValue(rItem
, rMat
, nField
, nRow
))
639 double fMatVal
= rMat
.GetDouble(nField
, nRow
);
640 bool bEqual
= approxEqual(fMatVal
, rItem
.mfVal
);
647 bValid
= (fMatVal
< rItem
.mfVal
) && !bEqual
;
650 bValid
= (fMatVal
> rItem
.mfVal
) && !bEqual
;
653 bValid
= (fMatVal
< rItem
.mfVal
) || bEqual
;
655 case SC_GREATER_EQUAL
:
656 bValid
= (fMatVal
> rItem
.mfVal
) || bEqual
;
665 else if (isQueryByString(rEntry
, rItem
, rMat
, nField
, nRow
))
670 // Equality check first.
671 svl::SharedString aMatStr
= rMat
.GetString(nField
, nRow
);
672 svl::SharedString aQueryStr
= rEntry
.GetQueryItem().maString
;
674 rtl_uString
* p1
= mpParam
->bCaseSens
? aMatStr
.getData() : aMatStr
.getDataIgnoreCase();
675 rtl_uString
* p2
= mpParam
->bCaseSens
? aQueryStr
.getData() : aQueryStr
.getDataIgnoreCase();
693 // Unequality check using collator.
694 sal_Int32 nCompare
= rCollator
.compareString(aMatStr
.getString(), aQueryStr
.getString());
698 bValid
= (nCompare
< 0);
701 bValid
= (nCompare
> 0);
704 bValid
= (nCompare
<= 0);
706 case SC_GREATER_EQUAL
:
707 bValid
= (nCompare
>= 0);
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
;
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
));
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
) :
775 ScTable
*pTab
= mrDoc
.FetchTable(mnTab
);
776 ScColumn
*pCol
= pTab
? pTab
->FetchColumn(mnCol
) : nullptr;
780 maEntries
= pCol
->GetFormulaGroupEntries();
786 sc::FormulaGroupEntry
* ScFormulaGroupIterator::first()
791 sc::FormulaGroupEntry
* ScFormulaGroupIterator::next()
793 if (mnIndex
>= maEntries
.size() || mbNullCol
)
795 while (mnIndex
>= maEntries
.size() || mbNullCol
)
799 if (mnCol
> mrDoc
.MaxCol())
803 if (mnTab
>= mrDoc
.GetTableCount())
806 ScTable
*pTab
= mrDoc
.FetchTable(mnTab
);
807 ScColumn
*pCol
= (pTab
&& pTab
->IsColValid(mnCol
)) ? pTab
->FetchColumn(mnCol
) : nullptr;
811 maEntries
= pCol
->GetFormulaGroupEntries();
818 return &maEntries
[mnIndex
++];
821 ScCellIterator::ScCellIterator( ScDocument
& rDoc
, const ScRange
& rRange
, SubtotalFlags nSubTotalFlags
) :
823 maStartPos(rRange
.aStart
),
824 maEndPos(rRange
.aEnd
),
825 mnSubTotalFlags(nSubTotalFlags
)
830 void ScCellIterator::incBlock()
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
;
847 // Move to the next block.
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.
888 maStartPos
.SetCol(mrDoc
.maTabs
[maStartPos
.Tab()]->ClampToAllocatedColumns(maStartPos
.Col()));
891 maCurPos
= maStartPos
;
894 bool ScCellIterator::getCurrent()
896 const ScColumn
* pCol
= getColumn();
900 bool bNextColumn
= maCurColPos
.first
== pCol
->maCells
.end();
903 if (maCurPos
.Row() > maEndPos
.Row())
909 // Move to the next column.
910 maCurPos
.SetRow(maStartPos
.Row());
914 while (maCurPos
.Col() >= mrDoc
.GetAllocatedColumnsCount(maCurPos
.Tab())
915 || maCurPos
.Col() > maEndPos
.Col())
917 maCurPos
.SetCol(maStartPos
.Col());
919 if (maCurPos
.Tab() > maEndPos
.Tab())
927 while (pCol
->IsEmptyData());
929 maCurColPos
= pCol
->maCells
.position(maCurPos
.Row());
932 if (maCurColPos
.first
->type
== sc::element_type_empty
)
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
) ) )
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
) )
964 maCurCell
= sc::toRefCell(maCurColPos
.first
, maCurColPos
.second
);
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());
983 return ScCellValue(maCurCell
.getEditText()->Clone());
986 return ScCellValue(maCurCell
.getDouble());
988 case CELLTYPE_FORMULA
:
989 return ScCellValue(maCurCell
.getFormula()->Clone());
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()))
1017 maCurPos
= maStartPos
;
1018 const ScColumn
* pCol
= getColumn();
1020 maCurColPos
= pCol
->maCells
.position(maCurPos
.Row());
1021 return getCurrent();
1024 bool ScCellIterator::next()
1027 return getCurrent();
1030 ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument
& rDocument
, SCTAB nTable
,
1031 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) :
1042 assert(mnTab
< rDoc
.GetTableCount() && "index out of bounds, FIX IT");
1044 const ScTable
* pTab
= rDoc
.FetchTable(mnTab
);
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 );
1057 ScHorizontalCellIterator::~ScHorizontalCellIterator()
1061 void ScHorizontalCellIterator::SetTab( SCTAB nTabP
)
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
];
1074 aParam
.maPos
= pCol
->maCells
.position(nStartRow
).first
;
1075 aParam
.maEnd
= pCol
->maCells
.end();
1078 // find first non-empty element.
1079 while (aParam
.maPos
!= aParam
.maEnd
) {
1080 if (aParam
.maPos
->type
== sc::element_type_empty
)
1084 maColPositions
.push_back( aParam
);
1090 if (maColPositions
.empty())
1093 maColPos
= maColPositions
.begin();
1098 ScRefCellValue
* ScHorizontalCellIterator::GetNext( SCCOL
& rCol
, SCROW
& rRow
)
1102 debugiter("no more !\n");
1106 // Return the current non-empty cell, and move the cursor to the next one.
1107 ColParam
& r
= *maColPos
;
1109 rCol
= mnCol
= r
.mnCol
;
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
);
1116 debugiter("advance to: col %d row %d\n", (int)maColPos
->mnCol
, (int)mnRow
);
1121 bool ScHorizontalCellIterator::GetPos( SCCOL
& rCol
, SCROW
& rRow
)
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()
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
);
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;
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");
1180 debugiter("advanced column %d to block starting row %d, retrying\n",
1181 (int)maColPos
->mnCol
, r
.maPos
->position
);
1187 debugiter("skip empty cells at column %d, row %d\n",
1188 (int)maColPos
->mnCol
, (int)nRow
);
1193 // No more columns with anything interesting in them ?
1194 if (maColPositions
.empty())
1196 debugiter("no more live columns left - done\n");
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
);
1220 void ScHorizontalCellIterator::Advance()
1223 assert (maColPos
!= maColPositions
.end());
1230 void ScHorizontalCellIterator::SkipInvalid()
1232 if (maColPos
== maColPositions
.end() ||
1233 !SkipInvalidInRow())
1237 if (mnRow
> nEndRow
)
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");
1251 mnRow
= FindNextNonEmptyRow();
1252 maColPos
= maColPositions
.begin();
1253 bool bCorrect
= SkipInvalidInRow();
1254 assert (bCorrect
); (void) bCorrect
;
1257 if (mnRow
> nEndRow
)
1261 ScHorizontalValueIterator::ScHorizontalValueIterator( ScDocument
& rDocument
,
1262 const ScRange
& rRange
) :
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;
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;
1304 ScRefCellValue
* pCell
= pCellIter
->GetNext( nCurCol
, nCurRow
);
1307 if ( nCurTab
< nEndTab
)
1309 pCellIter
->SetTab( ++nCurTab
);
1310 pCell
= pCellIter
->GetNext( nCurCol
, nCurRow
);
1315 switch (pCell
->getType())
1317 case CELLTYPE_VALUE
:
1319 rValue
= pCell
->getDouble();
1320 rErr
= FormulaError::NONE
;
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
);
1331 case CELLTYPE_FORMULA
:
1333 rErr
= pCell
->getFormula()->GetErrCode();
1334 if (rErr
!= FormulaError::NONE
|| pCell
->getFormula()->IsValue())
1336 rValue
= pCell
->getFormula()->GetValue();
1341 case CELLTYPE_STRING
:
1342 case CELLTYPE_EDIT
:
1344 default: ; // nothing
1350 ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument
& rDocument
, SCTAB nTable
,
1351 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) :
1359 assert(nTab
< rDoc
.GetTableCount() && "index out of bounds, FIX IT");
1360 assert(rDoc
.maTabs
[nTab
]);
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();
1390 if (bInitialization
)
1392 if ( pArray
.Count() )
1393 pArray
.Search( nStartRow
, nIndex
);
1396 pIndices
[nPos
] = nIndex
;
1397 pHorizEnd
[nPos
] = rDoc
.MaxCol()+1; // only for assert()
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
;
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");
1443 if ( nCol
<= nEndCol
)
1445 const ScPatternAttr
* pPat
= ppPatterns
[nCol
-nStartCol
];
1448 assert( pHorizEnd
[nCol
-nStartCol
] < rDoc
.MaxCol()+1 && "missing stored data" );
1449 nCol
= pHorizEnd
[nCol
-nStartCol
];
1451 ++nCol
; // Count up for next call
1452 return pPat
; // Found it!
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
)
1482 , nFoundStartCol( 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
;
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
1525 nFoundEndCol
= nAttrCol2
; // Everything
1530 if ( nAttrRow
== nCellRow
&& nAttrCol1
== nCellCol
) // Attributes on the cell?
1531 pFoundPattern
= pPattern
;
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
;
1552 if ( bUseCell
) // Cell position
1555 maFoundCell
= *pCell
;
1557 maFoundCell
.clear();
1559 nFoundRow
= nCellRow
;
1560 nFoundStartCol
= nFoundEndCol
= nCellCol
;
1565 nNextRow
= nFoundRow
;
1566 nNextCol
= nFoundEndCol
+ 1;
1572 ScDocAttrIterator::ScDocAttrIterator(ScDocument
& rDocument
, SCTAB nTable
,
1573 SCCOL nCol1
, SCROW nRow1
,
1574 SCCOL nCol2
, SCROW nRow2
) :
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
)
1590 const ScPatternAttr
* pPattern
= moColIter
->Next( rRow1
, rRow2
);
1598 if ( nCol
<= nEndCol
)
1599 moColIter
= rDoc
.maTabs
[nTab
]->GetColumnData(nCol
).CreateAttrIterator( nStartRow
, nEndRow
);
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
);
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
])
1632 ScFlatBoolRowSegments::RangeData aData
;
1633 ScFlatBoolRowSegments::RangeIterator
aRangeItr(rTabRanges
.maRanges
);
1634 for (bool bFound
= aRangeItr
.getFirst(aData
); bFound
; bFound
= aRangeItr
.getNext(aData
))
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
])
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
))
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
])
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
])
1689 sc::RowHeightContext
aCxt(mrDoc
.MaxRow(), mfPPTX
, mfPPTY
, aZoom
, aZoom
, mpOutDev
);
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
) :
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
) )
1719 void ScAttrRectIterator::DataChanged()
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
)
1733 const ScPatternAttr
* pPattern
= moColIter
->Next( rRow1
, rRow2
);
1736 rCol1
= nIterStartCol
;
1737 rCol2
= nIterEndCol
;
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
) )
1754 return nullptr; // Nothing anymore
1757 ScRowBreakIterator::ScRowBreakIterator(set
<SCROW
>& 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()
1772 return maItr
== maEnd
? NOT_FOUND
: *maItr
;
1775 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */