sw/inc/IDocumentMarkAccess.hxx                          |    3 
 sw/qa/extras/uiwriter/uiwriter.cxx                      |  146 ++++++++++++++++
 sw/source/core/doc/DocumentContentOperationsManager.cxx |    3 
 sw/source/core/doc/docbm.cxx                            |   12 -
 sw/source/core/inc/MarkManager.hxx                      |    3 
 sw/source/core/inc/mvsave.hxx                           |    3 
 sw/source/core/undo/rolbck.cxx                          |   50 ++---
 sw/source/core/undo/undel.cxx                           |   26 ++
 sw/source/core/undo/undobj.cxx                          |   16 +
 9 files changed, 220 insertions(+), 42 deletions(-)

New commits:
commit d272ab078b401bf4d70919680f2aa18ddbb15c6a
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Thu Nov 23 20:45:09 2023 +0100
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Nov 30 13:09:43 2023 +0100

    tdf#148389 sw: fix Delete Undo/Redo of bookmark positions
    
    The main problem is that in SwUndoSaveContent::DelContentIndex() if the
    selection start/end is equal to the bookmark start/end, the bookmark is
    not deleted and no SwHistoryBookmark is created, hence on Undo the
    bookmark positions are not restored.
    
    Since deleting bookmarks in more situations might cause user complaints,
    let's just extend the creation of SwHistoryBookmark to these cases,
    which means we need to take care both here and in
    SwHistoryBookmark::SetInDoc() that there is now a situation where all
    bookmark positions are saved and restored but the bookmark still exists
    in the document because it wasn't deleted.
    
    The next problem is that using Backspace/Delete keys sets the
    ArtificialSelection flag which is stored in SwUndoDelete, but when used
    multiple times the SwUndoDelete::CanGrouping() extends an existing
    SwUndoDelete, and if it previously would not delete a bookmark, the
    extended range might fully contain the bookmark and thus delete it on
    Redo, so check if there are saved bookmark positions and prevent
    grouping in that case.
    
    Another problem is then that SwUndoDelete::RedoImpl() deletes the
    bookmark anyway, as already indicated with a FIXME comment.
    
    This can be prevented by passing the now-existing m_DeleteFlags into
    DelBookmarks() from DeleteRangeImplImpl().
    
    Change-Id: Id5eb1a58927aaa6e7e8b75be82d7f854d8057cfc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159875
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    (cherry picked from commit 57974af130e7421da6b07589d4a63a754b757ad6)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159894
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/sw/inc/IDocumentMarkAccess.hxx b/sw/inc/IDocumentMarkAccess.hxx
index d53f180d3fe9..d63b58f606c4 100644
--- a/sw/inc/IDocumentMarkAccess.hxx
+++ b/sw/inc/IDocumentMarkAccess.hxx
@@ -228,7 +228,8 @@ class IDocumentMarkAccess
             const SwNode& rEnd,
             std::vector< ::sw::mark::SaveBookmark>* pSaveBkmk, // Ugly: 
SaveBookmark is core-internal
             std::optional<sal_Int32> oStartContentIdx,
-            std::optional<sal_Int32> oEndContentIdx) =0;
+            std::optional<sal_Int32> oEndContentIdx,
+            bool isReplace) = 0;
 
         /** Deletes a mark.
 
diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx 
b/sw/qa/extras/uiwriter/uiwriter.cxx
index 58ebbcde09ff..00719b072416 100644
--- a/sw/qa/extras/uiwriter/uiwriter.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter.cxx
@@ -1435,6 +1435,152 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testBookmarkUndo)
     CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount());
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf148389_Left)
+{
+    createSwDoc();
+    SwDoc* pDoc = getSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    CPPUNIT_ASSERT(pWrtShell);
+    pWrtShell->Insert("foo bar baz");
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, 
/*bBasicCall=*/false);
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 3, 
/*bBasicCall=*/false);
+    IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+    auto pMark = pMarkAccess->makeMark(*pWrtShell->GetCursor(), "Mark",
+        IDocumentMarkAccess::MarkType::BOOKMARK, ::sw::mark::InsertMode::New);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, 
/*bBasicCall=*/false);
+    pWrtShell->DelLeft();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(7), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->DelLeft();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(6), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->DelLeft();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->DelLeft();
+    // historically it wasn't deleted if empty, not sure if it should be
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    // the problem was that the end position was not restored
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    // this undo is no longer grouped, to prevent Redo deleting bookmark
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(7), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Redo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Redo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(7), 
pMark->GetOtherMarkPos().GetContentIndex());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf148389_Right)
+{
+    createSwDoc();
+    SwDoc* pDoc = getSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    CPPUNIT_ASSERT(pWrtShell);
+    pWrtShell->Insert("foo bar baz");
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, 
/*bBasicCall=*/false);
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 3, 
/*bBasicCall=*/false);
+    IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+    auto pMark = pMarkAccess->makeMark(*pWrtShell->GetCursor(), "Mark",
+        IDocumentMarkAccess::MarkType::BOOKMARK, ::sw::mark::InsertMode::New);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, 
/*bBasicCall=*/false);
+    pWrtShell->DelRight();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(6), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->DelRight();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->DelRight();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->DelRight();
+    // historically it wasn't deleted if empty, not sure if it should be
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    // the problem was that the start position was not restored
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    // this undo is no longer grouped, to prevent Redo deleting bookmark
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(6), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    // this undo is no longer grouped, to prevent Redo deleting bookmark
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(7), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Redo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(6), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Redo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Redo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(6), 
pMark->GetOtherMarkPos().GetContentIndex());
+    pWrtShell->Undo();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount());
+    // Undo re-creates the mark...
+    pMark = *pMarkAccess->getAllMarksBegin();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMark->GetMarkPos().GetContentIndex());
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(7), 
pMark->GetOtherMarkPos().GetContentIndex());
+}
+
 static void lcl_setWeight(SwWrtShell* pWrtShell, FontWeight aWeight)
 {
     SvxWeightItem aWeightItem(aWeight, EE_CHAR_WEIGHT);
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx 
b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index bac73a325a5f..81c8c14e1a4b 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -4433,7 +4433,8 @@ bool 
DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam, SwDelet
         pEnd->GetNode(),
         nullptr,
         pStt->GetContentIndex(),
-        pEnd->GetContentIndex());
+        pEnd->GetContentIndex(),
+        bool(flags & SwDeleteFlags::ArtificialSelection));
 
     SwNodeIndex aSttIdx( pStt->GetNode() );
     SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx
index 2d8a411a9c5d..bb8e75969239 100644
--- a/sw/source/core/doc/docbm.cxx
+++ b/sw/source/core/doc/docbm.cxx
@@ -1097,7 +1097,8 @@ namespace sw::mark
             const SwNode& rEnd,
             std::vector<SaveBookmark>* pSaveBkmk,
             std::optional<sal_Int32> oStartContentIdx,
-            std::optional<sal_Int32> oEndContentIdx )
+            std::optional<sal_Int32> oEndContentIdx,
+            bool const isReplace)
     {
         std::vector<const_iterator_t> vMarksToDelete;
         bool bIsSortingNeeded = false;
@@ -1116,7 +1117,8 @@ namespace sw::mark
             ::sw::mark::MarkBase *const pMark = *ppMark;
             bool bIsPosInRange(false);
             bool bIsOtherPosInRange(false);
-            bool const bDeleteMark = isDeleteMark(pMark, false, rStt, rEnd, 
oStartContentIdx, oEndContentIdx, bIsPosInRange, bIsOtherPosInRange);
+            bool const bDeleteMark = isDeleteMark(pMark, isReplace, rStt, rEnd,
+                oStartContentIdx, oEndContentIdx, bIsPosInRange, 
bIsOtherPosInRange);
 
             if ( bIsPosInRange
                  && ( bIsOtherPosInRange
@@ -2013,7 +2015,8 @@ void DelBookmarks(
     const SwNode& rEnd,
     std::vector<SaveBookmark> * pSaveBkmk,
     std::optional<sal_Int32> oStartContentIdx,
-    std::optional<sal_Int32> oEndContentIdx)
+    std::optional<sal_Int32> oEndContentIdx,
+    bool const isReplace)
 {
     // illegal range ??
     if(rStt.GetIndex() > rEnd.GetIndex()
@@ -2023,7 +2026,8 @@ void DelBookmarks(
 
     rDoc.getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk,
         oStartContentIdx,
-        oEndContentIdx);
+        oEndContentIdx,
+        isReplace);
 
     // Copy all Redlines which are in the move area into an array
     // which holds all position information as offset.
diff --git a/sw/source/core/inc/MarkManager.hxx 
b/sw/source/core/inc/MarkManager.hxx
index 7e9280f12aaa..ef0e79d74c11 100644
--- a/sw/source/core/inc/MarkManager.hxx
+++ b/sw/source/core/inc/MarkManager.hxx
@@ -67,7 +67,8 @@ namespace sw::mark {
                                     const SwNode& rEnd,
                                     std::vector< ::sw::mark::SaveBookmark>* 
pSaveBkmk,
                                     std::optional<sal_Int32> oStartContentIdx,
-                                    std::optional<sal_Int32> oEndContentIdx) 
override;
+                                    std::optional<sal_Int32> oEndContentIdx,
+                                    bool isReplace) override;
 
             // deleters
             virtual std::unique_ptr<ILazyDeleter>
diff --git a/sw/source/core/inc/mvsave.hxx b/sw/source/core/inc/mvsave.hxx
index 024276b78ccd..f36dee9a26cf 100644
--- a/sw/source/core/inc/mvsave.hxx
+++ b/sw/source/core/inc/mvsave.hxx
@@ -95,7 +95,8 @@ void DelBookmarks(SwNode& rStt,
     const SwNode& rEnd,
     std::vector< ::sw::mark::SaveBookmark> * SaveBkmk =nullptr,
     std::optional<sal_Int32> oStartContentIdx = std::nullopt,
-    std::optional<sal_Int32> oEndContentIdx = std::nullopt);
+    std::optional<sal_Int32> oEndContentIdx = std::nullopt,
+    bool isReplace = false);
 
 /** data structure to temporarily hold fly anchor positions relative to some
  *  location. */
diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx
index d17c08e4795b..230673f21597 100644
--- a/sw/source/core/undo/rolbck.cxx
+++ b/sw/source/core/undo/rolbck.cxx
@@ -645,60 +645,50 @@ void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool )
 
     SwNodes& rNds = pDoc->GetNodes();
     IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
-    std::optional<SwPaM> pPam;
+    std::optional<SwPaM> oPam;
     ::sw::mark::IMark* pMark = nullptr;
 
+    // now the situation is that m_bSavePos and m_bSaveOtherPos don't determine
+    // whether the mark was deleted
+    if (auto const iter = pMarkAccess->findMark(m_aName); iter != 
pMarkAccess->getAllMarksEnd())
+    {
+        pMark = *iter;
+    }
     if(m_bSavePos)
     {
         SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode();
-        OSL_ENSURE(pContentNd,
-            "<SwHistoryBookmark::SetInDoc(..)>"
-            " - wrong node for a mark");
-
-        // #111660# don't crash when nNode1 doesn't point to content node.
-        if(pContentNd)
-            pPam.emplace(*pContentNd, m_nContent);
+        assert(pContentNd);
+        oPam.emplace(*pContentNd, m_nContent);
     }
     else
     {
-        pMark = *pMarkAccess->findMark(m_aName);
-        pPam.emplace(pMark->GetMarkPos());
+        assert(pMark);
+        oPam.emplace(pMark->GetMarkPos());
     }
+    assert(oPam);
 
     if(m_bSaveOtherPos)
     {
         SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode();
-        OSL_ENSURE(pContentNd,
-            "<SwHistoryBookmark::SetInDoc(..)>"
-            " - wrong node for a mark");
-
-        if (pPam && pContentNd)
-        {
-            pPam->SetMark();
-            pPam->GetMark()->Assign(*pContentNd, m_nOtherContent);
-        }
+        assert(pContentNd);
+        oPam->SetMark();
+        oPam->GetMark()->Assign(*pContentNd, m_nOtherContent);
     }
     else if(m_bHadOtherPos)
     {
-        if(!pMark)
-            pMark = *pMarkAccess->findMark(m_aName);
-        OSL_ENSURE(pMark->IsExpanded(),
-            "<SwHistoryBookmark::SetInDoc(..)>"
-            " - missing pos on old mark");
-        pPam->SetMark();
-        *pPam->GetMark() = pMark->GetOtherMarkPos();
+        assert(pMark);
+        assert(pMark->IsExpanded());
+        oPam->SetMark();
+        *oPam->GetMark() = pMark->GetOtherMarkPos();
     }
 
-    if (!pPam)
-        return;
-
     if ( pMark != nullptr )
     {
         pMarkAccess->deleteMark( pMark );
     }
     ::sw::mark::IBookmark* const pBookmark =
         dynamic_cast<::sw::mark::IBookmark*>(
-            pMarkAccess->makeMark(*pPam, m_aName, m_eBkmkType, 
sw::mark::InsertMode::New));
+            pMarkAccess->makeMark(*oPam, m_aName, m_eBkmkType, 
sw::mark::InsertMode::New));
     if ( pBookmark == nullptr )
         return;
 
diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx
index cd1b3b1d9182..52efad11b69f 100644
--- a/sw/source/core/undo/undel.cxx
+++ b/sw/source/core/undo/undel.cxx
@@ -539,6 +539,7 @@ bool SwUndoDelete::CanGrouping( SwDoc& rDoc, const SwPaM& 
rDelPam )
         if( m_bGroup && !m_bBackSp ) return false;
         m_bBackSp = true;
     }
+    // note: compare m_nSttContent here because the text isn't there any more!
     else if( pStt->GetContentIndex() == m_nSttContent )
     {
         if( m_bGroup && m_bBackSp ) return false;
@@ -567,6 +568,30 @@ bool SwUndoDelete::CanGrouping( SwDoc& rDoc, const SwPaM& 
rDelPam )
         return false;
     }
 
+    if ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) && m_pHistory)
+    {
+        IDocumentMarkAccess const& rIDMA(*rDoc.getIDocumentMarkAccess());
+        for (auto i = m_pHistory->Count(); 0 < i; )
+        {
+            --i;
+            SwHistoryHint const*const pHistory((*m_pHistory)[i]);
+            if (pHistory->Which() == HSTRY_BOOKMARK)
+            {
+                SwHistoryBookmark const*const pHistoryBM(
+                        static_cast<SwHistoryBookmark const*>(pHistory));
+                auto const ppMark(rIDMA.findMark(pHistoryBM->GetName()));
+                if (ppMark != rIDMA.getAllMarksEnd()
+                    && (m_bBackSp
+                            ? ((**ppMark).GetMarkPos() == *pStt)
+                            : ((**ppMark).IsExpanded()
+                                && (**ppMark).GetOtherMarkPos() == *pEnd)))
+                {   // prevent grouping that would delete this mark on Redo()
+                    return false;
+                }
+            }
+        }
+    }
+
     {
         SwRedlineSaveDatas aTmpSav;
         const bool bSaved = FillSaveData( rDelPam, aTmpSav, false );
@@ -1294,7 +1319,6 @@ void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & 
rContext)
         rDoc.getIDocumentContentOperations().DelFullPara( rPam );
     }
     else
-        // FIXME: this ends up calling DeleteBookmarks() on the entire rPam 
which deletes too many!
         rDoc.getIDocumentContentOperations().DeleteAndJoin(rPam, 
m_DeleteFlags);
 }
 
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index dce7f3eb2724..d7f4795c95dd 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -1123,6 +1123,7 @@ void SwUndoSaveContent::DelContentIndex( const 
SwPosition& rMark,
         // #i81002#
         bool bSavePos = false;
         bool bSaveOtherPos = false;
+        bool bDelete = false;
         const ::sw::mark::IMark *const pBkmk = 
pMarkAccess->getAllMarksBegin()[n];
         auto const type(IDocumentMarkAccess::GetType(*pBkmk));
 
@@ -1139,6 +1140,7 @@ void SwUndoSaveContent::DelContentIndex( const 
SwPosition& rMark,
             {
                 bSaveOtherPos = true;
             }
+            bDelete = bSavePos && bSaveOtherPos;
         }
         else
         {
@@ -1179,8 +1181,16 @@ void SwUndoSaveContent::DelContentIndex( const 
SwPosition& rMark,
                 {
                     if( bMaybe )
                         bSavePos = true;
-                    bSaveOtherPos = true;
+                    bDelete = true;
                 }
+                if (bDelete || pBkmk->GetOtherMarkPos() == *pEnd)
+                {
+                    bSaveOtherPos = true; // tdf#148389 always undo if at end
+                }
+            }
+            if (!bSavePos && bMaybe && pBkmk->IsExpanded() && *pStt == 
pBkmk->GetMarkPos())
+            {
+                bSavePos = true; // tdf#148389 always undo if at start
             }
 
             if ( !bSavePos && !bSaveOtherPos
@@ -1219,6 +1229,7 @@ void SwUndoSaveContent::DelContentIndex( const 
SwPosition& rMark,
                 {
                     bSavePos = true;
                     bSaveOtherPos = pBkmk->IsExpanded(); //tdf#90138, only 
save the other pos if there is one
+                    bDelete = true;
                 }
             }
         }
@@ -1232,8 +1243,7 @@ void SwUndoSaveContent::DelContentIndex( const 
SwPosition& rMark,
                 m_pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos );
             }
             if ( bSavePos
-                 && ( bSaveOtherPos
-                      || !pBkmk->IsExpanded() ) )
+                 && (bDelete || !pBkmk->IsExpanded()))
             {
                 pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n, 
false);
                 n--;

Reply via email to