tdf#160984 sw continuous endnotes: switch to a section-based layout

The original layout added in commit
4814e8caa5f06c4fe438dfd7d7315e4a2410ea18 (tdf#124601 sw: add
ContinuousEndnotes layout compat option, 2019-09-30) puts endnotes to
the footnote container on the last page, which fixes the page count but
the endnote position is wrong: should be after the body text, not at the
bottom of the page.

Now that we can have an endnote section (with one or more section frames
at a layout level), we have a container that can span over multiple
pages, is at the end of the document and is inline.

Fix the bad position by:

1) Reverting the layout changes from the old approach, which gives us a
   bad position for the endnote.

2) Creating an endnote section frame on demand in
   SwFootnoteBossFrame::AppendFootnote().

3) Moving part of the endnote to a next page works out of the box, but
   moving part of the endnote to a previous page needs explicit handling
   in SwFlowFrame::MoveBwd(), similar to how SwFrame::GetPrevSctLeaf()
   does this in the simple section case. This needs explicit handling,
   because the body frame of the endnote section is empty, all content goes
   to its endnote container.

Note that this just reimplements the compat flag, but its enablement
(only for DOC import, only for <= 2 endnotes) stays unchanged for now.

Change-Id: I8b271895aeff378418aed8705fe6b99a69232bd2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167616
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
diff --git a/sw/qa/core/layout/data/inline-endnote-and-footnote.doc b/sw/qa/core/layout/data/inline-endnote-and-footnote.doc
new file mode 100644
index 0000000..39c5636
--- /dev/null
+++ b/sw/qa/core/layout/data/inline-endnote-and-footnote.doc
Binary files differ
diff --git a/sw/qa/core/layout/ftnfrm.cxx b/sw/qa/core/layout/ftnfrm.cxx
index 4c87420..71fd3fd 100644
--- a/sw/qa/core/layout/ftnfrm.cxx
+++ b/sw/qa/core/layout/ftnfrm.cxx
@@ -63,4 +63,25 @@ CPPUNIT_TEST_FIXTURE(Test, testFlySplitFootnoteLayout)
    CPPUNIT_ASSERT(pPage->FindFootnoteCont());
}

CPPUNIT_TEST_FIXTURE(Test, testInlineEndnoteAndFootnote)
{
    // Given a DOC file with an endnote and then a footnote:
    createSwDoc("inline-endnote-and-footnote.doc");

    // When laying out that document:
    xmlDocUniquePtr pXmlDoc = parseLayoutDump();

    // Then make sure the footnote is below the endnote:
    // Without the accompanying fix in place, this test would have failed with:
    // - xpath should match exactly 1 node
    // i.e. the endnote was also in the footnote container, not at the end of the body text.
    sal_Int32 nEndnoteTop
        = parseDump("/root/page/body/section/column/ftncont/ftn/infos/bounds"_ostr, "top"_ostr)
              .toInt32();
    sal_Int32 nFootnoteTop
        = parseDump("/root/page/ftncont/ftn/infos/bounds"_ostr, "top"_ostr).toInt32();
    // Endnote at the end of body text, footnote at page bottom.
    CPPUNIT_ASSERT_LESS(nFootnoteTop, nEndnoteTop);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx
index 37fd20b..1cb5cf7 100644
--- a/sw/source/core/layout/flowfrm.cxx
+++ b/sw/source/core/layout/flowfrm.cxx
@@ -2257,18 +2257,47 @@ bool SwFlowFrame::MoveBwd( bool &rbReformat )
        const bool bEndnote = pFootnote->GetAttr()->GetFootnote().IsEndNote();
        const IDocumentSettingAccess& rSettings
            = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
        if( bEndnote && pFootnote->IsInSct() )
        bool bContEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
        if( bEndnote && pFootnote->IsInSct() && !bContEndnotes)
        {
            SwSectionFrame* pSect = pFootnote->FindSctFrame();
            if( pSect->IsEndnAtEnd() )
                // Endnotes at the end of the section.
                pRef = pSect->FindLastContent( SwFindMode::LastCnt );
        }
        else if (bEndnote && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
        else if (bEndnote && bContEndnotes)
        {
            // Endnotes at the end of the document.
            SwPageFrame* pPage = m_rThis.getRootFrame()->GetLastPage();
            pRef = pPage->FindLastBodyContent();
            SwSectionFrame* pSect = pFootnote->FindSctFrame();
            if (!pSect->GetPrev() && !pSect->FindMaster())
            {
                // The section is at the top of the page, and is not a follow of a master section.
                // See if there is a previous body frame.
                SwLayoutFrame* pPrev = pSect->GetPrevLayoutLeaf();
                if (pPrev && pPrev->IsBodyFrame())
                {
                    // There is, then see if that's on a different page.
                    SwPageFrame* pSectPage = pSect->FindPageFrame();
                    SwPageFrame* pPage = pPrev->FindPageFrame();
                    if (pPage != pSectPage)
                    {
                        // It is, then create a new section frame at the end of that previous body
                        // frame.
                        auto pNew = new SwSectionFrame(*pSect, /*bMaster=*/true);
                        pNew->InsertBehind(pPage->FindBodyCont(), pPage->FindLastBodyContent());
                        pNew->Init();
                        SwFrame* pLower = pNew->GetLower();
                        if (pLower->IsColumnFrame())
                        {
                            pRef = pLower;
                        }
                    }
                }
            }
            else
            {
                pRef = pFootnote->FindFootnoteBossFrame();
            }
        }
        if( !pRef )
            // Endnotes on a separate page.
diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx
index 0fd430c..e03519e 100644
--- a/sw/source/core/layout/ftnfrm.cxx
+++ b/sw/source/core/layout/ftnfrm.cxx
@@ -768,26 +768,18 @@ SwLayoutFrame *SwFrame::GetPrevFootnoteLeaf( MakePageType eMakeFootnote )
        SwFrame* pTmpRef = nullptr;
        const IDocumentSettingAccess& rSettings
            = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
        if( bEndn && pFootnote->IsInSct() )
        bool bContEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
        if( bEndn && pFootnote->IsInSct() && !bContEndnotes)
        {
            SwSectionFrame* pSect = pFootnote->FindSctFrame();
            if( pSect->IsEndnAtEnd() )
                // Endnotes at the end of the section.
                pTmpRef = pSect->FindLastContent( SwFindMode::LastCnt );
        }
        else if (bEndn && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
        else if (bEndn && bContEndnotes)
        {
            // Endnotes at the end of the document.
            SwPageFrame* pPage = getRootFrame()->GetLastPage();
            assert(pPage);
            SwFrame* pPrevPage = pPage->GetPrev();
            if (pPrevPage)
            {
                // Have a last but one page, use that since we try to get a preceding frame.
                assert(pPrevPage->IsPageFrame());
                pPage = static_cast<SwPageFrame*>(pPrevPage);
            }
            pTmpRef = pPage->FindLastBodyContent();
            pTmpRef = pFootnote->FindFootnoteBossFrame();
        }
        if( !pTmpRef )
            // Endnotes on a separate page.
@@ -1581,8 +1573,28 @@ void SwFootnoteBossFrame::AppendFootnote( SwContentFrame *pRef, SwTextFootnote *
        else if (rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
        {
            // Endnotes at the end of the document.
            pBoss = getRootFrame()->GetLastPage();
            pPage = pBoss->FindPageFrame();
            // Find the first page that hosts an endnote section.
            SwSectionFrame* pEndnoteSection = pPage->GetEndNoteSection();
            while (pPage->GetNext() && !pEndnoteSection)
            {
                pPage = pPage->GetNext()->DynCastPageFrame();
                pEndnoteSection = pPage->GetEndNoteSection();
            }
            // If there are no endnotes sections yet, create one at the end of the document.
            if (!pEndnoteSection)
            {
                SwSection* pSwSection = pDoc->GetEndNoteInfo().GetSwSection(*pDoc);
                pEndnoteSection = new SwSectionFrame(*pSwSection, pPage);
                pEndnoteSection->InsertBehind(pPage->FindBodyCont(), pPage->FindLastBodyContent());
                pEndnoteSection->Init();
                pEndnoteSection->SetEndNoteSection(true);
            }

            SwFrame* pColumnFrame = pEndnoteSection->GetLower();
            if (pColumnFrame->IsColumnFrame())
            {
                pBoss = static_cast<SwColumnFrame*>(pColumnFrame);
            }
        }
        else
        {
@@ -1931,10 +1943,7 @@ void SwFootnoteBossFrame::CollectFootnotes_( const SwContentFrame*   _pRef,
        bool bCollectFoundFootnote = false;
        // Ignore endnotes which are on a separate endnote page.
        bool bEndNote = _pFootnote->GetAttr()->GetFootnote().IsEndNote();
        const IDocumentSettingAccess& rSettings
            = _pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
        bool bContinuousEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
        if (_pFootnote->GetRef() == _pRef && (!bEndNote || bContinuousEndnotes))
        if (_pFootnote->GetRef() == _pRef && !bEndNote)
        {
            if (_pRefFootnoteBossFrame)
            {
diff --git a/sw/source/core/text/txtftn.cxx b/sw/source/core/text/txtftn.cxx
index 35bb31f..bb21b6a 100644
--- a/sw/source/core/text/txtftn.cxx
+++ b/sw/source/core/text/txtftn.cxx
@@ -598,8 +598,6 @@ void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDea
    mbFootnote = true;
    mbInFootnoteConnect = true; // Just reset!
    // See if pFootnote is an endnote on a separate endnote page.
    const IDocumentSettingAccess& rSettings = GetDoc().getIDocumentSettingAccess();
    const bool bContinuousEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
    const bool bEnd = pFootnote->GetFootnote().IsEndNote();

    // We want to store this value, because it is needed as a fallback
@@ -630,15 +628,11 @@ void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDea

    if( bDocEnd )
    {
        if ((pSect || bContinuousEndnotes) && pSrcFrame)
        if (pSect && pSrcFrame)
        {
            SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
            if (pFootnoteFrame && (pFootnoteFrame->IsInSct() || bContinuousEndnotes))
            if (pFootnoteFrame && pFootnoteFrame->IsInSct())
            {
                // We either have a foot/endnote that goes to the end of the section or are in Word
                // compatibility mode where endnotes go to the end of the document.  Handle both
                // cases by removing the footnote here, then later appending them to the correct
                // last page of the document or section.
                pBoss->RemoveFootnote( pSrcFrame, pFootnote );
                pSrcFrame = nullptr;
            }