summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
authorAttila Szűcs <szucs.attila3@nisz.hu>2020-10-01 14:40:08 +0200
committerLászló Németh <nemeth@numbertext.org>2020-10-05 11:22:12 +0200
commit03dfa09886b6fd0ebda7abe7d5e142da172e8cc2 (patch)
tree41028434105f9cd5b6756e745bdd470bcc401d07 /sc
parent47f0e09af5bfc250d91896d0af56b34b83bd16a2 (diff)
tdf#88782 sc: autofill number sequences in merged cells
Improve FillAuto, FillAnalyse and FillSeries to continue linear sequences of numbers in merged cells by skipping the empty overlapped cells of the merged area, like other spreadsheets do. For example this fix autofill, when merged cells are used to highlight nth numbers. Instead of 1, 2 -> 1, 2, 2, 3, we get 1, 2 -> 1, 2, 3, 4 on the following fill: +-+-+-+ +-+-+-+-+-+-+ |1| 2 | -> |1| 2 |3| 4 | +-+-+-+ +-+-+-+-+-+-+ See the unit test document for more complex examples, and use merge/unmerge to check the work of the algorithm. For example, column C of the test document contains cells EMPTY, EMPTY, EMPTY, 2, EMPTY, 4, EMPTY calculated as 2, 4 during fill by skipping empty overlapped cells, but keeping also the merged structure, resulting the requested continuation of the linear sequence: EMPTY, EMPTY, EMPTY, 6, EMPTY, 8, EMPTY Note: special formats are not handled yet like date, string, boolean and userlist. Co-authored-by: Tibor Nagy (NISZ) Change-Id: Ib431e8968f5d71e321b0e57cfb173534a0f5da31 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103765 Tested-by: László Németh <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org>
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 );