tdf#124601 sw: add ContinuousEndnotes layout compat option

Writer has two cases for laying out endnotes: either they are at the end
of the section or are on a separate endnote page at the end of the
document.

Word always puts endnotes as continuous content at the end of the
document, not on a separate page.

Given that this continuous / separate page behavior difference seems to
be not part of the ODF or OOXML file format and neither UI allows to
configure this, the best way to resolve this looks like a new layout
compat option.

At a layout level, the "endnotes at the end of the section" code is
close to what we need, we just need to make sure that:

1) Endnotes are never moved backwards, even if their reference moves
back.

2) When appending an endnote, they should go to the footnote container
on the last page, not close to their reference.

With this, the page number in Word and Writer now match for the bugdoc.

Change-Id: I6fd0ee191e001d7c3a6df46d5e9fe8d7eb0327dc
Reviewed-on: https://gerrit.libreoffice.org/79857
Reviewed-by: Miklos Vajna <[email protected]>
Tested-by: Jenkins
diff --git a/sw/inc/IDocumentSettingAccess.hxx b/sw/inc/IDocumentSettingAccess.hxx
index 46f8c15..56ff3ab 100644
--- a/sw/inc/IDocumentSettingAccess.hxx
+++ b/sw/inc/IDocumentSettingAccess.hxx
@@ -102,6 +102,7 @@
    EMBED_COMPLEX_SCRIPT_FONTS,
    EMBED_SYSTEM_FONTS,
    APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING,
    CONTINUOUS_ENDNOTES,
};

 /** Provides access to settings of a document
diff --git a/sw/qa/extras/layout/data/tdf124601.doc b/sw/qa/extras/layout/data/tdf124601.doc
new file mode 100644
index 0000000..fdfde82
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf124601.doc
Binary files differ
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index a6d7023..87a86a35 100644
--- a/sw/qa/extras/layout/layout.cxx
+++ b/sw/qa/extras/layout/layout.cxx
@@ -3217,6 +3217,23 @@
    assertXPath(pXmlDoc, "/root/page/body/txt[2]/LineBreak", 1);
}

CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf124601)
{
    // This is a testcase for the ContinuousEndnotes compat flag.
    // The document has 2 pages, the endnote anchor is on the first page.
    // The endnote should be on the 2nd page together with the last page content.
    createDoc("tdf124601.doc");
    xmlDocPtr pXmlDoc = parseLayoutDump();

    // Without the accompanying fix in place, this test would have failed with:
    // - Expected: 2
    // - Actual  : 3
    // i.e. there was a separate endnote page, even when the ContinuousEndnotes compat option was
    // on.
    assertXPath(pXmlDoc, "/root/page", 2);
    assertXPath(pXmlDoc, "/root/page[2]/ftncont", 1);
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentSettingManager.cxx b/sw/source/core/doc/DocumentSettingManager.cxx
index 5e088e0..719f46f3 100644
--- a/sw/source/core/doc/DocumentSettingManager.cxx
+++ b/sw/source/core/doc/DocumentSettingManager.cxx
@@ -216,6 +216,8 @@
        case DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING: return mApplyParagraphMarkFormatToNumbering;
        case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING: return mbDisableOffPagePositioning;
        case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA: return mbEmptyDbFieldHidesPara;
        case DocumentSettingId::CONTINUOUS_ENDNOTES:
            return mbContinuousEndnotes;
        default:
            OSL_FAIL("Invalid setting id");
    }
@@ -448,6 +450,9 @@
        case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA:
            mbEmptyDbFieldHidesPara = value;
            break;
        case DocumentSettingId::CONTINUOUS_ENDNOTES:
            mbContinuousEndnotes = value;
            break;
        default:
            OSL_FAIL("Invalid setting id");
    }
diff --git a/sw/source/core/inc/DocumentSettingManager.hxx b/sw/source/core/inc/DocumentSettingManager.hxx
index c8c5b00..1cfe297 100644
--- a/sw/source/core/inc/DocumentSettingManager.hxx
+++ b/sw/source/core/inc/DocumentSettingManager.hxx
@@ -160,6 +160,7 @@
    bool mbLastBrowseMode                           : 1;
    bool mbDisableOffPagePositioning; // tdf#112443
    bool mbEmptyDbFieldHidesPara;
    bool mbContinuousEndnotes = false;

public:

diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx
index c7cfd30..1d1a2b3 100644
--- a/sw/source/core/layout/flowfrm.cxx
+++ b/sw/source/core/layout/flowfrm.cxx
@@ -2087,13 +2087,23 @@
        // have to have a result != 0
        SwFrame* pRef = nullptr;
        const bool bEndnote = pFootnote->GetAttr()->GetFootnote().IsEndNote();
        const IDocumentSettingAccess& rSettings
            = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
        if( bEndnote && pFootnote->IsInSct() )
        {
            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))
        {
            // Endnotes at the end of the document.
            SwPageFrame* pPage = m_rThis.getRootFrame()->GetLastPage();
            pRef = pPage->FindLastBodyContent();
        }
        if( !pRef )
            // Endnotes on a separate page.
            pRef = pFootnote->GetRef();

        OSL_ENSURE( pRef, "MoveBwd: Endnote for an empty section?" );
diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx
index 9826cf7..d4d4e53 100644
--- a/sw/source/core/layout/ftnfrm.cxx
+++ b/sw/source/core/layout/ftnfrm.cxx
@@ -37,6 +37,7 @@
#include <pam.hxx>
#include <ndtxt.hxx>
#include <sal/log.hxx>
#include <IDocumentSettingAccess.hxx>

#define ENDNOTE 0x80000000

@@ -1431,9 +1432,11 @@
    if ( pAttr->GetFootnote().IsEndNote() )
    {
        bEnd = true;
        const IDocumentSettingAccess& rSettings = *pAttr->GetTextNode().getIDocumentSettingAccess();
        if( GetUpper()->IsSctFrame() &&
            static_cast<SwSectionFrame*>(GetUpper())->IsEndnAtEnd() )
        {
            // Endnotes at the end of the section.
            SwFrame* pLast =
                static_cast<SwSectionFrame*>(GetUpper())->FindLastContent( SwFindMode::EndNote );
            if( pLast )
@@ -1442,8 +1445,15 @@
                pPage = pBoss->FindPageFrame();
            }
        }
        else if (rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
        {
            // Endnotes at the end of the document.
            pBoss = getRootFrame()->GetLastPage();
            pPage = pBoss->FindPageFrame();
        }
        else
        {
            // Endnotes on a separate page.
            while ( pPage->GetNext() && !pPage->IsEndNotePage() )
            {
                pPage = static_cast<SwPageFrame*>(pPage->GetNext());
diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx
index cade9b2..0c28088 100644
--- a/sw/source/filter/ww8/ww8par.cxx
+++ b/sw/source/filter/ww8/ww8par.cxx
@@ -1907,6 +1907,7 @@
    m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
    m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, true);
    m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true);
    m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);

    // COMPATIBILITY FLAGS END

diff --git a/sw/source/uibase/uno/SwXDocumentSettings.cxx b/sw/source/uibase/uno/SwXDocumentSettings.cxx
index d4bb9a0..93dc022 100644
--- a/sw/source/uibase/uno/SwXDocumentSettings.cxx
+++ b/sw/source/uibase/uno/SwXDocumentSettings.cxx
@@ -143,6 +143,7 @@
    HANDLE_SUBTRACT_FLYS,
    HANDLE_DISABLE_OFF_PAGE_POSITIONING,
    HANDLE_EMPTY_DB_FIELD_HIDES_PARA,
    HANDLE_CONTINUOUS_ENDNOTES,
};

static MasterPropertySetInfo * lcl_createSettingsInfo()
@@ -227,6 +228,7 @@
        { OUString("SubtractFlysAnchoredAtFlys"),       HANDLE_SUBTRACT_FLYS,         cppu::UnoType<bool>::get(),           0},
        { OUString("DisableOffPagePositioning"),       HANDLE_DISABLE_OFF_PAGE_POSITIONING,         cppu::UnoType<bool>::get(),           0},
        { OUString("EmptyDbFieldHidesPara"), HANDLE_EMPTY_DB_FIELD_HIDES_PARA, cppu::UnoType<bool>::get(), 0 },
        { OUString("ContinuousEndnotes"), HANDLE_CONTINUOUS_ENDNOTES, cppu::UnoType<bool>::get(), 0 },
/*
 * As OS said, we don't have a view when we need to set this, so I have to
 * find another solution before adding them to this property set - MTG
@@ -925,6 +927,16 @@
            }
        }
        break;
        case HANDLE_CONTINUOUS_ENDNOTES:
        {
            bool bTmp;
            if (rValue >>= bTmp)
            {
                mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES,
                                                       bTmp);
            }
        }
        break;
        default:
            throw UnknownPropertyException(OUString::number(rInfo.mnHandle));
    }
@@ -1381,6 +1393,12 @@
                DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA);
        }
        break;
        case HANDLE_CONTINUOUS_ENDNOTES:
        {
            rValue
                <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::CONTINUOUS_ENDNOTES);
        }
        break;
        default:
            throw UnknownPropertyException(OUString::number(rInfo.mnHandle));
    }