sw/qa/core/txtnode/data/floattable-anchor-split.docx |binary
 sw/qa/core/txtnode/txtnode.cxx                       |   36 +++++++++++++++++++
 sw/source/core/txtnode/ndtxt.cxx                     |   21 ++++++++++-
 3 files changed, 56 insertions(+), 1 deletion(-)

New commits:
commit 0746d13365139c356eb9d297a358c486bf47d6fb
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Dec 12 08:39:54 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Dec 12 09:56:10 2023 +0100

    sw floattable: fix split of anchor text in 2nd half of the paragraph
    
    If you go to the anchor text of the floating table and you press enter
    in the second half of the anchor text, you get a layout loop.
    
    The reason for this: an invariant around split text frames were
    violated, later resulting in a layout loop. The rule is that in case you
    have a split frame, then there can't be a frame for a new text node
    between the two split frames. So no new text frame for an other node
    after the master anchor of the fly; no new text frame for an other node
    before the follow anchor of the fly.
    
    Fix the problem by improving SwTextNode::SplitContentNode() to check if
    this is an anchor for a split fly: if so, always insert the new text
    frame after the follow anchor of the fly, which doesn't break the above
    invariant.
    
    The layout loop is fixed, but the text in the follow anchor of the fly
    still has a bad position, that still needs fixing. Also currently
    testSplitFlyMoveMaster enforces that split at the para start inserts a
    paragraph before the floating table, so leave that case unchanged for
    now.
    
    Change-Id: I77962a354e297d2e9957edcce9bf140f2c72fc6e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160608
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/core/txtnode/data/floattable-anchor-split.docx 
b/sw/qa/core/txtnode/data/floattable-anchor-split.docx
new file mode 100644
index 000000000000..a5dcdb28eb8c
Binary files /dev/null and 
b/sw/qa/core/txtnode/data/floattable-anchor-split.docx differ
diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx
index 9369a187c482..5b4023a41dc6 100644
--- a/sw/qa/core/txtnode/txtnode.cxx
+++ b/sw/qa/core/txtnode/txtnode.cxx
@@ -21,6 +21,7 @@
 #include <editeng/escapementitem.hxx>
 
 #include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
 #include <fmtanchr.hxx>
 #include <frameformats.hxx>
 #include <wrtsh.hxx>
@@ -37,6 +38,9 @@
 #include <frmmgr.hxx>
 #include <formatflysplit.hxx>
 #include <ftnidx.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
 
 /// Covers sw/source/core/txtnode/ fixes.
 class SwCoreTxtnodeTest : public SwModelTestBase
@@ -491,6 +495,38 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, 
testFlySplitFootnote)
     CPPUNIT_ASSERT(!pDoc->GetFootnoteIdxs().empty());
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testSplitFlyAnchorSplit)
+{
+    // Given a document with a 2 pages long floating table:
+    createSwDoc("floattable-anchor-split.docx");
+
+    // When splitting the "AB" anchor text into "A" (remains as anchor text) 
and "B" (new text node
+    // after it):
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    // Without the accompanying fix in place, this test would have failed with 
a layout loop.
+    pWrtShell->SplitNode();
+
+    // Then make sure the resulting layout is what we want:
+    SwDoc* pDoc = getSwDoc();
+    SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+    auto pPage1 = pLayout->Lower()->DynCastPageFrame();
+    CPPUNIT_ASSERT(pPage1);
+    // Page 1 has the master fly:
+    CPPUNIT_ASSERT(pPage1->GetSortedObjs());
+    auto pPage2 = pPage1->GetNext()->DynCastPageFrame();
+    CPPUNIT_ASSERT(pPage2);
+    // Page 1 has the follow fly:
+    CPPUNIT_ASSERT(pPage2->GetSortedObjs());
+    // Anchor text is now just "A":
+    auto pText1 = pPage2->FindFirstBodyContent()->DynCastTextFrame();
+    CPPUNIT_ASSERT_EQUAL(OUString("A"), pText1->GetText());
+    // New text frame is just "B":
+    auto pText2 = pText1->GetNext()->DynCastTextFrame();
+    CPPUNIT_ASSERT_EQUAL(OUString("B"), pText2->GetText());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 541e3fd5049f..8cce58df0960 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -83,6 +83,8 @@
 #include <fmtpdsc.hxx>
 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
 #include <svl/itemiter.hxx>
+#include <undobj.hxx>
+#include <formatflysplit.hxx>
 
 using namespace ::com::sun::star;
 
@@ -460,7 +462,24 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition 
& rPos,
         ResetAttr( RES_PARATR_LIST_LEVEL );
     }
 
-    if ( HasWriterListeners() && !m_Text.isEmpty() && (nTextLen / 2) < 
nSplitPos )
+    bool bSplitFly = false;
+    std::optional<std::vector<SwFrameFormat*>> oFlys = 
sw::GetFlysAnchoredAt(GetDoc(), GetIndex());
+    if (oFlys.has_value() && nSplitPos > 0)
+    {
+        // See if one of the flys is a split fly. If so, we need to keep
+        // the potentially split text frames unchanged and create a new
+        // text frame at the end.
+        for (const auto& rFly : *oFlys)
+        {
+            if (rFly->GetFlySplit().GetValue())
+            {
+                bSplitFly = true;
+                break;
+            }
+        }
+    }
+
+    if ( HasWriterListeners() && !m_Text.isEmpty() && ((nTextLen / 2) < 
nSplitPos || bSplitFly) )
     {
         // optimization for SplitNode: If a split is at the end of a node then
         // move the frames from the current to the new one and create new ones

Reply via email to