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
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 29a7b28..7d28afc 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 df0a151..e4d062f 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 0fb45a2..15e6cc7 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)