sw/qa/uibase/wrtsh/wrtsh.cxx      |   67 ++++++++++++++++++++++++++++++++++++++
 sw/source/core/inc/txtfrm.hxx     |    2 +
 sw/source/core/text/itratr.cxx    |    5 ++
 sw/source/uibase/wrtsh/delete.cxx |   23 +++++++++++++
 4 files changed, 97 insertions(+)

New commits:
commit 7715ba6e5a3f878d6fe2aa5b15a236ade8fd07e4
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Sep 3 08:17:23 2024 +0200
Commit:     Aron Budea <[email protected]>
CommitDate: Tue Jan 21 15:35:35 2025 +0100

    tdf#162507 sw floattable: fix not wanted join of two different split anchors
    
    Open the bugdoc, go to the end of the "Cannot press Delete here"
    paragraph, press delete, layout hangs.
    
    The trouble seems to be that this creates a doc model where multiple
    multi-page floating tables are anchored to the same paragraph, which is
    not something the layout supports. We explicitly avoid this at import
    time from DOCX since commit 01ad8ec4bb5425446e95dbada81de435646824b4 (sw
    floattable: fix lost tables around a floating table from DOCX,
    2023-06-05), but there was no mechanism to prevent the UI creating the
    same unsupported doc model.
    
    Fix the problem by extending SwWrtShell::DelRight() to prevent joining 2
    paragraphs in the unwanted case, interesting Word doesn't allow deleting
    at the end of that paragraph, either.
    
    Regression from commit 693ad3aadbf84afa750af73c330fe7e09b38a2e7
    (tdf#120262 sw floattable, legacy: go outside body only for page frame
    vert pos, 2023-07-31), which restricted the amount of cases where some
    weird legacy behavior was performed, so good to keep that side as-is.
    
    Conflicts:
          sw/qa/uibase/wrtsh/wrtsh.cxx
    
    Change-Id: I4d8a6702d8ac1689690fa464014c99fcd5ef7bd2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172780
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 5b0256f30ee154edb28b999bc4faba2453fc32b8)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172762
    Reviewed-by: Adolfo Jayme Barrientos <[email protected]>
    (cherry picked from commit 5d8ca53e218ec96f29d66e483541b7304da169cd)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180407
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Aron Budea <[email protected]>

diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx
index 125a80584026..1daf9c9ebd63 100644
--- a/sw/qa/uibase/wrtsh/wrtsh.cxx
+++ b/sw/qa/uibase/wrtsh/wrtsh.cxx
@@ -26,6 +26,9 @@
 #include <ndtxt.hxx>
 #include <textcontentcontrol.hxx>
 #include <fmtanchr.hxx>
+#include <itabenum.hxx>
+#include <frmmgr.hxx>
+#include <formatflysplit.hxx>
 
 namespace
 {
@@ -430,6 +433,70 @@ CPPUNIT_TEST_FIXTURE(Test, 
testInsertComboBoxContentControl)
     std::shared_ptr<SwContentControl> pContentControl = 
rFormatContentControl.GetContentControl();
     CPPUNIT_ASSERT(pContentControl->GetComboBox());
 }
+
+void InsertSplitFly(SwWrtShell* pWrtShell)
+{
+    SwPosition aInsertPos = *pWrtShell->GetCursor()->GetPoint();
+    // Insert a table:
+    SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
+    pWrtShell->InsertTable(aTableOptions, /*nRows=*/2, /*nCols=*/1);
+    pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+    pWrtShell->GoPrevCell();
+    pWrtShell->Insert(u"A1"_ustr);
+    // Select cell:
+    pWrtShell->SelAll();
+    // Select table:
+    pWrtShell->SelAll();
+    // Wrap the table in a text frame:
+    SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
+    pWrtShell->StartAllAction();
+    aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
+    pWrtShell->EndAllAction();
+    // Set fly properties:
+    pWrtShell->StartAllAction();
+    SwFrameFormat* pFly = pWrtShell->GetFlyFrameFormat();
+    SwAttrSet aSet(pFly->GetAttrSet());
+    aSet.Put(SwFormatFlySplit(true));
+    SwFormatAnchor aAnchor(RndStdIds::FLY_AT_PARA);
+    aAnchor.SetAnchor(&aInsertPos);
+    aSet.Put(aAnchor);
+    SwDoc* pDoc = pWrtShell->GetDoc();
+    pDoc->SetAttr(aSet, *pFly);
+    pWrtShell->EndAllAction();
+    pWrtShell->EnterStdMode();
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlysAnchorJoin)
+{
+    // Given a document with two paragraphs, each serving as an anchor of a 
split fly:
+    createSwDoc();
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->Insert(u"first para"_ustr);
+    pWrtShell->SplitNode();
+    pWrtShell->Insert(u"second para"_ustr);
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    InsertSplitFly(pWrtShell);
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    pWrtShell->SttPara();
+    InsertSplitFly(pWrtShell);
+
+    // When trying to delete at the end of the first para:
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    pWrtShell->EndPara();
+    pWrtShell->DelRight();
+
+    // Then make sure the join doesn't happen till a text node can only be an 
anchor for one split
+    // fly:
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    SwCursor* pCursor = pWrtShell->GetCursor();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: first para
+    // - Actual  : first parasecond para
+    // i.e. we did join the 2 anchors and for complex enough documents the 
layout never finished.
+    CPPUNIT_ASSERT_EQUAL(u"first para"_ustr, 
pCursor->GetPointNode().GetTextNode()->GetText());
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    CPPUNIT_ASSERT_EQUAL(u"second para"_ustr, 
pCursor->GetPointNode().GetTextNode()->GetText());
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
index 6e1c75c6a168..38def52dd4bf 100644
--- a/sw/source/core/inc/txtfrm.hxx
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -807,6 +807,8 @@ public:
     /// with a negative vertical offset. Such frames may need explicit 
splitting.
     bool IsEmptyWithSplitFly() const;
 
+    bool HasSplitFlyDrawObjs() const;
+
     static SwView* GetView();
 
     void dumpAsXml(xmlTextWriterPtr writer = nullptr) const override;
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index e29fe5ebe916..cf8871a46615 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -1481,6 +1481,11 @@ std::vector<SwFlyAtContentFrame*> 
SwTextFrame::GetSplitFlyDrawObjs() const
     return aObjs;
 }
 
+bool SwTextFrame::HasSplitFlyDrawObjs() const
+{
+    return !GetSplitFlyDrawObjs().empty();
+}
+
 SwFlyAtContentFrame* SwTextFrame::HasNonLastSplitFlyDrawObj() const
 {
     const SwTextFrame* pFollow = GetFollow();
diff --git a/sw/source/uibase/wrtsh/delete.cxx 
b/sw/source/uibase/wrtsh/delete.cxx
index 46b0a40cca5d..5e13bd799817 100644
--- a/sw/source/uibase/wrtsh/delete.cxx
+++ b/sw/source/uibase/wrtsh/delete.cxx
@@ -36,6 +36,8 @@
 #include <osl/diagnose.h>
 #include <doc.hxx>
 #include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <txtfrm.hxx>
 
 inline void SwWrtShell::OpenMark()
 {
@@ -336,6 +338,7 @@ bool SwWrtShell::DelRight(bool const isReplaceHeuristic)
             // #108049# Save the startnode of the current cell
             const SwStartNode* pSNdOld = pWasInTableNd ?
                 GetCursor()->GetPointNode().FindTableBoxStartNode() : nullptr;
+            SwTextNode* pOldTextNode = 
GetCursor()->GetPointNode().GetTextNode();
             bool bCheckDelFull = SelectionType::Text & nSelection && 
SwCursorShell::IsSttPara();
             bool bDelFull = false;
             bool bDoNothing = false;
@@ -365,6 +368,7 @@ bool SwWrtShell::DelRight(bool const isReplaceHeuristic)
             }
 
             // restore cursor
+            SwTextNode* pNewTextNode = 
GetCursor()->GetPointNode().GetTextNode();
             SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent);
 
             if (bDelFull)
@@ -372,6 +376,25 @@ bool SwWrtShell::DelRight(bool const isReplaceHeuristic)
                 DelFullPara();
                 UpdateAttr();
             }
+
+            if (pOldTextNode && pNewTextNode && pNewTextNode != pOldTextNode)
+            {
+                SwRootFrame* pLayout = 
mxDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+                if (pLayout)
+                {
+                    auto pOldFrame = 
static_cast<SwTextFrame*>(pOldTextNode->getLayoutFrame(pLayout));
+                    auto pNewFrame = 
static_cast<SwTextFrame*>(pNewTextNode->getLayoutFrame(pLayout));
+                    if (pOldFrame && pNewFrame && 
pOldFrame->HasSplitFlyDrawObjs() && pNewFrame->HasSplitFlyDrawObjs())
+                    {
+                        // We have a selection where both the old and the new 
position is an anchor
+                        // for a potentially split fly. Don't allow join of 
the nodes in this case,
+                        // since the layout supports multiple anchors for one 
split fly, but it
+                        // doesn't support the usage of the same anchor for 
multiple split flys.
+                        bDoNothing = true;
+                    }
+                }
+            }
+
             if (bDelFull || bDoNothing)
                 break;
         }

Reply via email to