sw_redlinehide_2: JoinPrev() may need to move frames on deleted ...

... prev. Node to the second node, if the two were merged before the
Join.

Change-Id: I047b6008c5f0bb6e79c63421a4dba09ba8cf3320
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
index c8c66ee..0a01b1f 100644
--- a/sw/source/core/doc/docedt.cxx
+++ b/sw/source/core/doc/docedt.cxx
@@ -412,7 +412,13 @@ bool sw_JoinText( SwPaM& rPam, bool bJoinPrev )
                    rPam.GetBound( false ) = aAlphaPos;
            }
            // delete the Node, at last!
            SwNode::Merge const eOldMergeFlag(pOldTextNd->GetRedlineMergeFlag());
            if (eOldMergeFlag == SwNode::Merge::First)
            {
                sw::MoveDeletedPrevFrames(*pOldTextNd, *pTextNd);
            }
            pDoc->GetNodes().Delete( aOldIdx );
            sw::CheckResetRedlineMergeFlag(*pTextNd, eOldMergeFlag == SwNode::Merge::NonFirst);
        }
        else
        {
diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
index 028c360..10bde32 100644
--- a/sw/source/core/inc/txtfrm.hxx
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -105,6 +105,9 @@ TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
        bool isRealDelete,
        SwTextNode const& rNode, sal_Int32 nIndex, sal_Int32 nLen);

void MoveDeletedPrevFrames(SwTextNode & rDeletedPrev, SwTextNode & rNode);
void CheckResetRedlineMergeFlag(SwTextNode & rNode, bool bRecreateMerged);

} // namespace sw

/// Represents the visualization of a paragraph. Typical upper is an
@@ -726,7 +729,7 @@ public:

    static void repaintTextFrames( const SwTextNode& rNode );

    void RegisterToNode( SwTextNode& );
    void RegisterToNode(SwTextNode &, bool isForceNodeAsFirst = false);

    virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override;
};
diff --git a/sw/source/core/layout/ssfrm.cxx b/sw/source/core/layout/ssfrm.cxx
index 3a37ae9..99d187f 100644
--- a/sw/source/core/layout/ssfrm.cxx
+++ b/sw/source/core/layout/ssfrm.cxx
@@ -27,6 +27,7 @@
#include <fmtfsize.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/shaditem.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <fmtclds.hxx>
#include <viewimp.hxx>
#include <sortedobjs.hxx>
@@ -440,14 +441,20 @@ SwContentFrame::~SwContentFrame()
{
}

void SwTextFrame::RegisterToNode(SwTextNode & rNode)
void SwTextFrame::RegisterToNode(SwTextNode & rNode, bool const isForceNodeAsFirst)
{
    if (isForceNodeAsFirst && m_pMergedPara)
    {   // nothing registered here, in particular no redlines
        assert(m_pMergedPara->pFirstNode->GetIndex() + 1 == rNode.GetIndex());
        assert(rNode.GetDoc()->getIDocumentRedlineAccess().GetRedlinePos(
                *m_pMergedPara->pFirstNode, USHRT_MAX) == SwRedlineTable::npos);
    }
    assert(&rNode != GetDep());
    assert(!m_pMergedPara
        || (m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
        || (rNode.GetIndex() + 1 == m_pMergedPara->pFirstNode->GetIndex()));
    SwTextNode & rFirstNode(
        (m_pMergedPara && m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
        (!isForceNodeAsFirst && m_pMergedPara && m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
            ? *m_pMergedPara->pFirstNode
            : rNode);
    // sw_redlinehide: use New here, because the only caller also calls lcl_ChangeFootnoteRef
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 38f5eff..80a258f 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -826,8 +826,45 @@ void SwTextNode::MoveTextAttr_To_AttrSet()

}

namespace {
namespace sw {

/// if first node is deleted & second survives, then the first node's frame
/// will be deleted too; prevent this by moving the frame to the second node
/// if necessary.
void MoveDeletedPrevFrames(SwTextNode & rDeletedPrev, SwTextNode & rNode)
{
    std::vector<SwTextFrame*> frames;
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rDeletedPrev);
    for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
    {
        frames.push_back(pFrame);
    }
    {
        auto frames2(frames);
        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIt(rNode);
        for (SwTextFrame* pFrame = aIt.First(); pFrame; pFrame = aIt.Next())
        {
            auto const it(std::find(frames2.begin(), frames2.end(), pFrame));
            assert(it != frames2.end());
            frames2.erase(it);
        }
        assert(frames2.empty());
    }
    for (SwTextFrame *const pFrame : frames)
    {
        pFrame->RegisterToNode(rNode, true);
    }
}

// typical Join:
// None,Node->None
// None,First->First
// First,NonFirst->First
// NonFirst,First->NonFirst
// NonFirst,None->NonFirst

/// if first node is First, its frames may need to be moved, never deleted.
/// if first node is NonFirst, second node's own frames (First/None) must be deleted
void CheckResetRedlineMergeFlag(SwTextNode & rNode, bool const bRecreateMerged)
{
    if (bRecreateMerged)
@@ -846,6 +883,9 @@ void CheckResetRedlineMergeFlag(SwTextNode & rNode, bool const bRecreateMerged)
            assert(rFirstNode.GetIndex() <= rNode.GetIndex());
            pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
                        *pFrame, rFirstNode, sw::FrameMode::Existing));
            assert(pFrame->GetMergedPara());
            assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
            assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
        }
    }
    else if (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
@@ -1051,6 +1091,10 @@ void SwTextNode::JoinPrev()
            pDoc->CorrAbs( aIdx, SwPosition( *this ), nLen, true );
        }
        SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
        if (eOldMergeFlag == SwNode::Merge::First)
        {
            sw::MoveDeletedPrevFrames(*pTextNode, *this);
        }
        rNds.Delete(aIdx);
        SetWrong( pList, false );
        SetGrammarCheck( pList3, false );