[Libreoffice-commits] core.git: sc/qa sc/source

Attila Szűcs (via logerrit) logerrit at kemper.freedesktop.org
Wed Sep 30 08:30:47 UTC 2020


 sc/qa/unit/copy_paste_test.cxx                   |  100 ++++++++++++++++++++++-
 sc/qa/unit/data/ods/tdf40993_fillMergedCells.ods |binary
 sc/source/core/data/table4.cxx                   |   60 ++++++++++++-
 sc/source/ui/inc/viewfunc.hxx                    |    3 
 4 files changed, 152 insertions(+), 11 deletions(-)

New commits:
commit 1ed04c2029218619aab2f3422130c890f67f309c
Author:     Attila Szűcs <szucs.attila3 at nisz.hu>
AuthorDate: Thu Sep 10 16:46:30 2020 +0200
Commit:     László Németh <nemeth at numbertext.org>
CommitDate: Wed Sep 30 10:30:08 2020 +0200

    tdf#40993 tdf#59585 sc fill: copy merged cell structure
    
    like other spreadsheets do. This avoids losing and
    recreation of merged cell structure again and again
    after modifying cell content using fill.
    
    By dragging the fill handle, cell attributes ATTR_MERGE
    and ATTR_MERGE_FLAG are copied, too, resulting the
    same merged cell structure in the selection. See TODOs
    about the remaining tasks.
    
    Co-authored-by: Tibor Nagy (NISZ)
    
    Change-Id: Icd4287d4429d3ba27a2ffb062560856d7ad86099
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102394
    Tested-by: László Németh <nemeth at numbertext.org>
    Reviewed-by: László Németh <nemeth at numbertext.org>

diff --git a/sc/qa/unit/copy_paste_test.cxx b/sc/qa/unit/copy_paste_test.cxx
index 70971c1d804a..0cb2dac43145 100644
--- a/sc/qa/unit/copy_paste_test.cxx
+++ b/sc/qa/unit/copy_paste_test.cxx
@@ -40,6 +40,7 @@ public:
     void testTdf126421();
     void testTdf107394();
     void testTdf53431_fillOnAutofilter();
+    void testTdf40993_fillMergedCells();
 
     CPPUNIT_TEST_SUITE(ScCopyPasteTest);
     CPPUNIT_TEST(testCopyPasteXLS);
@@ -48,10 +49,12 @@ public:
     CPPUNIT_TEST(testTdf126421);
     CPPUNIT_TEST(testTdf107394);
     CPPUNIT_TEST(testTdf53431_fillOnAutofilter);
+    CPPUNIT_TEST(testTdf40993_fillMergedCells);
     CPPUNIT_TEST_SUITE_END();
 
 private:
 
+    ScDocShellRef loadDocAndSetupModelViewController(const OUString& rFileName, sal_Int32 nFormat, bool bReadWrite);
     uno::Reference<uno::XInterface> m_xCalcComponent;
 };
 
@@ -433,7 +436,14 @@ static ScMF lcl_getMergeFlagOfCell(const ScDocument& rDoc, SCCOL nCol, SCROW nRo
     return rMergeFlag.GetValue();
 }
 
-void ScCopyPasteTest::testTdf53431_fillOnAutofilter()
+static ScAddress lcl_getMergeSizeOfCell(const ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+    const SfxPoolItem& rPoolItem = rDoc.GetPattern(nCol, nRow, nTab)->GetItem(ATTR_MERGE);
+    const ScMergeAttr& rMerge = static_cast<const ScMergeAttr&>(rPoolItem);
+    return ScAddress(rMerge.GetColMerge(), rMerge.GetRowMerge(), nTab);
+}
+
+ScDocShellRef ScCopyPasteTest::loadDocAndSetupModelViewController(const OUString& rFileName, sal_Int32 nFormat, bool bReadWrite)
 {
     uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext());
     CPPUNIT_ASSERT(xDesktop.is());
@@ -443,8 +453,8 @@ void ScCopyPasteTest::testTdf53431_fillOnAutofilter()
     CPPUNIT_ASSERT(xTargetFrame.is());
 
     // 1. Open the document
-    ScDocShellRef xDocSh = loadDoc("tdf53431_autofilterFilldown.", FORMAT_ODS, true);
-    CPPUNIT_ASSERT_MESSAGE("Failed to load tdf53431_autofilterFilldown.ods.", xDocSh.is());
+    ScDocShellRef xDocSh = loadDoc(rFileName, nFormat, bReadWrite);
+    CPPUNIT_ASSERT_MESSAGE(OString("Failed to load " + OUStringToOString(rFileName, RTL_TEXTENCODING_UTF8)).getStr(), xDocSh.is());
 
     uno::Reference< frame::XModel2 > xModel2(xDocSh->GetModel(), UNO_QUERY);
     CPPUNIT_ASSERT(xModel2.is());
@@ -459,6 +469,12 @@ void ScCopyPasteTest::testTdf53431_fillOnAutofilter()
     xController->attachFrame(xTargetFrame);
     xModel2->setCurrentController(xController.get());
 
+    return xDocSh;
+}
+
+void ScCopyPasteTest::testTdf53431_fillOnAutofilter()
+{
+    ScDocShellRef xDocSh = loadDocAndSetupModelViewController("tdf53431_autofilterFilldown.", FORMAT_ODS, true);
     ScDocument& rDoc = xDocSh->GetDocument();
 
     // Get the document controller
@@ -494,6 +510,84 @@ void ScCopyPasteTest::testTdf53431_fillOnAutofilter()
     CPPUNIT_ASSERT(!(lcl_getMergeFlagOfCell(rDoc, 0, 1, 0) & ScMF::Auto));
 }
 
+void ScCopyPasteTest::testTdf40993_fillMergedCells()
+{
+    ScDocShellRef xDocSh = loadDocAndSetupModelViewController("tdf40993_fillMergedCells.", FORMAT_ODS, true);
+    ScDocument& rDoc = xDocSh->GetDocument();
+
+    // Get the document controller
+    ScTabViewShell* pView = xDocSh->GetBestViewShell(false);
+    CPPUNIT_ASSERT(pView != nullptr);
+
+    // check content of the merged cell H11:I11
+    CPPUNIT_ASSERT_EQUAL(OUString("1.5"), rDoc.GetString(ScAddress(7, 10, 0)));
+
+    // fill operation on the merged cell should clone ATTR_MERGE and ATTR_MERGE_FLAG
+    // (as long as ATTR_MERGE_FLAG has only ScMF::Hor or ScMF::Ver)
+    //
+    // select merged cell
+    ScDocShell::GetViewData()->GetMarkData().SetMarkArea(ScRange(7, 10, 0, 8, 10, 0));
+    // copy its content in the next ten rows
+    pView->FillAuto(FILL_TO_BOTTOM, 7, 10, 8, 10, 10);
+    for (int i = 7; i < 9; i++)
+    {
+        ScMF nOriginFlag = lcl_getMergeFlagOfCell(rDoc, i, 10, 0);
+        ScAddress aOriginMerge = lcl_getMergeSizeOfCell(rDoc, i, 10, 0);
+
+        // ATTR_MERGE_FLAG: top left cell is NONE, the other cell shows horizontal overlapping
+        CPPUNIT_ASSERT_EQUAL(i == 7 ? ScMF::NONE : ScMF::Hor, nOriginFlag);
+
+        // ATTR_MERGE: top left cell contains the size of the
+        // merged area (2:1), the other cell doesn't
+        CPPUNIT_ASSERT_EQUAL(i == 7 ? ScAddress(2, 1, 0): ScAddress(0, 0, 0), aOriginMerge);
+
+        for (int j = 11; j < 21; j++)
+        {
+            // check copying of ATTR_MERGE and ATTR_MERGE_FLAG
+            CPPUNIT_ASSERT_EQUAL(lcl_getMergeFlagOfCell(rDoc, i, j, 0), nOriginFlag);
+            CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, i, j, 0), aOriginMerge);
+        }
+    }
+
+    CPPUNIT_ASSERT_EQUAL(lcl_getMergeFlagOfCell(rDoc, 7, 21, 0),
+                    lcl_getMergeFlagOfCell(rDoc, 7, 10, 0));
+    CPPUNIT_ASSERT(lcl_getMergeSizeOfCell(rDoc, 7, 21, 0) !=
+                    lcl_getMergeSizeOfCell(rDoc, 7, 10, 0));
+    CPPUNIT_ASSERT(lcl_getMergeFlagOfCell(rDoc, 8, 21, 0) !=
+                    lcl_getMergeFlagOfCell(rDoc, 8, 10, 0));
+    CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, 8, 21, 0),
+                    lcl_getMergeSizeOfCell(rDoc, 8, 10, 0));
+
+    // area A6:E9 with various merged cells copied vertically and horizontally
+    ScDocShell::GetViewData()->GetMarkData().SetMarkArea(ScRange(0, 5, 0, 4, 8, 0));
+    pView->FillAuto(FILL_TO_BOTTOM, 0, 5, 4, 8, 12);
+    ScDocShell::GetViewData()->GetMarkData().SetMarkArea(ScRange(0, 5, 0, 4, 8, 0));
+    pView->FillAuto(FILL_TO_RIGHT, 0, 5, 4, 8, 10);
+    for (int i = 0; i < 5; i++)
+    {
+        for (int j = 5; j < 9; j++)
+        {
+            ScMF nOriginFlag = lcl_getMergeFlagOfCell(rDoc, i, j, 0);
+            ScAddress aOriginMerge = lcl_getMergeSizeOfCell(rDoc, i, j, 0);
+            // copies contain the same ATTR_MERGE and ATTR_MERGE_FLAG
+            for (int k = 0; k < 12; k += 4)
+            {
+                CPPUNIT_ASSERT_EQUAL(lcl_getMergeFlagOfCell(rDoc, i, j + k, 0), nOriginFlag);
+                CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, i, j + k, 0), aOriginMerge);
+            }
+            for (int k = 0; k < 10; k += 5)
+            {
+                CPPUNIT_ASSERT_EQUAL(lcl_getMergeFlagOfCell(rDoc, i + k, j, 0), nOriginFlag);
+                CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, i + k, j, 0), aOriginMerge);
+            }
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, 1, 5, 0), ScAddress(2, 4, 0));
+    CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, 0, 5, 0), ScAddress(1, 2, 0));
+    CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, 4, 6, 0), ScAddress(1, 2, 0));
+    CPPUNIT_ASSERT_EQUAL(lcl_getMergeSizeOfCell(rDoc, 3, 5, 0), ScAddress(2, 1, 0));
+}
+
 ScCopyPasteTest::ScCopyPasteTest()
       : ScBootstrapFixture( "sc/qa/unit/data" )
 {
diff --git a/sc/qa/unit/data/ods/tdf40993_fillMergedCells.ods b/sc/qa/unit/data/ods/tdf40993_fillMergedCells.ods
new file mode 100644
index 000000000000..d1e455b33dec
Binary files /dev/null and b/sc/qa/unit/data/ods/tdf40993_fillMergedCells.ods differ
diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx
index 2bd2076689b0..23175efdcfb2 100644
--- a/sc/source/core/data/table4.cxx
+++ b/sc/source/core/data/table4.cxx
@@ -640,15 +640,61 @@ void ScTable::FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
                         pSrcPattern = aCol[nAtSrc].GetPattern(static_cast<SCROW>(nRow));
                     bGetPattern = false;
                     pStyleSheet = pSrcPattern->GetStyleSheet();
-                    //  do not transfer ATTR_MERGE / ATTR_MERGE_FLAG
+                    // do transfer ATTR_MERGE / ATTR_MERGE_FLAG
+                    //
+                    // Note: ATTR_MERGE is an attribute of the top left cell of a merged area
+                    // containing the size of the area. ATTR_MERGE_FLAGs are attributes of the
+                    // other cells of a merged area, containing the information about also
+                    // overlapping, i.e. visibility of their content.
+                    //
+                    // TODO: extend the similar incomplete selections to a bounding rectangle to
+                    // avoid incomplete fill, where not all AUTO_MERGE_FLAGs are synchronized with
+                    // the copied ATTR_MERGE, resulting broken grid and visibility during run-time.
+                    //
+                    //  +--+        +--+--+
+                    //  |  |        |  |  |
+                    //  +--+--+     +--+--+
+                    //  |     | ->  |     |
+                    //  +--+--+     +--+--+
+                    //  |  |        |  |  |
+                    //  +--+        +--+--+
+                    //
+                    // TODO: protect incompatible merged cells of the destination area, for example
+                    // by skipping the fill operation.
+                    //
+                    // TODO: by dragging the fill handle select only the multiples of the height
+                    // of the originally selected area which is merged vertically to avoid of
+                    // incomplete fill.
+                    //
+                    //  +--+     +--+
+                    //  |XX|     |XX|
+                    //  +XX+     +XX+
+                    //  |XX| ->  |XX|
+                    //  +--+     +--+
+                    //  |  |     |  |
+                    //  +--+     +--+
+                    //           |  |
+                    //           +--+
+                    //
+                    // Other things stored in ATTR_MERGE_FLAG, like autofilter button, will be
+                    // deleted now, but may need to be repaired later, like at ScDocument::Fill.
                     const SfxItemSet& rSet = pSrcPattern->GetItemSet();
-                    if ( rSet.GetItemState(ATTR_MERGE, false) == SfxItemState::SET
-                            || rSet.GetItemState(ATTR_MERGE_FLAG, false) == SfxItemState::SET )
+                    if ( rSet.GetItemState(ATTR_MERGE_FLAG, false) == SfxItemState::SET )
                     {
-                        pNewPattern.reset( new ScPatternAttr( *pSrcPattern ));
-                        SfxItemSet& rNewSet = pNewPattern->GetItemSet();
-                        rNewSet.ClearItem(ATTR_MERGE);
-                        rNewSet.ClearItem(ATTR_MERGE_FLAG);
+                        ScMF nOldValue = pSrcPattern->GetItem(ATTR_MERGE_FLAG).GetValue();
+                        ScMF nOldValueMerge = nOldValue & (ScMF::Hor | ScMF::Ver);
+                        // keep only the merge flags
+                        if ( nOldValue != nOldValueMerge )
+                        {
+                            pNewPattern.reset(new ScPatternAttr(*pSrcPattern));
+                            SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+                            if ( nOldValueMerge == ScMF::NONE )
+                                rNewSet.ClearItem(ATTR_MERGE_FLAG);
+                            else
+                                rNewSet.Put(ScMergeFlagAttr(nOldValueMerge));
+                        }
+                        else
+                            pNewPattern.reset();
                     }
                     else
                         pNewPattern.reset();
diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx
index f1ce7196c7e1..7780e74bc790 100644
--- a/sc/source/ui/inc/viewfunc.hxx
+++ b/sc/source/ui/inc/viewfunc.hxx
@@ -242,7 +242,8 @@ public:
                     FillSimple( FillDir eDir );
     void            FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
                                 double fStart, double fStep, double fMax );
-    void            FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+    SC_DLLPUBLIC void
+                    FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
                                 SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount );
     void            FillCrossDblClick();
     void            ConvertFormulaToValue();


More information about the Libreoffice-commits mailing list