diff options
author | Eike Rathke <erack@redhat.com> | 2019-09-05 22:17:32 +0200 |
---|---|---|
committer | Eike Rathke <erack@redhat.com> | 2019-09-05 23:18:29 +0200 |
commit | de146784684789174616108817f5c9df7c000e81 (patch) | |
tree | 0b3b84d2931b36e55ca77f953dba104b2ea4a229 /sc | |
parent | 001f739f1bca7607b0f8cf5f0c7e500dea70db44 (diff) |
Resolves: tdf#126577 stop filling a series when threshold is reached
... instead of filling the remainder of the selection with #NUM! errors.
Change-Id: I12f3943196878c7631f3dc6ac182631faab17323
Reviewed-on: https://gerrit.libreoffice.org/78675
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
Diffstat (limited to 'sc')
-rw-r--r-- | sc/inc/table.hxx | 2 | ||||
-rw-r--r-- | sc/source/core/data/table2.cxx | 17 | ||||
-rw-r--r-- | sc/source/core/data/table4.cxx | 276 |
3 files changed, 195 insertions, 100 deletions
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index 29a7b28fe635..7d28afcf102d 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -715,6 +715,8 @@ public: const ScPatternAttr& rPattern, SvNumFormatType nNewType ); void AddCondFormatData( const ScRangeList& rRange, sal_uInt32 nIndex ); void RemoveCondFormatData( const ScRangeList& rRange, sal_uInt32 nIndex ); + void SetPatternAreaCondFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, + const ScPatternAttr& rAttr, const ScCondFormatIndexes& rCondFormatIndexes ); void ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet* rStyle ); void ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, const ScStyleSheet& rStyle ); diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx index df0a1517f63a..e4d062f98e71 100644 --- a/sc/source/core/data/table2.cxx +++ b/sc/source/core/data/table2.cxx @@ -2667,6 +2667,23 @@ void ScTable::RemoveCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nI } } +void ScTable::SetPatternAreaCondFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, + const ScPatternAttr& rAttr, const ScCondFormatIndexes& rCondFormatIndexes ) +{ + aCol[nCol].SetPatternArea( nStartRow, nEndRow, rAttr); + + for (const auto& rIndex : rCondFormatIndexes) + { + ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex); + if (pCondFormat) + { + ScRangeList aRange = pCondFormat->GetRange(); + aRange.Join( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab)); + pCondFormat->SetRange(aRange); + } + } +} + void ScTable::ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet* rStyle ) { if (ValidColRow(nCol,nRow)) diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx index 0fb45a287026..15e6cc719c9f 100644 --- a/sc/source/core/data/table4.cxx +++ b/sc/source/core/data/table4.cxx @@ -1534,6 +1534,92 @@ void ScTable::FillAutoSimple( pProgress->SetStateOnPercent( rProgress ); } +namespace +{ +// Target value exceeded? +inline bool isOverflow( const double& rVal, const double& rMax, const double& rStep, + const double& rStartVal, FillCmd eFillCmd ) +{ + switch (eFillCmd) + { + case FILL_LINEAR: + case FILL_DATE: + if (rStep >= 0.0) + return rVal > rMax; + else + return rVal < rMax; + break; + case FILL_GROWTH: + if (rStep > 0.0) + { + if (rStep >= 1.0) + { + // Growing away from zero, including zero growth (1.0). + if (rVal >= 0.0) + return rVal > rMax; + else + return rVal < rMax; + } + else + { + // Shrinking towards zero. + if (rVal >= 0.0) + return rVal < rMax; + else + return rVal > rMax; + } + } + else if (rStep < 0.0) + { + // Alternating positive and negative values. + if (rStep <= -1.0) + { + // Growing away from zero, including zero growth (-1.0). + if (rVal >= 0.0) + { + if (rMax >= 0.0) + return rVal > rMax; + else + // Regard negative rMax as lower limit, which will + // be reached only by a negative rVal. + return false; + } + else + { + if (rMax <= 0.0) + return rVal < rMax; + else + // Regard positive rMax as upper limit, which will + // be reached only by a positive rVal. + return false; + } + } + else + { + // Shrinking towards zero. + if (rVal >= 0.0) + return rVal < rMax; + else + return rVal > rMax; + } + } + else // if (rStep == 0.0) + { + // All values become zero. + // Corresponds with bEntireArea in FillSeries(). + if (rMax > 0.0) + return rMax < rStartVal; + else if (rMax < 0.0) + return rStartVal < rMax; + } + break; + default: + assert(!"eFillCmd"); + } + return false; +} +} + void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sal_uLong nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd, double nStepValue, double nMaxValue, sal_uInt16 nArgMinDigits, @@ -1615,11 +1701,53 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, SCCOLROW nIMin = nIStart; SCCOLROW nIMax = nIEnd; PutInOrder(nIMin,nIMax); - InsertDeleteFlags nDel = bAttribs ? InsertDeleteFlags::AUTOFILL : (InsertDeleteFlags::AUTOFILL & InsertDeleteFlags::CONTENTS); - bool bIsFiltered = IsDataFiltered(aFillRange); - if (!bIsFiltered) + const bool bIsFiltered = IsDataFiltered(aFillRange); + bool bEntireArea = (!bIsFiltered && eFillCmd == FILL_SIMPLE); + if (!bIsFiltered && !bEntireArea && (eFillCmd == FILL_LINEAR || eFillCmd == FILL_GROWTH) + && (nOEnd - nOStart == 0)) + { + // For the usual case of one col/row determine if a numeric series is + // at least as long as the area to be filled and does not end earlier, + // so we can treat it as entire area for performance reasons at least + // in the vertical case. + ScCellValue aSrcCell; + if (bVertical) + aSrcCell = aCol[static_cast<SCCOL>(nOStart)].GetCellValue(static_cast<SCROW>(nISource)); + else + aSrcCell = aCol[static_cast<SCCOL>(nISource)].GetCellValue(static_cast<SCROW>(nOStart)); + // Same logic as for the actual series. + if (!aSrcCell.isEmpty() && (aSrcCell.meType == CELLTYPE_VALUE || aSrcCell.meType == CELLTYPE_FORMULA)) + { + double nStartVal; + if (aSrcCell.meType == CELLTYPE_VALUE) + nStartVal = aSrcCell.mfValue; + else + nStartVal = aSrcCell.mpFormula->GetValue(); + if (eFillCmd == FILL_LINEAR) + { + if (nStepValue == 0.0) + bEntireArea = (nStartVal <= nMaxValue); // fill with same value + else if (((nMaxValue - nStartVal) / nStepValue) >= nFillCount) + bEntireArea = true; + } + else if (eFillCmd == FILL_GROWTH) + { + if (nStepValue == 1.0) + bEntireArea = (nStartVal <= nMaxValue); // fill with same value + else if (nStepValue == -1.0) + bEntireArea = (fabs(nStartVal) <= fabs(nMaxValue)); // fill with alternating value + else if (nStepValue == 0.0) + bEntireArea = (nStartVal == 0.0 + || (nStartVal < 0.0 && nMaxValue >= 0.0) + || (nStartVal > 0.0 && nMaxValue <= 0.0)); // fill with 0.0 + } + } + } + if (bEntireArea) { + InsertDeleteFlags nDel = (bAttribs ? InsertDeleteFlags::AUTOFILL : + (InsertDeleteFlags::AUTOFILL & InsertDeleteFlags::CONTENTS)); if (bVertical) DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), nDel); else @@ -1643,72 +1771,45 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, // Source cell value. We need to clone the value since it may be inserted repeatedly. ScCellValue aSrcCell = aCol[nCol].GetCellValue(static_cast<SCROW>(nRow)); + const ScPatternAttr* pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nRow)); + const ScCondFormatItem& rCondFormatItem = pSrcPattern->GetItem(ATTR_CONDITIONAL); + const ScCondFormatIndexes& rCondFormatIndex = rCondFormatItem.GetCondFormatData(); + if (bAttribs) { - const ScPatternAttr* pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nRow)); - - const ScCondFormatItem& rCondFormatItem = pSrcPattern->GetItem(ATTR_CONDITIONAL); - const ScCondFormatIndexes& rCondFormatIndex = rCondFormatItem.GetCondFormatData(); - if (bVertical) { - // if not filtered use the faster method - // hidden cols/rows should be skipped - if(!bIsFiltered) + // If entire area (not filtered and simple fill) use the faster + // method, else hidden cols/rows should be skipped and series + // fill needs to determine the end row dynamically. + if (bEntireArea) { - aCol[nCol].SetPatternArea( static_cast<SCROW>(nIMin), - static_cast<SCROW>(nIMax), *pSrcPattern ); - - for(const auto& rIndex : rCondFormatIndex) - { - ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex); - if (pCondFormat) - { - ScRangeList aRange = pCondFormat->GetRange(); - aRange.Join(ScRange(nCol, nIMin, nTab, nCol, nIMax, nTab)); - pCondFormat->SetRange(aRange); - } - } + SetPatternAreaCondFormat( nCol, static_cast<SCROW>(nIMin), + static_cast<SCROW>(nIMax), *pSrcPattern, rCondFormatIndex); } - else + else if (eFillCmd == FILL_SIMPLE) { + assert(bIsFiltered); for(SCROW nAtRow = static_cast<SCROW>(nIMin); nAtRow <= static_cast<SCROW>(nIMax); ++nAtRow) { if(!RowHidden(nAtRow)) { - aCol[nCol].SetPatternArea( nAtRow, - nAtRow, *pSrcPattern); - for(const auto& rIndex : rCondFormatIndex) - { - ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex); - if (pCondFormat) - { - ScRangeList aRange = pCondFormat->GetRange(); - aRange.Join(ScRange(nCol, nAtRow, nTab, nCol, nAtRow, nTab)); - pCondFormat->SetRange(aRange); - } - } + SetPatternAreaCondFormat( nCol, nAtRow, nAtRow, *pSrcPattern, rCondFormatIndex); } } } } - else + else if (bEntireArea || eFillCmd == FILL_SIMPLE) + { for (SCCOL nAtCol = static_cast<SCCOL>(nIMin); nAtCol <= sal::static_int_cast<SCCOL>(nIMax); nAtCol++) + { if(!ColHidden(nAtCol)) { - aCol[nAtCol].SetPattern(static_cast<SCROW>(nRow), *pSrcPattern); - for(const auto& rIndex : rCondFormatIndex) - { - ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex); - if (pCondFormat) - { - ScRangeList aRange = pCondFormat->GetRange(); - aRange.Join(ScRange(nAtCol, static_cast<SCROW>(nRow), nTab, nAtCol, static_cast<SCROW>(nRow), nTab)); - pCondFormat->SetRange(aRange); - } - } + SetPatternAreaCondFormat( nAtCol, nRow, nRow, *pSrcPattern, rCondFormatIndex); } + } + } } if (!aSrcCell.isEmpty()) @@ -1721,11 +1822,8 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, } else if (eCellType == CELLTYPE_VALUE || eCellType == CELLTYPE_FORMULA) { - double nStartVal; - if (eCellType == CELLTYPE_VALUE) - nStartVal = aSrcCell.mfValue; - else - nStartVal = aSrcCell.mpFormula->GetValue(); + const double nStartVal = (eCellType == CELLTYPE_VALUE ? aSrcCell.mfValue : + aSrcCell.mpFormula->GetValue()); double nVal = nStartVal; long nIndex = 0; @@ -1734,11 +1832,11 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sal_uInt16 nDayOfMonth = 0; rInner = nIStart; - while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + while (true) { if(!ColHidden(nCol) && !RowHidden(nRow)) { - if (!bError && !bOverflow) + if (!bError) { switch (eFillCmd) { @@ -1769,33 +1867,20 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, } } - if (nStepValue >= 0) - { - if (nVal > nMaxValue) // target value reached ? - { - nVal = nMaxValue; - bOverflow = true; - } - } - else - { - if (nVal < nMaxValue) - { - nVal = nMaxValue; - bOverflow = true; - } - } + if (!bError) + bOverflow = isOverflow( nVal, nMaxValue, nStepValue, nStartVal, eFillCmd); } if (bError) aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::NoValue); - else if (bOverflow) - aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::IllegalFPOperation); - else + else if (!bOverflow) aCol[nCol].SetValue(static_cast<SCROW>(nRow), nVal); + + if (bAttribs && !bEntireArea && !bOverflow) + SetPatternAreaCondFormat( nCol, nRow, nRow, *pSrcPattern, rCondFormatIndex); } - if (rInner == nIEnd) + if (rInner == nIEnd || bOverflow) break; if (bPositive) { @@ -1832,7 +1917,7 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, short nHeadNoneTail = lcl_DecompValueString( aValue, nStringValue, &nMinDigits ); if ( nHeadNoneTail ) { - double nStartVal = static_cast<double>(nStringValue); + const double nStartVal = static_cast<double>(nStringValue); double nVal = nStartVal; long nIndex = 0; bool bError = false; @@ -1842,11 +1927,11 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, static_cast<sal_Int32>(nStartVal)); rInner = nIStart; - while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + while (true) { if(!ColHidden(nCol) && !RowHidden(nRow)) { - if (!bError && !bOverflow) + if (!bError) { switch (eFillCmd) { @@ -1871,29 +1956,13 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, } } - if (nStepValue >= 0) - { - if (nVal > nMaxValue) // target value reached ? - { - nVal = nMaxValue; - bOverflow = true; - } - } - else - { - if (nVal < nMaxValue) - { - nVal = nMaxValue; - bOverflow = true; - } - } + if (!bError) + bOverflow = isOverflow( nVal, nMaxValue, nStepValue, nStartVal, eFillCmd); } if (bError) aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::NoValue); - else if (bOverflow) - aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::IllegalFPOperation); - else + else if (!bOverflow) { nStringValue = static_cast<sal_Int32>(nVal); OUString aStr; @@ -1914,10 +1983,17 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, aCol[nCol].SetRawString(static_cast<SCROW>(nRow), aStr); } } + + if (bAttribs && !bEntireArea && !bOverflow) + SetPatternAreaCondFormat( nCol, nRow, nRow, *pSrcPattern, rCondFormatIndex); } - if (rInner == nIEnd) break; - if (bPositive) ++rInner; else --rInner; + if (rInner == nIEnd || bOverflow) + break; + if (bPositive) + ++rInner; + else + --rInner; } } if(pProgress) |