sw/qa/extras/uiwriter/uiwriter3.cxx  |  113 +++++++++++++++++++++++++++++++++++
 sw/source/uibase/dochdl/swdtflvr.cxx |   26 ++++++--
 2 files changed, 135 insertions(+), 4 deletions(-)

New commits:
commit c52acf9d1935b95a8279b760212553c0c3b4b267
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Tue Apr 26 12:29:29 2022 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Wed Apr 27 10:49:31 2022 +0200

    tdf#148791 tdf#64902 tdf#127759 sw: fix row insert/paste
    
    Table insertion mode and Paste as Rows Above inserted
    only a single row above, and overwrite the next ones in
    the following cases (instead of inserting/pasting rows
    above according to the clipboard table content):
    
    The table stored in the clipboard
    
    – has centered, right or not paragraph starting alignment;
    
    – or its resource table has a table header with 2 or more
      repeating rows, and the clipboard content contains the
      same amount or less rows (it doesn't matter, that
      the copied rows weren't header rows originally).
    
    The reason was that parsing of the HTML clipboard content
    which is used for counting the row count of the clipboard
    content hadn't handle the following cases:
    
    – different table aligment results <tr> elements with
      greater indentation level in the HTML extract, according
      to the new root element <center>, <div> or <dl>;
    
    – copying rows from a table with row header always starts
      with <thead> in the HTML extract, i.e. detecting <tbody>
      can fail, because there could be only <thead> in the
      HTML extract (see Case 2 above).
    
    Follow-up to commit 0c8b1efbad48fa9697c0b1afbe4753bbbc3c4c5c
    "tdf#127759 Writer: add table row/column insert mode" and
    commit 7efae60f3625a58f8a617c80f2a55a695fbaef36
    "tdf#64902 Writer table: Paste Special->Rows Above".
    
    Change-Id: I466cea20705bc5dd5455e22842da7dfa6631ba81
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133445
    Tested-by: Jenkins
    Reviewed-by: László Németh <nem...@numbertext.org>
    (cherry picked from commit 2fbf0f418ccb25010add33449d4e42b8b3f7fd0b)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133411
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx 
b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 26ca4e8db96f..3c453ab313da 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -2267,6 +2267,119 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf141391)
     assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt/Text", 
"Portion", "hello");
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf148791)
+{
+    // test Paste as Rows Above with centered table alignment
+
+    // load a 2-row table
+    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf116789.fodt");
+    CPPUNIT_ASSERT(pDoc);
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    CPPUNIT_ASSERT(pWrtShell);
+
+    // select and copy the table, and Paste As Rows Above
+
+    dispatchCommand(mxComponent, ".uno:SelectTable", {});
+    dispatchCommand(mxComponent, ".uno:Copy", {});
+    // remove the selection and positionate the cursor at beginning of A2
+    pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    pWrtShell->Up(/*bSelect=*/false);
+    dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {});
+    Scheduler::ProcessEventsToIdle();
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    // Paste as Rows Above results 4-row table with default table aligment
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 4);
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[1]/cell[1]/txt/Text", 
"Portion", "hello");
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[3]/cell[1]/txt/Text", 
"Portion", "hello");
+
+    // set table alignment to center, select and copy the table again
+    uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> 
xIndexAccess(xTextTablesSupplier->getTextTables(),
+                                                         uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount());
+
+    uno::Reference<text::XTextTable> xTextTable(xIndexAccess->getByIndex(0), 
uno::UNO_QUERY);
+
+    // Default table alignment
+    CPPUNIT_ASSERT_EQUAL(text::HoriOrientation::FULL,
+                         getProperty<sal_Int16>(xTextTable, "HoriOrient"));
+
+    //CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty<OUString>(xTextTable, 
"TableTemplateName"));
+    uno::Reference<beans::XPropertySet> xTableProps(xTextTable, 
uno::UNO_QUERY_THROW);
+
+    xTableProps->setPropertyValue("HoriOrient", 
uno::makeAny(text::HoriOrientation::CENTER));
+
+    CPPUNIT_ASSERT_EQUAL(text::HoriOrientation::CENTER,
+                         getProperty<sal_Int16>(xTextTable, "HoriOrient"));
+
+    dispatchCommand(mxComponent, ".uno:SelectTable", {});
+    dispatchCommand(mxComponent, ".uno:Copy", {});
+    // remove the selection and positionate the cursor at beginning of A2
+    pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    pWrtShell->Up(/*bSelect=*/false);
+    pWrtShell->Up(/*bSelect=*/false);
+    pWrtShell->Up(/*bSelect=*/false);
+    dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {});
+    Scheduler::ProcessEventsToIdle();
+
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    // This was 5 (inserting only a single row for the 4-row clipboard 
content, and
+    // overwriting 3 existing rows)
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 8);
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[1]/cell[1]/txt/Text", 
"Portion", "hello");
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[3]/cell[1]/txt/Text", 
"Portion", "hello");
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[5]/cell[1]/txt/Text", 
"Portion", "hello");
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[7]/cell[1]/txt/Text", 
"Portion", "hello");
+
+    // tdf#64902 add a test case for nested tables
+
+    // insert a nested table, and copy as paste as rows above the whole table 
with it
+    dispatchCommand(mxComponent, ".uno:PasteNestedTable", {});
+    dispatchCommand(mxComponent, ".uno:SelectTable", {});
+    dispatchCommand(mxComponent, ".uno:Copy", {});
+    // remove the selection and positionate the cursor at beginning of A2
+    pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    // skip 7 table rows plus 4 rows of the nested table
+    for (int i = 0; i < 7 + 4; ++i)
+        pWrtShell->Up(/*bSelect=*/false);
+    dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {});
+    Scheduler::ProcessEventsToIdle();
+
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    // rows of the nested table doesn't effect row number of the main table
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 16);
+    // there are two nested tables after the paste
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell/tab", 2);
+
+    // tdf#64902 add a test case for repeated table headings
+
+    xTableProps->setPropertyValue("RepeatHeadline", uno::makeAny(true));
+    CPPUNIT_ASSERT(getProperty<bool>(xTextTable, "RepeatHeadline"));
+
+    xTableProps->setPropertyValue("HeaderRowCount", 
uno::makeAny(sal_Int32(3)));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty<sal_Int32>(xTextTable, 
"HeaderRowCount"));
+
+    dispatchCommand(mxComponent, ".uno:SelectTable", {});
+    dispatchCommand(mxComponent, ".uno:Copy", {});
+    // remove the selection and positionate the cursor at beginning of A2
+    pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    // skip 15 table rows plus 4 * 2 rows of the nested tables
+    for (int i = 0; i < 15 + 4 * 2; ++i)
+        pWrtShell->Up(/*bSelect=*/false);
+    dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {});
+    Scheduler::ProcessEventsToIdle();
+
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    // repeating table header (and its thead/tbody indentation) doesn't effect 
row number
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 32);
+    // there are two nested tables after the paste
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell/tab", 4);
+}
+
 CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf135014)
 {
     createSwDoc();
diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx 
b/sw/source/uibase/dochdl/swdtflvr.cxx
index 3ec45999d067..db217051359e 100644
--- a/sw/source/uibase/dochdl/swdtflvr.cxx
+++ b/sw/source/uibase/dochdl/swdtflvr.cxx
@@ -1454,6 +1454,18 @@ void 
SwTransferable::SelectPasteFormat(TransferableDataHelper& rData, sal_uInt8&
     nFormat = SotClipboardFormatId::EMBED_SOURCE;
 }
 
+// get HTML indentation level by counting tabulator characters before the index
+// (also index value -1 returns with 0)
+static sal_Int32 lcl_getLevel(OUString& sText, sal_Int32 nIdx)
+{
+    sal_Int32 nRet = 0;
+    while ( nIdx-- > 0 && sText[nIdx] == '\t' )
+    {
+        nRet++;
+    }
+    return nRet;
+}
+
 bool SwTransferable::Paste(SwWrtShell& rSh, TransferableDataHelper& rData, 
RndStdIds nAnchorType, bool bIgnoreComments, PasteTableType ePasteTable)
 {
     SwPasteContext aPasteContext(rSh);
@@ -1568,16 +1580,22 @@ bool SwTransferable::Paste(SwWrtShell& rSh, 
TransferableDataHelper& rData, RndSt
         bool bRowMode = rSh.GetTableInsertMode() == SwTable::SEARCH_ROW || 
ePasteTable == PasteTableType::PASTE_ROW;
         if( rData.GetString( SotClipboardFormatId::HTML, aExpand ) && (nIdx = 
aExpand.indexOf("<table")) > -1 )
         {
-            // table rows with span use also tbody
-            bool bShifted = aExpand.indexOf("<tbody>") > -1;
+            // calculate table row/column count by analysing indentation of 
the HTML table extract
+
+            // calculate indentation level of <table>, which is the base of 
the next calculations
+            // (tdf#148791 table alignment can enlarge it using first level 
<center>, <div> or <dl>)
+            sal_Int32 nTableLevel = lcl_getLevel(aExpand, nIdx);
+            // table rows repeated heading use extra indentation, too:
+            // <thead> is always used here, and the first table with <thead> 
is not nested,
+            // if its indentation level is greater only by 1, than intentation 
level of the table
+            bool bShifted = lcl_getLevel(aExpand, aExpand.indexOf("<thead")) 
== nTableLevel + 1;
             // calculate count of selected rows or columns
             sal_Int32 nSelectedRowsOrCols = 0;
             const OUString sSearchRowOrCol = bRowMode ? OUString("</tr>") : 
OUString("<col ");
             while((nIdx = aExpand.indexOf(sSearchRowOrCol, nIdx)) > -1)
             {
                 // skip rows/columns of nested tables, based on HTML 
indentation
-                if ( nIdx > 3 && (aExpand[nIdx-1] != '\t' || aExpand[nIdx-2] 
!= '\t' ||
-                    ( bShifted && aExpand[nIdx-3] != '\t') ) &&
+                if ( lcl_getLevel(aExpand, nIdx) == nTableLevel + (bShifted ? 
2 : 1) &&
                     // skip also strange hidden empty rows <tr></tr>
                     !aExpand.match("<tr></tr>", nIdx - 4) )
                 {

Reply via email to