summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
Diffstat (limited to 'sc')
-rw-r--r--sc/inc/document.hxx2
-rw-r--r--sc/inc/table.hxx8
-rw-r--r--sc/qa/unit/copy_paste_test.cxx60
-rw-r--r--sc/qa/unit/data/ods/tdf88782_AutofillLinearNumbersInMergedCells.odsbin0 -> 24452 bytes
-rw-r--r--sc/source/core/data/table4.cxx171
-rw-r--r--sc/source/ui/inc/cellmergeoption.hxx6
-rw-r--r--sc/source/ui/inc/docfunc.hxx3
7 files changed, 234 insertions, 16 deletions
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 62315b51429f..7fcb3af03fc6 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1178,7 +1178,7 @@ public:
* @return pointer to the double value stored in a numeric cell, or NULL
* if the cell at specified position is not a numeric cell.
*/
- double* GetValueCell( const ScAddress& rPos );
+ SC_DLLPUBLIC double* GetValueCell( const ScAddress& rPos );
SC_DLLPUBLIC svl::SharedStringPool& GetSharedStringPool();
const svl::SharedStringPool& GetSharedStringPool() const;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 15a74d3fbaf8..3c2ab08e2b9c 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1115,11 +1115,15 @@ private:
sal_uLong nFillCount, FillDir eFillDir, FillCmd eFillCmd,
FillDateCmd eFillDateCmd,
double nStepValue, double nMaxValue, sal_uInt16 nMinDigits,
- bool bAttribs, ScProgress* pProgress );
+ bool bAttribs, ScProgress* pProgress,
+ bool bSkipOverlappedCells = false,
+ std::vector<sal_Int32>* pNonOverlappedCellIdx = nullptr);
void FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
FillCmd& rCmd, FillDateCmd& rDateCmd,
double& rInc, sal_uInt16& rMinDigits,
- ScUserListData*& rListData, sal_uInt16& rListIndex);
+ ScUserListData*& rListData, sal_uInt16& rListIndex,
+ bool bHasFiltered, bool& rSkipOverlappedCells,
+ std::vector<sal_Int32>& rNonOverlappedCellIdx );
void FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
sal_uLong nFillCount, FillDir eFillDir, ScProgress* pProgress );
diff --git a/sc/qa/unit/copy_paste_test.cxx b/sc/qa/unit/copy_paste_test.cxx
index caeb05263a9c..325a883d212d 100644
--- a/sc/qa/unit/copy_paste_test.cxx
+++ b/sc/qa/unit/copy_paste_test.cxx
@@ -12,6 +12,8 @@
#include <comphelper/processfactory.hxx>
#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <cellmergeoption.hxx>
#include <tabvwsh.hxx>
#include <impex.hxx>
#include <viewfunc.hxx>
@@ -42,6 +44,7 @@ public:
void testTdf53431_fillOnAutofilter();
void testTdf40993_fillMergedCells();
void testTdf43958_clickSelectOnMergedCells();
+ void testTdf88782_autofillLinearNumbersInMergedCells();
CPPUNIT_TEST_SUITE(ScCopyPasteTest);
CPPUNIT_TEST(testCopyPasteXLS);
@@ -52,6 +55,7 @@ public:
CPPUNIT_TEST(testTdf53431_fillOnAutofilter);
CPPUNIT_TEST(testTdf40993_fillMergedCells);
CPPUNIT_TEST(testTdf43958_clickSelectOnMergedCells);
+ CPPUNIT_TEST(testTdf88782_autofillLinearNumbersInMergedCells);
CPPUNIT_TEST_SUITE_END();
private:
@@ -620,6 +624,62 @@ void ScCopyPasteTest::testTdf43958_clickSelectOnMergedCells()
lcl_clickAndCheckCurrentArea(2, 8, 2, 8); // C9
}
+void ScCopyPasteTest::testTdf88782_autofillLinearNumbersInMergedCells()
+{
+ ScDocShellRef xDocSh = loadDocAndSetupModelViewController("tdf88782_AutofillLinearNumbersInMergedCells.", FORMAT_ODS, true);
+ ScDocument& rDoc = xDocSh->GetDocument();
+
+ // Get the document controller
+ ScTabViewShell* pView = xDocSh->GetBestViewShell(false);
+ CPPUNIT_ASSERT(pView != nullptr);
+
+ // merge the yellow cells
+ ScCellMergeOption aMergeOptions(9, 11, 10, 13); //J12:K14
+ aMergeOptions.maTabs.insert(0);
+ xDocSh->GetDocFunc().MergeCells(aMergeOptions, false, true, true, false);
+
+ // fillauto numbers, these areas contains mostly merged cells
+ pView->FillAuto(FILL_TO_BOTTOM, 1, 8, 3, 14, 7); // B9:D15 -> B9:D22
+ pView->FillAuto(FILL_TO_BOTTOM, 5, 8, 7, 17, 10); // F9:H18 -> F9:H28
+ pView->FillAuto(FILL_TO_BOTTOM, 9, 8, 10, 13, 6); // J9:K14 -> J9:K20
+ pView->FillAuto(FILL_TO_RIGHT, 9, 30, 16, 35, 8); //J31:Q36 -> J31:Y36
+ pView->FillAuto(FILL_TO_LEFT, 9, 30, 16, 35, 8); //J31:Q36 -> B31:Q36
+
+ // compare the results of fill-down with the reference stored in the test file
+ // this compare the whole area blindly, for concrete test cases, check the test file
+ // the test file have instructions / explanations, so that is easy to understand
+ for (int nCol = 1; nCol <= 10; nCol++) {
+ for (int nRow = 8; nRow <= 27; nRow++) {
+ CellType nType1 = rDoc.GetCellType(ScAddress(nCol, nRow, 0));
+ CellType nType2 = rDoc.GetCellType(ScAddress(nCol + 22, nRow, 0));
+ double* pValue1 = rDoc.GetValueCell(ScAddress(nCol, nRow, 0));
+ double* pValue2 = rDoc.GetValueCell(ScAddress(nCol + 22, nRow, 0));
+
+ CPPUNIT_ASSERT_EQUAL(nType1, nType2);
+ if (pValue2 != nullptr)
+ CPPUNIT_ASSERT_EQUAL(*pValue1, *pValue2); //cells with number value
+ else
+ CPPUNIT_ASSERT_EQUAL(pValue1, pValue2); //empty cells
+ }
+ }
+
+ // compare the results of fill-right and left with the reference stored in the test file
+ for (int nCol = 1; nCol <= 24; nCol++) {
+ for (int nRow = 30; nRow <= 35; nRow++) {
+ CellType nType1 = rDoc.GetCellType(ScAddress(nCol, nRow, 0));
+ CellType nType2 = rDoc.GetCellType(ScAddress(nCol, nRow + 16, 0));
+ double* pValue1 = rDoc.GetValueCell(ScAddress(nCol, nRow, 0));
+ double* pValue2 = rDoc.GetValueCell(ScAddress(nCol, nRow + 16, 0));
+
+ CPPUNIT_ASSERT_EQUAL(nType1, nType2);
+ if (pValue2 != nullptr)
+ CPPUNIT_ASSERT_EQUAL(*pValue1, *pValue2);
+ else
+ CPPUNIT_ASSERT_EQUAL(pValue1, pValue2);
+ }
+ }
+}
+
ScCopyPasteTest::ScCopyPasteTest()
: ScBootstrapFixture( "sc/qa/unit/data" )
{
diff --git a/sc/qa/unit/data/ods/tdf88782_AutofillLinearNumbersInMergedCells.ods b/sc/qa/unit/data/ods/tdf88782_AutofillLinearNumbersInMergedCells.ods
new file mode 100644
index 000000000000..99fea7087010
--- /dev/null
+++ b/sc/qa/unit/data/ods/tdf88782_AutofillLinearNumbersInMergedCells.ods
Binary files differ
diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx
index f6058d51c921..e2d86480cc72 100644
--- a/sc/source/core/data/table4.cxx
+++ b/sc/source/core/data/table4.cxx
@@ -216,7 +216,9 @@ double approxDiff( double a, double b )
void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
FillCmd& rCmd, FillDateCmd& rDateCmd,
double& rInc, sal_uInt16& rMinDigits,
- ScUserListData*& rListData, sal_uInt16& rListIndex)
+ ScUserListData*& rListData, sal_uInt16& rListIndex,
+ bool bHasFiltered, bool& rSkipOverlappedCells,
+ std::vector<sal_Int32>& rNonOverlappedCellIdx)
{
OSL_ENSURE( nCol1==nCol2 || nRow1==nRow2, "FillAnalyse: invalid range" );
@@ -224,6 +226,7 @@ void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
rMinDigits = 0;
rListData = nullptr;
rCmd = FILL_SIMPLE;
+ rSkipOverlappedCells = false;
if ( nScFillModeMouseModifier & KEY_MOD1 )
return ; // Ctrl-key: Copy
@@ -243,6 +246,100 @@ void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
nCount = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
}
+ // Try to analyse the merged cells only if there are no filtered rows in the destination area
+ // Else fallback to the old way to avoid regression.
+ // Filling merged cells into an area with filtered (hidden) rows, is a very complex task
+ // that is not implemented, but not even decided how to do, even excel can't handle that well
+ if (!bHasFiltered)
+ {
+ bool bHasOverlappedCells = false;
+ bool bSkipOverlappedCells = true;
+ SCCOL nColAkt = nCol1;
+ SCROW nRowAkt = nRow1;
+
+ // collect cells that are not empty or not overlapped
+ rNonOverlappedCellIdx.resize(nCount);
+ SCSIZE nValueCount = 0;
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ const ScPatternAttr* pPattern = GetPattern(nColAkt, nRowAkt);
+ bool bOverlapped
+ = pPattern->GetItemSet().GetItemState(ATTR_MERGE_FLAG, false) == SfxItemState::SET
+ && pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped();
+
+ if (bOverlapped)
+ bHasOverlappedCells = true;
+
+ if (!bOverlapped || GetCellValue(nColAkt, nRowAkt).meType != CELLTYPE_NONE)
+ {
+ rNonOverlappedCellIdx[nValueCount++] = i;
+ // if there is at least 1 non empty overlapped cell, then no cell should be skipped
+ if (bOverlapped)
+ bSkipOverlappedCells = false;
+ }
+
+ nColAkt += nAddX;
+ nRowAkt += nAddY;
+ }
+ rNonOverlappedCellIdx.resize(nValueCount);
+
+ // if all the values are overlapped CELLTYPE_NONE, then there is no need to analyse it.
+ if (nValueCount == 0)
+ return;
+
+ // if there is no overlapped cells, there is nothing to skip
+ if (!bHasOverlappedCells)
+ bSkipOverlappedCells = false;
+
+ if (bSkipOverlappedCells)
+ {
+ nColAkt = nCol1 + rNonOverlappedCellIdx[0] * nAddX;
+ nRowAkt = nRow1 + rNonOverlappedCellIdx[0] * nAddY;
+ ScRefCellValue aPrevCell, aAktCell;
+ aAktCell = GetCellValue(nColAkt, nRowAkt);
+ CellType eCellType = aAktCell.meType;
+ if (eCellType == CELLTYPE_VALUE)
+ {
+ // TODO: Check / handle special cases of number formats: like date, boolean
+ bool bVal = true;
+ if (nValueCount >= 2)
+ {
+ for (SCSIZE i = 1; i < nValueCount && bVal; i++)
+ {
+ aPrevCell = aAktCell;
+ nColAkt = nCol1 + rNonOverlappedCellIdx[i] * nAddX;
+ nRowAkt = nRow1 + rNonOverlappedCellIdx[i] * nAddY;
+ aAktCell = GetCellValue(nColAkt, nRowAkt);
+ if (aAktCell.meType == CELLTYPE_VALUE)
+ {
+ double nDiff = approxDiff(aAktCell.mfValue, aPrevCell.mfValue);
+ if (i == 1)
+ rInc = nDiff;
+ if (!::rtl::math::approxEqual(nDiff, rInc, 13))
+ bVal = false;
+ }
+ else
+ bVal = false;
+ }
+ if (bVal)
+ {
+ rCmd = FILL_LINEAR;
+ rSkipOverlappedCells = true;
+ return;
+ }
+ }
+ }
+ else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT)
+ {
+ // TODO: check / handle if it is a sequence of userlist string
+ // or if the strings are composition of a number sequence and a constant string
+ }
+ }
+ }
+
+ //if it is not a FILL_LINEAR - CELLTYPE_VALUE - with merged cells [without hidden values]
+ //then do it in the old way
+
SCCOL nCol = nCol1;
SCROW nRow = nRow1;
@@ -795,14 +892,18 @@ void ScTable::FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
sal_uInt16 nMinDigits;
ScUserListData* pListData = nullptr;
sal_uInt16 nListIndex;
+ bool nSkipOverlappedCells;
+ std::vector<sal_Int32> aNonOverlappedCellIdx;
if (bVertical)
FillAnalyse(static_cast<SCCOL>(nCol),nRow1,
static_cast<SCCOL>(nCol),nRow2, eFillCmd,eDateCmd,
- nInc,nMinDigits, pListData,nListIndex);
+ nInc, nMinDigits, pListData, nListIndex,
+ bHasFiltered, nSkipOverlappedCells, aNonOverlappedCellIdx);
else
FillAnalyse(nCol1,static_cast<SCROW>(nRow),
nCol2,static_cast<SCROW>(nRow), eFillCmd,eDateCmd,
- nInc,nMinDigits, pListData,nListIndex);
+ nInc, nMinDigits, pListData, nListIndex,
+ bHasFiltered, nSkipOverlappedCells, aNonOverlappedCellIdx);
if (pListData)
{
@@ -860,12 +961,12 @@ void ScTable::FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
FillSeries( static_cast<SCCOL>(nCol), nRow1,
static_cast<SCCOL>(nCol), nRow2, nFillCount, eFillDir,
eFillCmd, eDateCmd, nInc, nEndVal, nMinDigits, false,
- pProgress );
+ pProgress, nSkipOverlappedCells, &aNonOverlappedCellIdx);
else
FillSeries( nCol1, static_cast<SCROW>(nRow), nCol2,
static_cast<SCROW>(nRow), nFillCount, eFillDir,
eFillCmd, eDateCmd, nInc, nEndVal, nMinDigits, false,
- pProgress );
+ pProgress, nSkipOverlappedCells, &aNonOverlappedCellIdx);
if (pProgress)
nProgress = pProgress->GetState();
}
@@ -919,8 +1020,15 @@ OUString ScTable::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW
sal_uInt16 nMinDigits;
ScUserListData* pListData = nullptr;
sal_uInt16 nListIndex;
+ bool nSkipOverlappedCells;
+ std::vector<sal_Int32> aNonOverlappedCellIdx;
- FillAnalyse(nCol1,nRow1, nCol2,nRow2, eFillCmd,eDateCmd, nInc,nMinDigits, pListData,nListIndex);
+ // Todo: update this function to calculate with merged cell fills,
+ // after FillAnalyse / FillSeries fully handle them.
+ // Now FillAnalyse called as if there are filtered rows, so it will work in the old way.
+ FillAnalyse(nCol1, nRow1, nCol2, nRow2, eFillCmd, eDateCmd,
+ nInc, nMinDigits, pListData, nListIndex,
+ true, nSkipOverlappedCells, aNonOverlappedCellIdx);
if ( pListData ) // user defined list
{
@@ -1693,7 +1801,8 @@ inline bool isOverflow( const double& rVal, const double& rMax, const double& rS
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,
- bool bAttribs, ScProgress* pProgress )
+ bool bAttribs, ScProgress* pProgress,
+ bool bSkipOverlappedCells, std::vector<sal_Int32>* pNonOverlappedCellIdx )
{
// The term 'inner' here refers to the loop in the filling direction i.e.
// when filling vertically, the inner position is the row position whereas
@@ -1718,9 +1827,12 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
SCCOLROW nIEnd;
SCCOLROW nISource;
ScRange aFillRange;
+ sal_uLong nFillerCount;
+ std::vector<bool> aIsNonEmptyCell;
if (bVertical)
{
+ nFillerCount = (nRow2 - nRow1) + 1;
nFillCount += (nRow2 - nRow1);
if (nFillCount == 0)
return;
@@ -1745,6 +1857,7 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
}
else
{
+ nFillerCount = (nCol2 - nCol1) + 1;
nFillCount += (nCol2 - nCol1);
if (nFillCount == 0)
return;
@@ -1781,6 +1894,8 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
// 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.
+ // This is not exact in case of merged cell fills with skipping overlapped parts, but
+ // it is still a good upper estimation.
ScCellValue aSrcCell;
if (bVertical)
aSrcCell = aCol[static_cast<SCCOL>(nOStart)].GetCellValue(static_cast<SCROW>(nISource));
@@ -1841,6 +1956,34 @@ 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));
+ // Maybe another source cell need to be searched, if the fill is going trough merged cells,
+ // where overlapped parts does not contain any information, so they can be skipped.
+ if (bSkipOverlappedCells)
+ {
+ // create a vector to make it easier to decide if a cell need to be filled, or skipped.
+ aIsNonEmptyCell.resize(nFillerCount, false);
+
+ SCCOLROW nfirstValueIdx;
+ if (bPositive)
+ {
+ nfirstValueIdx = nISource + (*pNonOverlappedCellIdx)[0];
+ for (auto i : (*pNonOverlappedCellIdx))
+ aIsNonEmptyCell[i] = true;
+ }
+ else
+ {
+ nfirstValueIdx = nISource - (nFillerCount - 1 - (*pNonOverlappedCellIdx).back());
+ for (auto i : (*pNonOverlappedCellIdx))
+ aIsNonEmptyCell[nFillerCount - 1 - i] = true;
+ }
+
+ //Set the real source cell
+ if (bVertical)
+ aSrcCell = aCol[nOStart].GetCellValue(static_cast<SCROW>(nfirstValueIdx));
+ else
+ aSrcCell = aCol[nfirstValueIdx].GetCellValue(static_cast<SCROW>(nOStart));
+ }
+
const ScPatternAttr* pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nRow));
const ScCondFormatItem& rCondFormatItem = pSrcPattern->GetItem(ATTR_CONDITIONAL);
const ScCondFormatIndexes& rCondFormatIndex = rCondFormatItem.GetCondFormatData();
@@ -1899,14 +2042,24 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
bool bError = false;
bool bOverflow = false;
+ bool bNonEmpty = true;
sal_uInt16 nDayOfMonth = 0;
+ sal_Int32 nFillerIdx = 0;
+ if (bSkipOverlappedCells && !aIsNonEmptyCell[0])
+ --nIndex;
rInner = nIStart;
while (true)
{
+ if (bSkipOverlappedCells)
+ {
+ nFillerIdx = (nFillerIdx + 1) % nFillerCount;
+ bNonEmpty = aIsNonEmptyCell[nFillerIdx];
+ }
+
if(!ColHidden(nCol) && !RowHidden(nRow))
{
- if (!bError)
+ if (!bError && bNonEmpty)
{
switch (eFillCmd)
{
@@ -1943,7 +2096,7 @@ void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
if (bError)
aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::NoValue);
- else if (!bOverflow)
+ else if (!bOverflow && bNonEmpty)
aCol[nCol].SetValue(static_cast<SCROW>(nRow), nVal);
if (bAttribs && !bEntireArea && !bOverflow)
diff --git a/sc/source/ui/inc/cellmergeoption.hxx b/sc/source/ui/inc/cellmergeoption.hxx
index ff4a7cbebcc9..937b9079960d 100644
--- a/sc/source/ui/inc/cellmergeoption.hxx
+++ b/sc/source/ui/inc/cellmergeoption.hxx
@@ -24,9 +24,9 @@ struct ScCellMergeOption
bool mbCenter;
explicit ScCellMergeOption(const ScRange& rRange);
- explicit ScCellMergeOption(SCCOL nStartCol, SCROW nStartRow,
- SCCOL nEndCol, SCROW nEndRow,
- bool bCenter = false);
+ SC_DLLPUBLIC explicit ScCellMergeOption(SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ bool bCenter = false);
ScRange getSingleRange(SCTAB nTab) const;
ScRange getFirstSingleRange() const;
diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx
index e3a8117a5493..1984c0da440a 100644
--- a/sc/source/ui/inc/docfunc.hxx
+++ b/sc/source/ui/inc/docfunc.hxx
@@ -191,7 +191,8 @@ public:
void ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd );
- bool MergeCells( const ScCellMergeOption& rOption, bool bContents,
+ SC_DLLPUBLIC bool
+ MergeCells( const ScCellMergeOption& rOption, bool bContents,
bool bRecord, bool bApi, bool bEmptyMergedCells = false );
bool UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge );
bool UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge );