tdf#135061 sw_redlinehide: create frames following hidden table

When pasting, the entire document body is covered by a delete redline.

The insert position is in the last node, following a table; table nodes
do hot have any connection to frames if they are hidden by redlines,
so MakeFrames() won't create frames for the inserted nodes.

Refactor SwNodes::FindPrvNxtFrameNode() so that it can detect that table
nodes are hidden in the current layout and continue to search in this
case.

For multiple layouts, MakeFrames() may need to use different nodes per
layout, as the "nearest" one in one layout may be hidden in another.

Change-Id: I3bb2c861c861438ac2695ab49dd91dc2bde87db4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127272
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/sw/inc/ndarr.hxx b/sw/inc/ndarr.hxx
index 28c96cc..87c78c5 100644
--- a/sw/inc/ndarr.hxx
+++ b/sw/inc/ndarr.hxx
@@ -45,6 +45,7 @@ class SwNodeIndex;
class SwNodeRange;
class SwOLENode;
class SwPaM;
class SwRootFrame;
class SwSectionData;
class SwSectionFormat;
class SwTOXBase;
@@ -305,7 +306,8 @@ public:
     forward after pEnd.
     If no valid node is found, return 0. rFrameIdx points to the node with frames. **/
    SwNode* FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
                                const SwNode* pEnd ) const;
                                const SwNode* pEnd,
                                SwRootFrame const* pLayout = nullptr) const;

    SwNode * DocumentSectionStartNode(SwNode * pNode) const;
    SwNode * DocumentSectionEndNode(SwNode * pNode) const;
diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx
index 869be577..d352cfb 100644
--- a/sw/source/core/docnode/nodes.cxx
+++ b/sw/source/core/docnode/nodes.cxx
@@ -43,6 +43,7 @@
#include <fmtftn.hxx>

#include <docsh.hxx>
#include <rootfrm.hxx>
#include <txtfrm.hxx>

typedef std::vector<SwStartNode*> SwStartNodePointers;
@@ -2035,7 +2036,8 @@ SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx,
 * @return result node; 0 if not found
 */
SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
                                    const SwNode* pEnd ) const
        SwNode const*const pEnd,
        SwRootFrame const*const pLayout) const
{
    assert(pEnd != nullptr); // every caller currently

@@ -2058,53 +2060,65 @@ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
                    ? pSttNd->StartOfSectionNode()->FindTableNode()
                    : pSttNd->FindTableNode();
            SwNodeIndex aIdx( rFrameIdx );

            // search backward for a content or table node

            --aIdx;
            SwNode *const pNd = &aIdx.GetNode();
            pFrameNd = &aIdx.GetNode();

            pFrameNd = pNd;
            if (pFrameNd->IsContentNode())
            do
            {
                rFrameIdx = aIdx;
                return pFrameNd;
            }
            // search forward or backward for a content node
            pFrameNd = GoPrevSection( &aIdx, true, false );
            if ( nullptr != pFrameNd &&
                    ::CheckNodesRange( aIdx, rFrameIdx, true ) &&
                    // Never out of the table at the start
                    pFrameNd->FindTableNode() == pTableNd &&
                    // Bug 37652: Never out of the table at the end
                    (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
                        == pSttNd->FindTableBoxStartNode() ) &&
                     (!pSectNd || pSttNd->IsSectionNode() ||
                      pSectNd->GetIndex() < pFrameNd->GetIndex())
                    )
            {
                rFrameIdx = aIdx;
            }
            else
            {
                aIdx = pEnd->GetIndex() + 1;

                pFrameNd = &aIdx.GetNode();
                if (!pFrameNd->IsContentNode())
                if (pFrameNd->IsContentNode())
                {
                    pFrameNd = GoNextSection( &aIdx, true, false );
                    // NEVER leave the section when doing this!
                    if (pFrameNd
                        && !(::CheckNodesRange(aIdx, rFrameIdx, true)
                             && (pFrameNd->FindTableNode() == pTableNd &&
                                // NEVER go out of the table cell at the end
                                (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
                                    == pSttNd->FindTableBoxStartNode()))
                             && (!pSectNd || pSttNd->IsSectionNode() ||
                               pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()))
                        )
                    // TODO why does this not check for nested tables like forward direction
                    rFrameIdx = aIdx;
                    return pFrameNd;
                }
                else if (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsTableNode())
                {
                    if (pLayout == nullptr
                        || !pLayout->HasMergedParas()
                        || pFrameNd->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
                    {
                        pFrameNd = nullptr;
                        pFrameNd = pFrameNd->StartOfSectionNode();
                        rFrameIdx = *pFrameNd;
                        return pFrameNd;
                    }
                    else
                    {
                        aIdx = *pFrameNd->StartOfSectionNode();
                        --aIdx;
                        pFrameNd = &aIdx.GetNode();
                    }
                }
                if (pFrameNd && pFrameNd->IsContentNode())
                else
                {
                    pFrameNd = GoPrevSection( &aIdx, true, false );
                    if ( nullptr != pFrameNd && !(
                            ::CheckNodesRange( aIdx, rFrameIdx, true ) &&
                            // Never out of the table at the start
                            pFrameNd->FindTableNode() == pTableNd &&
                            // Bug 37652: Never out of the table at the end
                            (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
                                == pSttNd->FindTableBoxStartNode() ) &&
                             (!pSectNd || pSttNd->IsSectionNode() ||
                              pSectNd->GetIndex() < pFrameNd->GetIndex())
                            ))
                    {
                        pFrameNd = nullptr; // no preceding content node, stop search
                    }
                }
            }
            while (pFrameNd != nullptr);

            // search forward for a content or table node

            aIdx = pEnd->GetIndex() + 1;
            pFrameNd = &aIdx.GetNode();

            do
            {
                if (pFrameNd->IsContentNode())
                {
                    // Undo when merging a table with one before, if there is also one after it.
                    // However, if the node is in a table, it needs to be returned if the
@@ -2119,24 +2133,55 @@ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
                        rFrameIdx = *pFrameNd;
                    }
                    else
                        rFrameIdx = aIdx;
                }
                else if( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
                {
                    pFrameNd = pNd->StartOfSectionNode();
                    rFrameIdx = *pFrameNd;
                }
                else
                {
                    aIdx = pEnd->GetIndex() + 1;

                    pFrameNd = &aIdx.GetNode();
                    if (pFrameNd->IsTableNode())
                    {
                        rFrameIdx = aIdx;
                    }
                    return pFrameNd;
                }
                else if (pFrameNd->IsTableNode())
                {
                    if (pLayout == nullptr
                        || !pLayout->HasMergedParas()
                        || pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
                    {
                        rFrameIdx = *pFrameNd;
                        return pFrameNd;
                    }
                    else
                    {
                        aIdx = *pFrameNd->EndOfSectionNode();
                        ++aIdx;
                        pFrameNd = &aIdx.GetNode();
                    }
                }
                else
                {
                    pFrameNd = GoNextSection( &aIdx, true, false );
                    // NEVER leave the section when doing this!
                    if (pFrameNd
                        && !(::CheckNodesRange(aIdx, rFrameIdx, true)
                             && (pFrameNd->FindTableNode() == pTableNd &&
                                // NEVER go out of the table cell at the end
                                (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
                                    == pSttNd->FindTableBoxStartNode()))
                             && (!pSectNd || pSttNd->IsSectionNode() ||
                               pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()))
                        )
                    {
                        pFrameNd = nullptr; // no following content node, stop search
                    }
                }
            }
            while (pFrameNd != nullptr);

            // probably this is dead code, because the GoNextSection()
            // should have ended up in the first text node in the table and
            // then checked it's in a table?
            {
                    aIdx = pEnd->GetIndex() + 1;

                    pFrameNd = &aIdx.GetNode();
                    {
                        pFrameNd = nullptr;

                        // is there some sectionnodes before a tablenode?
@@ -2153,9 +2198,9 @@ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
                        {
                            rFrameIdx = aIdx;
                            pFrameNd = &aIdx.GetNode();
                            assert(!"this isn't dead code?");
                        }
                    }
                }
            }
        }
    }
diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
index 3a62692..79fbaa2 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -70,6 +70,7 @@
#include <undobj.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentTimerAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
@@ -2006,8 +2007,10 @@ void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx,

    SwNodeIndex aTmp( rSttIdx );
    SwNodeOffset nEndIdx = rEndIdx.GetIndex();
    // TODO for multiple layouts there should be a loop here
    SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( aTmp,
                                            pDoc->GetNodes()[ nEndIdx-1 ]);
                    pDoc->GetNodes()[ nEndIdx-1 ],
                    pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
    if ( pNd )
    {
        bool bApres = aTmp < rSttIdx;