sw/qa/core/frmedt/frmedt.cxx     |   12 ++++++++++++
 sw/source/core/frmedt/fefly1.cxx |   22 ++++++++++++++++++++--
 2 files changed, 32 insertions(+), 2 deletions(-)

New commits:
commit 5601c0845caeb65db20dfceb927725906dce3f5c
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Nov 20 08:35:57 2023 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Wed Nov 22 10:35:56 2023 +0100

    sw floattable, delete UI: fix undo/redo
    
    Pressing the unfloat context menu item for a frame resulted in 2 undo
    actions and trying to undo both even crashed.
    
    One problem was that no SwNode was left in the TextFrame and then it was
    deleted; but undo created a TextFrame with an empty SwNode in it.
    
    Fix this by inserting an empty text node at the end of the TextFrame and
    only then moving content from it.
    
    The other trouble was that in case the new text node has no matching
    frame during MoveNodeRange(), then the fly frame will be deleted twice:
    once during MoveNodeRange() (because it has become empty) and once more
    during DelLayoutFormat(). This can be avoided by moving AppendTextNode()
    to its own layout action. Finally also group the (now) 3 undo actions
    together, because it was a single action on the UI.
    
    (cherry picked from commit 45a4ed02281a7a8ca52fccf626c792e417c8ef1c)
    
    Conflicts:
            sw/qa/core/frmedt/frmedt.cxx
    
    Change-Id: I95ce0abbab79a293ecb209579c105f96d284a1c9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159775
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/sw/qa/core/frmedt/frmedt.cxx b/sw/qa/core/frmedt/frmedt.cxx
index 9d6a60fce8eb..3ce234685777 100644
--- a/sw/qa/core/frmedt/frmedt.cxx
+++ b/sw/qa/core/frmedt/frmedt.cxx
@@ -28,6 +28,7 @@
 #include <formatflysplit.hxx>
 #include <itabenum.hxx>
 #include <frmmgr.hxx>
+#include <UndoManager.hxx>
 
 /// Covers sw/source/core/frmedt/ fixes.
 class SwCoreFrmedtTest : public SwModelTestBase
@@ -203,6 +204,8 @@ CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testSplitFlyUnfloat)
     // Given a document with a floating table:
     createSwDoc();
     SwDoc* pDoc = getSwDocShell()->GetDoc();
+    CPPUNIT_ASSERT(pDoc->GetUndoManager().IsUndoEnabled());
+    pDoc->GetUndoManager().EnableUndo(false);
     SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
     CPPUNIT_ASSERT(rFlyFormats.empty());
     // Insert a table:
@@ -225,6 +228,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testSplitFlyUnfloat)
     pWrtShell->EndAllAction();
     CPPUNIT_ASSERT(!rFlyFormats.empty());
     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
pDoc->GetTableFrameFormatCount(/*bUsed=*/true));
+    pDoc->GetUndoManager().EnableUndo(true);
 
     // When marking that frame and unfloating it:
     selectShape(1);
@@ -235,6 +239,14 @@ CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testSplitFlyUnfloat)
     // have failed, the frame was not removed.
     CPPUNIT_ASSERT(rFlyFormats.empty());
     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
pDoc->GetTableFrameFormatCount(/*bUsed=*/true));
+
+    // When undoing the conversion to inline:
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
pDoc->GetUndoManager().GetUndoActionCount());
+    pDoc->GetUndoManager().Undo();
+
+    // Then the undo stack had 2 undo actions and undo-all crashed.
+    CPPUNIT_ASSERT(!rFlyFormats.empty());
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
pDoc->GetTableFrameFormatCount(/*bUsed=*/true));
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/frmedt/fefly1.cxx b/sw/source/core/frmedt/fefly1.cxx
index 0d8efa725c2e..79486415c403 100644
--- a/sw/source/core/frmedt/fefly1.cxx
+++ b/sw/source/core/frmedt/fefly1.cxx
@@ -31,6 +31,7 @@
 #include <com/sun/star/embed/XEmbeddedObject.hpp>
 #include <comphelper/types.hxx>
 #include <osl/diagnose.h>
+#include <comphelper/scopeguard.hxx>
 #include <fmtanchr.hxx>
 #include <fmtcntnt.hxx>
 #include <fmtornt.hxx>
@@ -275,6 +276,10 @@ void SwFEShell::SelectFlyFrame( SwFlyFrame& rFrame )
 
 void SwFEShell::UnfloatFlyFrame()
 {
+    GetIDocumentUndoRedo().StartUndo(SwUndoId::DELLAYFMT, nullptr);
+    comphelper::ScopeGuard g([this]
+                             { 
GetIDocumentUndoRedo().EndUndo(SwUndoId::DELLAYFMT, nullptr); });
+
     SwFlyFrame* pFly = GetSelectedFlyFrame();
     if (!pFly)
     {
@@ -295,7 +300,21 @@ void SwFEShell::UnfloatFlyFrame()
         return;
     }
 
-    SwNodeRange aRange(pFlyStart->GetNode(), SwNodeOffset(1), *pFlyEnd, 
SwNodeOffset(0));
+    // Create an empty paragraph after the table, so the frame's SwNodes 
section is non-empty after
+    // MoveNodeRange(). Undo would ensure it's non-empty and then node offsets 
won't match.
+    IDocumentContentOperations& rIDCO = 
GetDoc()->getIDocumentContentOperations();
+    {
+        SwNodeIndex aInsertIndex(*pFlyEnd);
+        --aInsertIndex;
+        SwPosition aInsertPos(aInsertIndex);
+        StartAllAction();
+        rIDCO.AppendTextNode(aInsertPos);
+        // Make sure that a layout frame is created for the node, so the fly 
frame is not deleted,
+        // during MoveNodeRange(), either.
+        EndAllAction();
+    }
+
+    SwNodeRange aRange(pFlyStart->GetNode(), SwNodeOffset(1), *pFlyEnd, 
SwNodeOffset(-1));
     const SwFormatAnchor& rAnchor = rFlyFormat.GetAnchor();
     SwNode* pAnchor = rAnchor.GetAnchorNode();
     if (!pAnchor)
@@ -305,7 +324,6 @@ void SwFEShell::UnfloatFlyFrame()
 
     // Move the content outside of the text frame.
     SwNodeIndex aInsertPos(*pAnchor);
-    IDocumentContentOperations& rIDCO = 
rFlyFormat.GetDoc()->getIDocumentContentOperations();
     rIDCO.MoveNodeRange(aRange, aInsertPos.GetNode(), 
SwMoveFlags::CREATEUNDOOBJ);
 
     // Remove the fly frame frame.

Reply via email to