/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <config_features.h>
 
#include <doc.hxx>
#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <DocumentFieldsManager.hxx>
#include <DocumentSettingManager.hxx>
#include <DocumentDrawModelManager.hxx>
#include <DocumentTimerManager.hxx>
#include <DocumentDeviceManager.hxx>
#include <DocumentChartDataProviderManager.hxx>
#include <DocumentLinksAdministrationManager.hxx>
#include <DocumentListItemsManager.hxx>
#include <DocumentListsManager.hxx>
#include <DocumentOutlineNodesManager.hxx>
#include <DocumentContentOperationsManager.hxx>
#include <DocumentRedlineManager.hxx>
#include <DocumentStatisticsManager.hxx>
#include <DocumentStateManager.hxx>
#include <DocumentStylePoolManager.hxx>
#include <DocumentLayoutManager.hxx>
#include <DocumentExternalDataManager.hxx>
#include <UndoManager.hxx>
#include <dbmgr.hxx>
#include <hintids.hxx>
 
#include <comphelper/random.hxx>
#include <tools/multisel.hxx>
#include <rtl/ustring.hxx>
#include <svl/poolitem.hxx>
#include <unotools/syslocale.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/formatbreakitem.hxx>
#include <editeng/pbinitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/xmlcnitm.hxx>
#include <editeng/fontitem.hxx>
#include <unotools/localedatawrapper.hxx>
 
#include <officecfg/Office/Writer.hxx>
 
#include <swatrset.hxx>
#include <swmodule.hxx>
#include <fmtrfmrk.hxx>
#include <fmtinfmt.hxx>
#include <fmtfld.hxx>
#include <txtfld.hxx>
#include <dbfld.hxx>
#include <txtinet.hxx>
#include <txtrfmrk.hxx>
#include <frmatr.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <swundo.hxx>
#include <rolbck.hxx>
#include <UndoAttribute.hxx>
#include <UndoCore.hxx>
#include <UndoTable.hxx>
#include <pagedesc.hxx>
#include <doctxm.hxx>
#include <poolfmt.hxx>
#include <SwGrammarMarkUp.hxx>
#include <scriptinfo.hxx>
#include <mdiexp.hxx>
#include <docary.hxx>
#include <printdata.hxx>
#include <strings.hrc>
#include <SwUndoTOXChange.hxx>
#include <unocrsr.hxx>
#include <docfld.hxx>
#include <docufld.hxx>
#include <viewsh.hxx>
#include <shellres.hxx>
#include <txtfrm.hxx>
#include <attrhint.hxx>
 
#include <vector>
#include <map>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <osl/interlck.h>
#include <vbahelper/vbaaccesshelper.hxx>
#include <editeng/langitem.hxx>
#include <calbck.hxx>
#include <crsrsh.hxx>
 
/* @@@MAINTAINABILITY-HORROR@@@
   Probably unwanted dependency on SwDocShell
*/
#include <docsh.hxx>
 
#include <com/sun/star/text/XTextRange.hpp>
#include <editeng/unoprnms.hxx>
#include <unotextrange.hxx>
#include <unoprnms.hxx>
#include <unomap.hxx>
#include <fmturl.hxx>
#include <tblafmt.hxx>
#include <istyleaccess.hxx>
 
using namespace ::com::sun::star;
 
sal_Int32 SwDoc::acquire()
{
    assert(mReferenceCount >= 0);
    return osl_atomic_increment(&mReferenceCount);
}
 
sal_Int32 SwDoc::release()
{
    assert(mReferenceCount >= 1);
    auto x = osl_atomic_decrement(&mReferenceCount);
    if (x == 0)
        delete this;
    return x;
}
 
sal_Int32 SwDoc::getReferenceCount() const
{
    assert(mReferenceCount >= 0);
    return mReferenceCount;
}
 
::sw::MetaFieldManager & SwDoc::GetMetaFieldManager()
{
    return *m_pMetaFieldManager;
}
 
::SwContentControlManager& SwDoc::GetContentControlManager()
{
    return *m_pContentControlManager;
}
 
::sw::UndoManager & SwDoc::GetUndoManager()
{
    return *m_pUndoManager;
}
 
::sw::UndoManager const & SwDoc::GetUndoManager() const
{
    return *m_pUndoManager;
}
 
 
IDocumentUndoRedo & SwDoc::GetIDocumentUndoRedo()
{
    return *m_pUndoManager;
}
 
IDocumentUndoRedo const & SwDoc::GetIDocumentUndoRedo() const
{
    return *m_pUndoManager;
}
 
/* IDocumentDrawModelAccess */
IDocumentDrawModelAccess const & SwDoc::getIDocumentDrawModelAccess() const
{
    return GetDocumentDrawModelManager();
}
 
IDocumentDrawModelAccess & SwDoc::getIDocumentDrawModelAccess()
{
    return GetDocumentDrawModelManager();
}
 
::sw::DocumentDrawModelManager const & SwDoc::GetDocumentDrawModelManager() const
{
    return *m_pDocumentDrawModelManager;
}
 
::sw::DocumentDrawModelManager & SwDoc::GetDocumentDrawModelManager()
{
    return *m_pDocumentDrawModelManager;
}
 
/* IDocumentSettingAccess */
IDocumentSettingAccess const & SwDoc::getIDocumentSettingAccess() const
{
    return GetDocumentSettingManager();
}
 
IDocumentSettingAccess & SwDoc::getIDocumentSettingAccess()
{
    return GetDocumentSettingManager();
}
 
::sw::DocumentSettingManager & SwDoc::GetDocumentSettingManager()
{
    return *m_pDocumentSettingManager;
}
 
::sw::DocumentSettingManager const & SwDoc::GetDocumentSettingManager() const
{
    return *m_pDocumentSettingManager;
}
 
sal_uInt32 SwDoc::getRsid() const
{
    return mnRsid;
}
 
void SwDoc::setRsid( sal_uInt32 nVal )
{
    static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
 
    sal_uInt32 nIncrease = 0;
    if (!bHack)
    {
        // Increase the rsid with a random number smaller than 2^17. This way we
        // expect to be able to edit a document 2^12 times before rsid overflows.
        // start from 1 to ensure the new rsid is not the same
        nIncrease = comphelper::rng::uniform_uint_distribution(1, (1 << 17) - 1);
    }
    mnRsid = nVal + nIncrease;
}
 
sal_uInt32 SwDoc::getRsidRoot() const
{
    return mnRsidRoot;
}
 
void SwDoc::setRsidRoot( sal_uInt32 nVal )
{
    mnRsidRoot = nVal;
}
 
/* IDocumentChartDataProviderAccess */
IDocumentChartDataProviderAccess const & SwDoc::getIDocumentChartDataProviderAccess() const
{
    return *m_pDocumentChartDataProviderManager;
}
 
IDocumentChartDataProviderAccess & SwDoc::getIDocumentChartDataProviderAccess()
{
    return *m_pDocumentChartDataProviderManager;
}
 
// IDocumentDeviceAccess
IDocumentDeviceAccess const & SwDoc::getIDocumentDeviceAccess() const
{
    return *m_pDeviceAccess;
}
 
IDocumentDeviceAccess & SwDoc::getIDocumentDeviceAccess()
{
    return *m_pDeviceAccess;
}
 
//IDocumentTimerAccess
IDocumentTimerAccess const & SwDoc::getIDocumentTimerAccess() const
{
    return *m_pDocumentTimerManager;
}
 
IDocumentTimerAccess & SwDoc::getIDocumentTimerAccess()
{
    return *m_pDocumentTimerManager;
}
 
// IDocumentLinksAdministration
IDocumentLinksAdministration const & SwDoc::getIDocumentLinksAdministration() const
{
    return *m_pDocumentLinksAdministrationManager;
}
 
IDocumentLinksAdministration & SwDoc::getIDocumentLinksAdministration()
{
    return *m_pDocumentLinksAdministrationManager;
}
 
::sw::DocumentLinksAdministrationManager const & SwDoc::GetDocumentLinksAdministrationManager() const
{
    return *m_pDocumentLinksAdministrationManager;
}
 
::sw::DocumentLinksAdministrationManager & SwDoc::GetDocumentLinksAdministrationManager()
{
    return *m_pDocumentLinksAdministrationManager;
}
 
//IDocumentListItems
IDocumentListItems const & SwDoc::getIDocumentListItems() const
{
    return *m_pDocumentListItemsManager;
}
 
//IDocumentListItems
IDocumentListItems & SwDoc::getIDocumentListItems()
{
    return *m_pDocumentListItemsManager;
}
 
//IDocumentListsAccess
IDocumentListsAccess const & SwDoc::getIDocumentListsAccess() const
{
    return *m_pDocumentListsManager;
}
 
IDocumentListsAccess & SwDoc::getIDocumentListsAccess()
{
    return *m_pDocumentListsManager;
}
 
//IDocumentOutlinesNodes
IDocumentOutlineNodes const & SwDoc::getIDocumentOutlineNodes() const
{
    return *m_pDocumentOutlineNodesManager;
}
 
IDocumentOutlineNodes & SwDoc::getIDocumentOutlineNodes()
{
    return *m_pDocumentOutlineNodesManager;
}
 
//IDocumentContentOperations
IDocumentContentOperations const & SwDoc::getIDocumentContentOperations() const
{
    return *m_pDocumentContentOperationsManager;
}
 
IDocumentContentOperations & SwDoc::getIDocumentContentOperations()
{
    return *m_pDocumentContentOperationsManager;
}
 
::sw::DocumentContentOperationsManager const & SwDoc::GetDocumentContentOperationsManager() const
{
    return *m_pDocumentContentOperationsManager;
}
::sw::DocumentContentOperationsManager & SwDoc::GetDocumentContentOperationsManager()
{
    return *m_pDocumentContentOperationsManager;
}
 
//IDocumentRedlineAccess
IDocumentRedlineAccess const & SwDoc::getIDocumentRedlineAccess() const
{
    return *m_pDocumentRedlineManager;
}
 
IDocumentRedlineAccess& SwDoc::getIDocumentRedlineAccess()
{
    return *m_pDocumentRedlineManager;
}
 
::sw::DocumentRedlineManager const & SwDoc::GetDocumentRedlineManager() const
{
    return *m_pDocumentRedlineManager;
}
 
::sw::DocumentRedlineManager& SwDoc::GetDocumentRedlineManager()
{
    return *m_pDocumentRedlineManager;
}
 
//IDocumentFieldsAccess
 
IDocumentFieldsAccess const & SwDoc::getIDocumentFieldsAccess() const
{
    return *m_pDocumentFieldsManager;
}
 
IDocumentFieldsAccess & SwDoc::getIDocumentFieldsAccess()
{
    return *m_pDocumentFieldsManager;
}
 
::sw::DocumentFieldsManager & SwDoc::GetDocumentFieldsManager()
{
    return *m_pDocumentFieldsManager;
}
 
//IDocumentStatistics
IDocumentStatistics const & SwDoc::getIDocumentStatistics() const
{
    return *m_pDocumentStatisticsManager;
}
 
IDocumentStatistics & SwDoc::getIDocumentStatistics()
{
    return *m_pDocumentStatisticsManager;
}
 
::sw::DocumentStatisticsManager const & SwDoc::GetDocumentStatisticsManager() const
{
    return *m_pDocumentStatisticsManager;
}
 
::sw::DocumentStatisticsManager & SwDoc::GetDocumentStatisticsManager()
{
    return *m_pDocumentStatisticsManager;
}
 
//IDocumentState
IDocumentState const & SwDoc::getIDocumentState() const
{
    return *m_pDocumentStateManager;
}
 
IDocumentState & SwDoc::getIDocumentState()
{
    return *m_pDocumentStateManager;
}
 
//IDocumentLayoutAccess
IDocumentLayoutAccess const & SwDoc::getIDocumentLayoutAccess() const
{
    return *m_pDocumentLayoutManager;
}
 
IDocumentLayoutAccess & SwDoc::getIDocumentLayoutAccess()
{
    return *m_pDocumentLayoutManager;
}
 
::sw::DocumentLayoutManager const & SwDoc::GetDocumentLayoutManager() const
{
    return *m_pDocumentLayoutManager;
}
 
::sw::DocumentLayoutManager & SwDoc::GetDocumentLayoutManager()
{
    return *m_pDocumentLayoutManager;
}
 
//IDocumentStylePoolAccess
IDocumentStylePoolAccess const & SwDoc::getIDocumentStylePoolAccess() const
{
    return *m_pDocumentStylePoolManager;
}
 
IDocumentStylePoolAccess & SwDoc::getIDocumentStylePoolAccess()
{
    return *m_pDocumentStylePoolManager;
}
 
//IDocumentExternalData
IDocumentExternalData const & SwDoc::getIDocumentExternalData() const
{
    return *m_pDocumentExternalDataManager;
}
 
IDocumentExternalData & SwDoc::getIDocumentExternalData()
{
    return *m_pDocumentExternalDataManager;
}
 
/* Implementations the next Interface here */
 
/*
 * Document editing (Doc-SS) to fill the document
 * by the RTF parser and for the EditShell.
 */
void SwDoc::ChgDBData(const SwDBData& rNewData)
{
    if( rNewData != maDBData )
    {
        maDBData = rNewData;
        getIDocumentState().SetModified();
        if (m_pDBManager)
            m_pDBManager->CommitLastRegistrations();
    }
    getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DatabaseName)->UpdateFields();
}
 
namespace {
 
struct PostItField_ : public SetGetExpField
{
    PostItField_( const SwNode& rNd, const SwTextField* pField )
        : SetGetExpField( rNd, pField, std::nullopt ) {}
 
    sal_uInt16 GetPageNo( const StringRangeEnumerator &rRangeEnum,
            const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
            sal_uInt16& rVirtPgNo, sal_Int32& rLineNo );
 
    const SwPostItField* GetPostIt() const
    {
        return static_cast<const SwPostItField*>( GetTextField()->GetFormatField().GetField() );
    }
};
 
}
 
sal_uInt16 PostItField_::GetPageNo(
    const StringRangeEnumerator &rRangeEnum,
    const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
    /* out */ sal_uInt16& rVirtPgNo, /* out */ sal_Int32& rLineNo )
{
    //Problem: If a PostItField is contained in a Node that is represented
    //by more than one layout instance,
    //we have to decide whether it should be printed once or n-times.
    //Probably only once. For the page number we don't select a random one,
    //but the PostIt's first occurrence in the selected area.
    rVirtPgNo = 0;
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(GetTextField()->GetTextNode());
    for( SwTextFrame* pFrame = aIter.First(); pFrame;  pFrame = aIter.Next() )
    {
        TextFrameIndex const nPos = pFrame->MapModelToView(
                &GetTextField()->GetTextNode(), GetContent());
        if( pFrame->GetOffset() > nPos ||
            (pFrame->HasFollow() && pFrame->GetFollow()->GetOffset() <= nPos) )
            continue;
        sal_uInt16 nPgNo = pFrame->GetPhyPageNum();
        if( rRangeEnum.hasValue( nPgNo, &rPossiblePages ))
        {
            rLineNo = o3tl::narrowing<sal_Int32>(pFrame->GetLineCount( nPos ) +
                      pFrame->GetAllLines() - pFrame->GetThisLines());
            rVirtPgNo = pFrame->GetVirtPageNum();
            return nPgNo;
        }
    }
    return 0;
}
 
bool sw_GetPostIts(const IDocumentFieldsAccess& rIDFA, SetGetExpFields* pSrtLst)
{
    SwFieldType* pFieldType = rIDFA.GetSysFieldType(SwFieldIds::Postit);
    assert(pFieldType);
 
    std::vector<SwFormatField*> vFields;
    pFieldType->GatherFields(vFields);
    if(pSrtLst)
        for(auto pField: vFields)
        {
            auto pTextField = pField->GetTextField();
            std::unique_ptr<PostItField_> pNew(new PostItField_(pTextField->GetTextNode(), pTextField));
            pSrtLst->insert(std::move(pNew));
 
        }
    return vFields.size()>0;
}
 
static void lcl_FormatPostIt(
    IDocumentContentOperations* pIDCO,
    SwPaM& aPam,
    const SwPostItField* pField,
    bool bNewPage, bool bIsFirstPostIt,
    sal_uInt16 nPageNo, sal_Int32 nLineNo )
{
    static char const sTmp[] = " : ";
 
    assert(SwViewShell::GetShellRes());
 
    if (bNewPage)
    {
        pIDCO->InsertPoolItem( aPam, SvxFormatBreakItem( SvxBreak::PageAfter, RES_BREAK ) );
        pIDCO->SplitNode( *aPam.GetPoint(), false );
    }
    else if (!bIsFirstPostIt)
    {
        // add an empty line between different notes
        pIDCO->SplitNode( *aPam.GetPoint(), false );
        pIDCO->SplitNode( *aPam.GetPoint(), false );
    }
 
    OUString aStr = SwViewShell::GetShellRes()->aPostItPage +
        sTmp +
        OUString::number( nPageNo ) +
        " ";
    if( nLineNo )
    {
        aStr += SwViewShell::GetShellRes()->aPostItLine +
            sTmp +
            OUString::number( nLineNo ) +
            " ";
    }
    SvtSysLocale aSysLocale;
    aStr += SwViewShell::GetShellRes()->aPostItAuthor +
        sTmp + pField->GetPar1() + " " +
        /*(LocaleDataWrapper&)*/aSysLocale.GetLocaleData().getDate( pField->GetDate() );
    if(pField->GetResolved())
        aStr += " " + SwResId(STR_RESOLVED);
    pIDCO->InsertString( aPam, aStr );
 
    pIDCO->SplitNode( *aPam.GetPoint(), false );
    aStr = pField->GetPar2();
#if defined(_WIN32)
    // Throw out all CR in Windows
    aStr = aStr.replaceAll("\r", "");
#endif
    pIDCO->InsertString( aPam, aStr );
}
 
/// provide the paper tray to use according to the page style in use,
/// but do that only if the respective item is NOT just the default item
static sal_Int32 lcl_GetPaperBin( const SwPageFrame *pStartFrame )
{
    sal_Int32 nRes = -1;
 
    const SwFrameFormat &rFormat = pStartFrame->GetPageDesc()->GetMaster();
    const SfxPoolItem *pItem = nullptr;
    SfxItemState eState = rFormat.GetItemState( RES_PAPER_BIN, false, &pItem );
    const SvxPaperBinItem *pPaperBinItem = dynamic_cast< const SvxPaperBinItem * >(pItem);
    if (eState > SfxItemState::DEFAULT && pPaperBinItem)
        nRes = pPaperBinItem->GetValue();
 
    return nRes;
}
 
namespace
{
// tdf#:114663 Translates a range string from user input (with page numbering possibly not
// taking blank pages into account) to equivalent string which references physical page numbers.
// rUIPages2PhyPagesMap must contain a contiguous sequence of UI page numbers
OUString UIPages2PhyPages(const OUString& rUIPageRange, const std::map< sal_Int32, sal_Int32 >& rUIPages2PhyPagesMap)
{
    if (rUIPages2PhyPagesMap.empty())
        return OUString();
    auto iMin = rUIPages2PhyPagesMap.begin();
    const sal_Int32 nUIPageMin = iMin->first, nPhyPageMin = iMin->second;
    auto iMax = rUIPages2PhyPagesMap.rbegin();
    const sal_Int32 nUIPageMax = iMax->first, nPhyPageMax = iMax->second;
    OUStringBuffer aOut(rUIPageRange.getLength());
    OUStringBuffer aNumber(16);
    const sal_Unicode* pInput = rUIPageRange.getStr();
    while (*pInput)
    {
        while (*pInput >= '0' && *pInput <= '9')
            aNumber.append(*pInput++);
        if (!aNumber.isEmpty())
        {
            sal_Int32 nNumber = o3tl::toInt32(aNumber);
            aNumber.setLength(0);
            if (nNumber < nUIPageMin)
                nNumber = nPhyPageMin-1;
            else if (nNumber > nUIPageMax)
                nNumber = nPhyPageMax+1;
            else
                nNumber = rUIPages2PhyPagesMap.at(nNumber);
            aOut.append(nNumber);
        }
 
        while (*pInput && (*pInput < '0' || *pInput > '9'))
            aOut.append(*pInput++);
    }
 
    return aOut.makeStringAndClear();
}
}
 
// tdf#52316 remove blank pages from page count and actual page number
void SwDoc::CalculateNonBlankPages(
    const SwRootFrame& rLayout,
    sal_uInt16& nDocPageCount,
    sal_uInt16& nActualPage)
{
    sal_uInt16 nDocPageCountWithBlank = nDocPageCount;
    sal_uInt16 nActualPageWithBlank = nActualPage;
    sal_uInt16 nPageNum = 1;
    const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
    while (pStPage && nPageNum <= nDocPageCountWithBlank)
    {
        if ( pStPage->getFrameArea().Height() == 0 )
        {
            --nDocPageCount;
            if (nPageNum <= nActualPageWithBlank)
                --nActualPage;
        }
        ++nPageNum;
        pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
    }
}
 
void SwDoc::CalculatePagesForPrinting(
    const SwRootFrame& rLayout,
    /* out */ SwRenderData &rData,
    const SwPrintUIOptions &rOptions,
    bool bIsPDFExport,
    sal_Int32 nDocPageCount )
{
    const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
    const bool bPrintSelection = nContent == 2;
 
    // properties to take into account when calculating the set of pages
    // (PDF export UI does not allow for selecting left or right pages only)
    bool bPrintLeftPages    = bIsPDFExport || rOptions.IsPrintLeftPages();
    bool bPrintRightPages   = bIsPDFExport || rOptions.IsPrintRightPages();
    // #i103700# printing selections should not allow for automatic inserting empty pages
    bool bPrintEmptyPages   = !bPrintSelection && rOptions.IsPrintEmptyPages( bIsPDFExport );
 
    std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
    o3tl::sorted_vector< sal_Int32 > &rValidPages = rData.GetValidPagesSet();
    // Map page numbers from user input (possibly ignoring blanks) to physical page numbers
    std::map< sal_Int32, sal_Int32 > aUIPages2PhyPagesMap;
    rValidPages.clear();
 
    sal_Int32 nPageNum = 1, nUIPageNum = 1;
    const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
    while (pStPage && nPageNum <= nDocPageCount)
    {
        const bool bNonEmptyPage = pStPage->getFrameArea().Height() != 0;
        const bool bPrintThisPage =
            ( (bPrintRightPages && pStPage->OnRightPage()) ||
              (bPrintLeftPages && !pStPage->OnRightPage()) ) &&
            ( bPrintEmptyPages || bNonEmptyPage );
 
        if (bPrintThisPage)
        {
            rValidPages.insert( nPageNum );
            rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
        }
 
        if ( bPrintEmptyPages || bNonEmptyPage )
        {
            aUIPages2PhyPagesMap[nUIPageNum++] = nPageNum;
        }
        ++nPageNum;
        pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
    }
 
    // now that we have identified the valid pages for printing according
    // to the print settings we need to get the PageRange to use and
    // use both results to get the actual pages to be printed
    // (post-it settings need to be taken into account later on!)
 
    // get PageRange value to use
    OUString aPageRange;
    // #i116085# - adjusting fix for i113919
    if ( !bIsPDFExport )
    {
        // PageContent :
        // 0 -> print all pages (default if aPageRange is empty)
        // 1 -> print range according to PageRange
        // 2 -> print selection
        if (1 == nContent)
            aPageRange = rOptions.getStringValue( "PageRange" );
 
        if (2 == nContent)
        {
            // note that printing selections is actually implemented by copying
            // the selection to a new temporary document and printing all of that one.
            // Thus for Writer "PrintContent" must never be 2.
            // See SwXTextDocument::GetRenderDoc for evaluating if a selection is to be
            // printed and for creating the temporary document.
        }
 
        // please note
    }
    if (aPageRange.isEmpty())    // empty string -> print all
    {
        // set page range to print to 'all pages'
        aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
    }
    else
    {
        // Convert page numbers from user input to physical page numbers
        aPageRange = UIPages2PhyPages(aPageRange, aUIPages2PhyPagesMap);
    }
    rData.SetPageRange( aPageRange );
 
    // get vector of pages to print according to PageRange and valid pages set from above
    // (result may be an empty vector, for example if the range string is not correct)
    // If excluding empty pages, allow range to specify range of printable pages
    StringRangeEnumerator::getRangesFromString( aPageRange, rData.GetPagesToPrint(),
                                                1, nDocPageCount, 0, &rData.GetValidPagesSet() );
}
 
void SwDoc::UpdatePagesForPrintingWithPostItData(
    /* out */ SwRenderData &rData,
    const SwPrintUIOptions &rOptions,
    sal_Int32 nDocPageCount )
{
 
    SwPostItMode nPostItMode = static_cast<SwPostItMode>( rOptions.getIntValue( "PrintAnnotationMode", 0 ) );
    assert((nPostItMode == SwPostItMode::NONE || rData.HasPostItData())
            && "print post-its without post-it data?");
    const SetGetExpFields::size_type nPostItCount =
        rData.HasPostItData() ? rData.m_pPostItFields->size() : 0;
    if (nPostItMode == SwPostItMode::NONE || nPostItCount <= 0)
        return;
 
    CurrShell aCurr( rData.m_pPostItShell.get() );
 
    // clear document and move to end of it
    SwDoc & rPostItDoc(*rData.m_pPostItShell->GetDoc());
    SwPaM aPam(rPostItDoc.GetNodes().GetEndOfContent());
    aPam.Move( fnMoveBackward, GoInDoc );
    aPam.SetMark();
    aPam.Move( fnMoveForward, GoInDoc );
    rPostItDoc.getIDocumentContentOperations().DeleteRange( aPam );
 
    const StringRangeEnumerator aRangeEnum( rData.GetPageRange(), 1, nDocPageCount, 0 );
 
    // For mode SwPostItMode::EndPage:
    // maps a physical page number to the page number in post-it document that holds
    // the first post-it for that physical page . Needed to relate the correct start frames
    // from the post-it doc to the physical page of the document
    std::map< sal_Int32, sal_Int32 >  aPostItLastStartPageNum;
 
    // add all post-its on valid pages within the page range to the
    // temporary post-it document.
    // Since the array of post-it fields is sorted by page and line number we will
    // already get them in the correct order
    sal_uInt16 nVirtPg = 0, nLastPageNum = 0, nPhyPageNum = 0;
    sal_Int32 nLineNo = 0;
    bool bIsFirstPostIt = true;
    for (SetGetExpFields::size_type i = 0; i < nPostItCount; ++i)
    {
        PostItField_& rPostIt = static_cast<PostItField_&>(*(*rData.m_pPostItFields)[ i ]);
        nLastPageNum = nPhyPageNum;
        nPhyPageNum = rPostIt.GetPageNo(
                aRangeEnum, rData.GetValidPagesSet(), nVirtPg, nLineNo );
        if (nPhyPageNum)
        {
            // need to insert a page break?
            // In SwPostItMode::EndPage mode for each document page the following
            // post-it page needs to start on a new page
            const bool bNewPage = nPostItMode == SwPostItMode::EndPage &&
                    !bIsFirstPostIt && nPhyPageNum != nLastPageNum;
 
            lcl_FormatPostIt( &rData.m_pPostItShell->GetDoc()->getIDocumentContentOperations(), aPam,
                    rPostIt.GetPostIt(), bNewPage, bIsFirstPostIt, nVirtPg, nLineNo );
            bIsFirstPostIt = false;
 
            if (nPostItMode == SwPostItMode::EndPage)
            {
                // get the correct number of current pages for the post-it document
                rData.m_pPostItShell->CalcLayout();
                const sal_Int32 nPages = rData.m_pPostItShell->GetPageCount();
                aPostItLastStartPageNum[ nPhyPageNum ] = nPages;
            }
        }
    }
 
    // format post-it doc to get correct number of pages
    rData.m_pPostItShell->CalcLayout();
 
    SwRootFrame* pPostItRoot = rData.m_pPostItShell->GetLayout();
    //tdf#103313 print dialog maxes out cpu as Idles never get to
    //complete this postitshell's desire to complete formatting
    pPostItRoot->ResetIdleFormat();
 
    const sal_Int32 nPostItDocPageCount = rData.m_pPostItShell->GetPageCount();
 
    if (nPostItMode == SwPostItMode::Only || nPostItMode == SwPostItMode::EndDoc)
    {
        // now add those post-it pages to the vector of pages to print
        // or replace them if only post-its should be printed
 
        if (nPostItMode == SwPostItMode::Only)
        {
            // no document page to be printed
            rData.GetPagesToPrint().clear();
        }
 
        // now we just need to add the post-it pages to be printed to the
        // end of the vector of pages to print
        sal_Int32 nPageNum = 0;
        const SwPageFrame * pPageFrame = static_cast<SwPageFrame*>(pPostItRoot->Lower());
        while( pPageFrame && nPageNum < nPostItDocPageCount )
        {
            ++nPageNum;
            // negative page number indicates page is from the post-it doc
            rData.GetPagesToPrint().push_back( -nPageNum );
            pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
        }
        OSL_ENSURE( nPageNum == nPostItDocPageCount, "unexpected number of pages" );
    }
    else if (nPostItMode == SwPostItMode::EndPage)
    {
        // the next step is to find all the pages from the post-it
        // document that should be printed for a given physical page
        // of the document
 
        std::vector< sal_Int32 >            aTmpPagesToPrint;
        sal_Int32 nLastPostItPage(0);
        const size_t nNum = rData.GetPagesToPrint().size();
        for (size_t i = 0 ;  i < nNum;  ++i)
        {
            // add the physical page to print from the document
            const sal_Int32 nPhysPage = rData.GetPagesToPrint()[i];
            aTmpPagesToPrint.push_back( nPhysPage );
 
            // add the post-it document pages to print, i.e those
            // post-it pages that have the data for the above physical page
            std::map<sal_Int32, sal_Int32>::const_iterator const iter(
                    aPostItLastStartPageNum.find(nPhysPage));
            if (iter != aPostItLastStartPageNum.end())
            {
                for (sal_Int32 j = nLastPostItPage + 1;
                        j <= iter->second; ++j)
                {
                    // negative page number indicates page is from the
                    aTmpPagesToPrint.push_back(-j); // post-it document
                }
                nLastPostItPage = iter->second;
            }
        }
 
        // finally we need to assign those vectors to the resulting ones.
        // swapping the data should be more efficient than assigning since
        // we won't need the temporary vectors anymore
        rData.GetPagesToPrint().swap( aTmpPagesToPrint );
    }
 
}
 
void SwDoc::CalculatePagePairsForProspectPrinting(
    const SwRootFrame& rLayout,
    /* out */ SwRenderData &rData,
    const SwPrintUIOptions &rOptions,
    sal_Int32 nDocPageCount )
{
    std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
    o3tl::sorted_vector< sal_Int32 > &rValidPagesSet = rData.GetValidPagesSet();
    std::vector< std::pair< sal_Int32, sal_Int32 > > &rPagePairs = rData.GetPagePairsForProspectPrinting();
    std::map< sal_Int32, const SwPageFrame * > validStartFrames;
 
    rPagePairs.clear();
    rValidPagesSet.clear();
 
    OUString aPageRange;
    // PageContent :
    // 0 -> print all pages (default if aPageRange is empty)
    // 1 -> print range according to PageRange
    // 2 -> print selection
    const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
    if (nContent == 1)
        aPageRange = rOptions.getStringValue( "PageRange" );
    if (aPageRange.isEmpty())    // empty string -> print all
    {
        // set page range to print to 'all pages'
        aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
    }
    StringRangeEnumerator aRange( aPageRange, 1, nDocPageCount, 0 );
 
    if ( aRange.size() <= 0)
        return;
 
    const SwPageFrame *pStPage  = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
    for ( sal_Int32 i = 1; pStPage && i < nDocPageCount; ++i )
        pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
    if ( !pStPage )          // Then it was that
        return;
 
    // currently for prospect printing all pages are valid to be printed
    // thus we add them all to the respective map and set for later use
    sal_Int32 nPageNum = 0;
    const SwPageFrame *pPageFrame  = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
    while( pPageFrame && nPageNum < nDocPageCount )
    {
        ++nPageNum;
        rValidPagesSet.insert( nPageNum );
        validStartFrames[ nPageNum ] = pPageFrame;
        pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
 
        rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
    }
    OSL_ENSURE( nPageNum == nDocPageCount, "unexpected number of pages" );
 
    // properties to take into account when calculating the set of pages
    // Note: here bPrintLeftPages and bPrintRightPages refer to the (virtual) resulting pages
    //      of the prospect!
    bool bPrintLeftPages     = rOptions.IsPrintLeftPages();
    bool bPrintRightPages    = rOptions.IsPrintRightPages();
    bool bPrintProspectRTL = rOptions.getIntValue( "PrintProspectRTL",  0 ) != 0;
 
    // get pages for prospect printing according to the 'PageRange'
    // (duplicates and any order allowed!)
    std::vector< sal_Int32 > aPagesToPrint;
    StringRangeEnumerator::getRangesFromString(
            aPageRange, aPagesToPrint, 1, nDocPageCount, 0 );
 
    if (aPagesToPrint.empty())
        return;
 
    // now fill the vector for calculating the page pairs with the start frames
    // from the above obtained vector
    std::vector< const SwPageFrame * > aVec;
    for (sal_Int32 nPage : aPagesToPrint)
    {
        const SwPageFrame *pFrame = validStartFrames[ nPage ];
        aVec.push_back( pFrame );
    }
 
    // just one page is special ...
    if ( 1 == aVec.size() )
    {
        aVec.push_back( nullptr ); // insert a second empty page
    }
    else
    {
        // now extend the number of pages to fit a multiple of 4
        // (4 'normal' pages are needed for a single prospect paper
        //  with back and front)
        while( aVec.size() & 3 )
            aVec.push_back( nullptr );
    }
 
    // make sure that all pages are in correct order
    std::vector< const SwPageFrame * >::size_type nSPg = 0;
    std::vector< const SwPageFrame * >::size_type nEPg = aVec.size();
    assert(nEPg >= 2);
    sal_Int32 nStep = 1;
    if ( 0 == (nEPg & 1 ))      // there are no uneven ones!
        --nEPg;
 
    if ( !bPrintLeftPages )
        ++nStep;
    else if ( !bPrintRightPages )
    {
        ++nStep;
        ++nSPg;
        --nEPg;
    }
 
    // the number of 'virtual' pages to be printed
    sal_Int32 nCntPage = (( nEPg - nSPg ) / ( 2 * nStep )) + 1;
 
    for ( sal_Int32 nPrintCount = 0; nSPg < nEPg &&
            nPrintCount < nCntPage; ++nPrintCount )
    {
        pStPage = aVec[ nSPg ];
        const SwPageFrame* pNxtPage = nEPg < aVec.size() ? aVec[ nEPg ] : nullptr;
 
        short nRtlOfs = bPrintProspectRTL ? 1 : 0;
        if ( 0 == (( nSPg + nRtlOfs) & 1 ) )     // switch for odd number in LTR, even number in RTL
        {
            const SwPageFrame* pTmp = pStPage;
            pStPage = pNxtPage;
            pNxtPage = pTmp;
        }
 
        sal_Int32 nFirst = -1, nSecond = -1;
        for ( int nC = 0; nC < 2; ++nC )
        {
            sal_Int32 nPage = -1;
            if ( pStPage )
                nPage = pStPage->GetPhyPageNum();
            if (nC == 0)
                nFirst  = nPage;
            else
                nSecond = nPage;
 
            pStPage = pNxtPage;
        }
        rPagePairs.emplace_back(nFirst, nSecond );
 
        nSPg = nSPg + nStep;
        nEPg = nEPg - nStep;
    }
    OSL_ENSURE( size_t(nCntPage) == rPagePairs.size(), "size mismatch for number of page pairs" );
 
    // luckily prospect printing does not make use of post-its so far,
    // thus we are done here.
}
 
/// @return the reference in the doc for the name
const SwFormatRefMark* SwDoc::GetRefMark( std::u16string_view rName ) const
{
    const SwFormatRefMark* pRet = nullptr;
    ForEachRefMark(
        [&pRet, &rName] (const SwFormatRefMark& rRefMark) -> bool
        {
            const SwTextRefMark* pTextRef = rRefMark.GetTextRefMark();
            if( pTextRef && rName == rRefMark.GetRefName() )
            {
                pRet = &rRefMark;
                return false;
            }
            return true;
        });
    return pRet;
}
 
/// @return the RefMark per index - for Uno
const SwFormatRefMark* SwDoc::GetRefMark( sal_uInt16 nIndex ) const
{
    const SwFormatRefMark* pRet = nullptr;
 
    sal_uInt32 nCount = 0;
    ForEachRefMark(
        [&nCount, &pRet, &nIndex] (const SwFormatRefMark& rRefMark) -> bool
        {
            if(nCount == nIndex)
            {
                pRet = &rRefMark;
                return false;
            }
            nCount++;
            return true;
        });
    return pRet;
}
 
/// @return the names of all set references in the Doc
//JP 24.06.96: If the array pointer is 0, then just return whether a RefMark is set in the Doc
// OS 25.06.96: From now on we always return the reference count
sal_uInt16 SwDoc::GetRefMarks( std::vector<OUString>* pNames ) const
{
    sal_uInt16 nCount = 0;
    ForEachRefMark(
        [&pNames, &nCount] (const SwFormatRefMark& rRefMark) -> bool
        {
            if( pNames )
            {
                OUString aTmp(rRefMark.GetRefName());
                pNames->insert(pNames->begin() + nCount, aTmp);
            }
            ++nCount;
            return true;
        });
 
    return nCount;
}
 
void SwDoc::GetRefMarks( std::vector<const SwFormatRefMark*>& rMarks ) const
{
    ForEachRefMark(
        [&rMarks] (const SwFormatRefMark& rRefMark) -> bool
        {
            rMarks.push_back(&rRefMark);
            return true;
        });
}
 
/// Iterate over all SwFormatRefMark, if the function returns false, iteration is stopped
void SwDoc::ForEachRefMark( const std::function<bool(const SwFormatRefMark&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        SwNode* pNode = GetNodes()[i];
        if (!pNode->IsTextNode())
            continue;
        SwTextNode* pTextNode = pNode->GetTextNode();
        if (!pTextNode->HasHints())
            continue;
        SwpHints& rHints = pTextNode->GetSwpHints();
        for (size_t j = 0; j < rHints.Count(); ++j)
        {
            const SwTextAttr* pTextAttr = rHints.Get(j);
            if (pTextAttr->Which() != RES_TXTATR_REFMARK)
                continue;
            const SwFormatRefMark& rRefMark = pTextAttr->GetRefMark();
            if (!rFunc(rRefMark))
                return;
        }
    }
}
 
void SwDoc::DeleteFormatRefMark(const SwFormatRefMark* pFormatRefMark)
{
    const SwTextRefMark* pTextRefMark = pFormatRefMark->GetTextRefMark();
    SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextRefMark->GetTextNode());
    std::unique_ptr<SwRegHistory> aRegHistory;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        SwUndoResetAttr* pUndo = new SwUndoResetAttr(SwPosition(rTextNd, pTextRefMark->GetStart()),
                                                     RES_TXTATR_REFMARK);
        GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
        aRegHistory.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
        rTextNd.GetpSwpHints()->Register(aRegHistory.get());
    }
    rTextNd.DeleteAttribute(const_cast<SwTextRefMark*>(pTextRefMark));
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        if (rTextNd.GetpSwpHints())
            rTextNd.GetpSwpHints()->DeRegister();
    }
    getIDocumentState().SetModified();
}
 
static bool lcl_SpellAndGrammarAgain( SwNode* pNd, void* pArgs )
{
    SwTextNode *pTextNode = pNd->GetTextNode();
    bool bOnlyWrong = *static_cast<sal_Bool*>(pArgs);
    if( pTextNode )
    {
        if( bOnlyWrong )
        {
            if( pTextNode->GetWrong() &&
                pTextNode->GetWrong()->InvalidateWrong() )
                pTextNode->SetWrongDirty(sw::WrongState::TODO);
            if( pTextNode->GetGrammarCheck() &&
                pTextNode->GetGrammarCheck()->InvalidateWrong() )
                pTextNode->SetGrammarCheckDirty( true );
        }
        else
        {
            pTextNode->SetWrongDirty(sw::WrongState::TODO);
            if( pTextNode->GetWrong() )
                pTextNode->GetWrong()->SetInvalid( 0, COMPLETE_STRING );
            pTextNode->SetGrammarCheckDirty( true );
            if( pTextNode->GetGrammarCheck() )
                pTextNode->GetGrammarCheck()->SetInvalid( 0, COMPLETE_STRING );
        }
    }
    return true;
}
 
static bool lcl_CheckSmartTagsAgain( SwNode* pNd, void*  )
{
    SwTextNode *pTextNode = pNd->GetTextNode();
    if( pTextNode )
    {
        pTextNode->SetSmartTagDirty( true );
        pTextNode->ClearSmartTags();
    }
    return true;
}
 
/// Iterate over all SvxOverlineItem, if the function returns false, iteration is stopped
void SwDoc::ForEachOverlineItem( const std::function<bool(const SvxOverlineItem&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        SwNode* pNode = GetNodes()[i];
        if (!pNode->IsTextNode())
            continue;
        SwTextNode* pTextNode = pNode->GetTextNode();
        const SwAttrSet& rAttrSet = pTextNode->GetSwAttrSet();
        if (const SvxOverlineItem* pItem = rAttrSet.GetItemIfSet(RES_CHRATR_OVERLINE, false))
            if (!rFunc(*pItem))
                return;
        if (pTextNode->HasHints())
        {
            SwpHints& rHints = pTextNode->GetSwpHints();
            for (size_t j = 0; j < rHints.Count(); ++j)
            {
                const SwTextAttr* pTextAttr = rHints.Get(j);
                if (pTextAttr->Which() != RES_TXTATR_AUTOFMT)
                    continue;
                const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
                const std::shared_ptr<SfxItemSet> & rxItemSet = rAutoFormat.GetStyleHandle();
                if (const SvxOverlineItem* pItem = rxItemSet->GetItemIfSet(RES_CHRATR_OVERLINE, false))
                    if (!rFunc(*pItem))
                        return;
            }
        }
    }
    const auto& aTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap();
    const SwTableAutoFormatTable& rTableStyles = GetTableStyles();
    for (size_t i=0; i < rTableStyles.size(); ++i)
    {
        const SwTableAutoFormat& rTableStyle = rTableStyles[i];
        for (const sal_uInt32 nBoxIndex : aTableTemplateMap)
        {
            const SwBoxAutoFormat& rBoxFormat = rTableStyle.GetBoxFormat(nBoxIndex);
            const SvxOverlineItem rOverlineItem = rBoxFormat.GetOverline();
            if (!rFunc(rOverlineItem))
                return;
        }
    }
    const SwCellStyleTable& rCellStyleTable = GetCellStyles();
    for (size_t i=0; i < rCellStyleTable.size(); ++i)
    {
        const SwCellStyleDescriptor aCellStyle = rCellStyleTable[i];
        const SwBoxAutoFormat& rBoxFormat = aCellStyle.GetAutoFormat();
        const SvxOverlineItem rOverlineItem = rBoxFormat.GetOverline();
        if (!rFunc(rOverlineItem))
            return;
    }
}
 
/// Iterate over all SwFormatField, if the function returns false, iteration is stopped
void SwDoc::ForEachFormatField( TypedWhichId<SwFormatField> nWhich, const std::function<bool(const SwFormatField&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        SwNode* pNode = GetNodes()[i];
        if (!pNode->IsTextNode())
            continue;
        SwTextNode* pTextNode = pNode->GetTextNode();
        if (!pTextNode->HasHints())
            continue;
        SwpHints& rHints = pTextNode->GetSwpHints();
        for (size_t j = 0; j < rHints.Count(); ++j)
        {
            const SwTextAttr* pTextAttr = rHints.Get(j);
            if (pTextAttr->Which() != nWhich)
                continue;
            const SwFormatField& rFormatField = pTextAttr->GetFormatField();
            if (!rFunc(rFormatField))
                return;
        }
    }
}
 
/**
 * Re-trigger spelling in the idle handler.
 *
 * @param bInvalid if <true>, the WrongLists in all nodes are invalidated
 *                 and the SpellInvalid flag is set on all pages.
 * @param bOnlyWrong controls whether only the areas with wrong words are
 *                   checked or the whole area.
 * @param bSmartTags ???
 */
void SwDoc::SpellItAgainSam( bool bInvalid, bool bOnlyWrong, bool bSmartTags )
{
    o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
    assert(getIDocumentLayoutAccess().GetCurrentLayout() && "SpellAgain: Where's my RootFrame?");
    if( bInvalid )
    {
        for ( auto aLayout : aAllLayouts )
        {
            aLayout->AllInvalidateSmartTagsOrSpelling(bSmartTags);
            aLayout->SetNeedGrammarCheck(true);
        }
        if ( bSmartTags )
            GetNodes().ForEach( lcl_CheckSmartTagsAgain, &bOnlyWrong );
        GetNodes().ForEach( lcl_SpellAndGrammarAgain, &bOnlyWrong );
    }
 
    for ( auto aLayout : aAllLayouts )
        aLayout->SetIdleFlags();
}
 
void SwDoc::InvalidateAutoCompleteFlag()
{
    SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
    if( !pTmpRoot )
        return;
 
    o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
    for( auto aLayout : aAllLayouts )
        aLayout->AllInvalidateAutoCompleteWords();
    for( SwNodeOffset nNd(1), nCnt = GetNodes().Count(); nNd < nCnt; ++nNd )
    {
        SwTextNode* pTextNode = GetNodes()[ nNd ]->GetTextNode();
        if ( pTextNode ) pTextNode->SetAutoCompleteWordDirty( true );
    }
 
    for( auto aLayout : aAllLayouts )
        aLayout->SetIdleFlags();
}
 
const SwFormatINetFormat* SwDoc::FindINetAttr( std::u16string_view rName ) const
{
    const SwFormatINetFormat* pRet = nullptr;
    ForEachINetFormat(
        [&pRet, &rName] (const SwFormatINetFormat& rFormatItem) -> bool
        {
            if( rFormatItem.GetName() == rName )
            {
                pRet = &rFormatItem;
                return false;
            };
            return true;
        });
    return pRet;
}
 
/// Iterate over all SwFormatINetFormat, if the function returns false, iteration is stopped
void SwDoc::ForEachINetFormat( const std::function<bool(const SwFormatINetFormat&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        SwNode* pNode = GetNodes()[i];
        if (!pNode->IsTextNode())
            continue;
        SwTextNode* pTextNode = pNode->GetTextNode();
        if (!pTextNode->HasHints())
            continue;
        SwpHints& rHints = pTextNode->GetSwpHints();
        for (size_t j = 0; j < rHints.Count(); ++j)
        {
            const SwTextAttr* pTextAttr = rHints.Get(j);
            if (pTextAttr->Which() != RES_TXTATR_INETFMT)
                continue;
            const SwFormatINetFormat& rFormat = pTextAttr->GetINetFormat();
            if (!rFunc(rFormat))
                return;
        }
    }
}
 
/// Iterate over all SwFormatURL, if the function returns false, iteration is stopped
void SwDoc::ForEachFormatURL( const std::function<bool(const SwFormatURL&)>& rFunc ) const
{
    for(sw::SpzFrameFormat* pSpz : *GetSpzFrameFormats())
    {
        if (pSpz->Which() != RES_FLYFRMFMT)
            continue;
        auto pFormat = static_cast<SwFlyFrameFormat*>(pSpz);
        const SwFormatURL& rURLItem = pFormat->GetURL();
        if (!rFunc(rURLItem))
            return;
    }
}
 
namespace
{
/// Iterate over all pool item of type T, if the function returns false, iteration is stopped
template<typename T>
void ForEachCharacterItem(const SwDoc* pDoc, TypedWhichId<T> nWhich, const std::function<bool(const T&)>& rFunc )
{
    for(SwCharFormat* pFormat : *pDoc->GetCharFormats())
    {
        const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
        if (const T* pColorItem = rAttrSet.GetItemIfSet(nWhich))
            if (!rFunc(*pColorItem))
                return;
    }
    std::vector<std::shared_ptr<SfxItemSet>> aStyles;
    for (auto eFamily : { IStyleAccess::AUTO_STYLE_CHAR, IStyleAccess::AUTO_STYLE_RUBY, IStyleAccess::AUTO_STYLE_PARA, IStyleAccess::AUTO_STYLE_NOTXT })
    {
        const_cast<SwDoc*>(pDoc)->GetIStyleAccess().getAllStyles(aStyles, eFamily);
        for (const auto & rxItemSet : aStyles)
            if (const T* pColorItem = rxItemSet->GetItemIfSet(nWhich))
                if (!rFunc(*pColorItem))
                    return;
    }
}
}
 
/// Iterate over all SwFormatURL, if the function returns false, iteration is stopped
void SwDoc::ForEachCharacterBoxItem( const std::function<bool(const SvxBoxItem&)>& rFunc ) const
{
    ForEachCharacterItem(this, RES_CHRATR_BOX, rFunc);
}
 
/// Iterate over all SvxColorItem, if the function returns false, iteration is stopped
void SwDoc::ForEachCharacterColorItem( const std::function<bool(const SvxColorItem&)>& rFunc ) const
{
    ForEachCharacterItem(this, RES_CHRATR_COLOR, rFunc);
}
 
/// Iterate over all SvxUnderlineItem, if the function returns false, iteration is stopped
void SwDoc::ForEachCharacterUnderlineItem( const std::function<bool(const SvxUnderlineItem&)>& rFunc ) const
{
    ForEachCharacterItem(this, RES_CHRATR_UNDERLINE, rFunc);
}
 
/// Iterate over all SvxBrushItem, if the function returns false, iteration is stopped
void SwDoc::ForEachCharacterBrushItem( const std::function<bool(const SvxBrushItem&)>& rFunc ) const
{
    ForEachCharacterItem(this, RES_CHRATR_BACKGROUND, rFunc);
}
 
/// Iterate over all RES_TXTATR_UNKNOWN_CONTAINER SvXMLAttrContainerItem, if the function returns false, iteration is stopped
void SwDoc::ForEachTxtAtrContainerItem(const std::function<bool(const SvXMLAttrContainerItem&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        SwNode* pNode = GetNodes()[i];
        if (!pNode->IsTextNode())
            continue;
        SwTextNode* pTextNode = pNode->GetTextNode();
        if (!pTextNode->HasHints())
            continue;
        SwpHints& rHints = pTextNode->GetSwpHints();
        for (size_t j = 0; j < rHints.Count(); ++j)
        {
            const SwTextAttr* pTextAttr = rHints.Get(j);
            if (pTextAttr->Which() != RES_TXTATR_AUTOFMT)
                continue;
            const SwFormatAutoFormat& rFmt = pTextAttr->GetAutoFormat();
            if (const SvXMLAttrContainerItem* pItem = rFmt.GetStyleHandle()->GetItemIfSet(RES_TXTATR_UNKNOWN_CONTAINER))
                if (!rFunc(*pItem))
                    return;
        }
    }
}
 
/// Iterate over all RES_CHRATR_FONT/RES_CHRATR_CJK_FONT/RES_CHRATR_CTL_FONT SvxFontItem, if the function returns false, iteration is stopped
void SwDoc::ForEachCharacterFontItem(TypedWhichId<SvxFontItem> nWhich, bool bIgnoreAutoStyles, const std::function<bool(const SvxFontItem&)>& rFunc )
{
    assert(nWhich == RES_CHRATR_FONT || nWhich == RES_CHRATR_CJK_FONT || nWhich == RES_CHRATR_CTL_FONT);
    for(const SwCharFormat* pFormat : *GetCharFormats())
    {
        const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
        if (const SvxFontItem* pItem = rAttrSet.GetItemIfSet(nWhich))
            if (!rFunc(*pItem))
                return;
    }
    for(const SwTextFormatColl* pFormat : *GetTextFormatColls())
    {
        const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
        if (const SvxFontItem* pItem = rAttrSet.GetItemIfSet(nWhich))
            if (!rFunc(*pItem))
                return;
    }
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        const SwNode* pNode = GetNodes()[i];
        if (pNode->IsContentNode())
        {
            const SwContentNode* pTextNode = pNode->GetContentNode();
            if (pTextNode->HasSwAttrSet())
                if (const SvxFontItem* pItem = pTextNode->GetSwAttrSet().GetItemIfSet(nWhich))
                    if (!rFunc(*pItem))
                        return;
        }
    }
    // ignore auto styles when called from the code that is constructing the auto style pool
    if (!bIgnoreAutoStyles)
    {
        // auto styles
        std::vector<std::shared_ptr<SfxItemSet>> aStyles;
        GetIStyleAccess().getAllStyles(aStyles, IStyleAccess::AUTO_STYLE_CHAR);
        for (const auto & rxItemSet : aStyles)
            if (const SvxFontItem* pItem = rxItemSet->GetItemIfSet(nWhich))
                if (!rFunc(*pItem))
                    return;
    }
}
 
/// Iterate over all RES_PARATR_TABSTOP SvxTabStopItem, if the function returns false, iteration is stopped
void SwDoc::ForEachParaAtrTabStopItem(const std::function<bool(const SvxTabStopItem&)>& rFunc )
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        const SwNode* pNode = GetNodes()[i];
        if (pNode->IsContentNode())
        {
            const SwContentNode* pTextNode = pNode->GetContentNode();
            if (pTextNode->HasSwAttrSet())
                if (const SvxTabStopItem* pItem = pTextNode->GetSwAttrSet().GetItemIfSet(RES_PARATR_TABSTOP))
                    if (!rFunc(*pItem))
                        return;
        }
    }
    for(const SwTextFormatColl* pFormat : *GetTextFormatColls())
    {
        const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
        if (const SvxTabStopItem* pItem = rAttrSet.GetItemIfSet(RES_PARATR_TABSTOP))
            if (!rFunc(*pItem))
                return;
    }
}
 
/// Iterate over all RES_UNKNOWNATR_CONTAINER SvXMLAttrContainerItem, if the function returns false, iteration is stopped
void SwDoc::ForEachUnknownAtrContainerItem(const std::function<bool(const SvXMLAttrContainerItem&)>& rFunc ) const
{
    for(SwFrameFormat* pFormat : *GetFrameFormats())
    {
        const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
        if (const SvXMLAttrContainerItem* pItem = rAttrSet.GetItemIfSet(RES_UNKNOWNATR_CONTAINER))
            if (!rFunc(*pItem))
                return;
    }
}
 
/// Iterate over all RES_BOX SvxBoxItem, if the function returns false, iteration is stopped
void SwDoc::ForEachBoxItem(const std::function<bool(const SvxBoxItem&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        const SwNode* pNode = GetNodes()[i];
        if (pNode->IsContentNode())
        {
            const SwContentNode* pTextNode = pNode->GetContentNode();
            if (pTextNode->HasSwAttrSet())
                if (const SvxBoxItem* pItem = pTextNode->GetSwAttrSet().GetItemIfSet(RES_BOX))
                    if (!rFunc(*pItem))
                        return;
        }
    }
}
 
/// Iterate over all RES_SHADOW SvxBoxItem, if the function returns false, iteration is stopped
void SwDoc::ForEachShadowItem(const std::function<bool(const SvxShadowItem&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        const SwNode* pNode = GetNodes()[i];
        if (pNode->IsContentNode())
        {
            const SwContentNode* pTextNode = pNode->GetContentNode();
            if (pTextNode->HasSwAttrSet())
                if (const SvxShadowItem* pItem = pTextNode->GetSwAttrSet().GetItemIfSet(RES_SHADOW))
                    if (!rFunc(*pItem))
                        return;
        }
    }
}
 
/// Iterate over all RES_BACKGROUND SvxBrushItem, if the function returns false, iteration is stopped
void SwDoc::ForEachBackgroundBrushItem(const std::function<bool(const SvxBrushItem&)>& rFunc ) const
{
    SwNodeOffset nCount = GetNodes().Count();
    for (SwNodeOffset i(0); i < nCount; ++i)
    {
        const SwNode* pNode = GetNodes()[i];
        if (!pNode->IsTableNode())
            continue;
        const SwTableNode* pTableNode = pNode->GetTableNode();
        const SwTable& rTable = pTableNode->GetTable();
        if (const SwTableFormat* pFormat = rTable.GetFrameFormat())
        {
            const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
            if (const SvxBrushItem* pItem = rAttrSet.GetItemIfSet(RES_BACKGROUND))
                if (!rFunc(*pItem))
                    return;
        }
        for (const SwTableLine* pTableLine : rTable.GetTabLines())
        {
            if (const SwTableLineFormat* pFormat = pTableLine->GetFrameFormat())
            {
                const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
                if (const SvxBrushItem* pItem = rAttrSet.GetItemIfSet(RES_BACKGROUND))
                    if (!rFunc(*pItem))
                        return;
            }
            for (const SwTableBox* pTableBox : pTableLine->GetTabBoxes())
                if (SwTableBoxFormat* pFormat = pTableBox->GetFrameFormat())
                {
                    const SwAttrSet& rAttrSet = pFormat->GetAttrSet();
                    if (const SvxBrushItem* pItem = rAttrSet.GetItemIfSet(RES_BACKGROUND))
                        if (!rFunc(*pItem))
                            return;
                }
        }
    }
}
 
void SwDoc::Summary(SwDoc& rExtDoc, sal_uInt8 nLevel, sal_uInt8 nPara, bool bImpress)
{
    const SwOutlineNodes& rOutNds = GetNodes().GetOutLineNds();
    if (rOutNds.empty())
        return;
 
    ::StartProgress( STR_STATSTR_SUMMARY, 0, rOutNds.size(), GetDocShell() );
    SwNodeIndex aEndOfDoc( rExtDoc.GetNodes().GetEndOfContent(), -1 );
    for( SwOutlineNodes::size_type i = 0; i < rOutNds.size(); ++i )
    {
        ::SetProgressState( static_cast<tools::Long>(i), GetDocShell() );
        const SwNodeOffset nIndex = rOutNds[ i ]->GetIndex();
 
        const int nLvl = GetNodes()[ nIndex ]->GetTextNode()->GetAttrOutlineLevel()-1;
        if( nLvl > nLevel )
            continue;
        SwNodeOffset nEndOfs(1);
        sal_uInt8 nWish = nPara;
        SwNodeOffset nNextOutNd = i + 1 < rOutNds.size() ?
            rOutNds[ i + 1 ]->GetIndex() : GetNodes().Count();
        bool bKeep = false;
        while( ( nWish || bKeep ) && nIndex + nEndOfs < nNextOutNd &&
               GetNodes()[ nIndex + nEndOfs ]->IsTextNode() )
        {
            SwTextNode* pTextNode = GetNodes()[ nIndex+nEndOfs ]->GetTextNode();
            if (pTextNode->GetText().getLength() && nWish)
                --nWish;
            bKeep = pTextNode->GetSwAttrSet().GetKeep().GetValue();
            ++nEndOfs;
        }
 
        SwNodeRange aRange( *rOutNds[ i ], SwNodeOffset(0), *rOutNds[ i ], nEndOfs );
        GetNodes().Copy_( aRange, aEndOfDoc.GetNode() );
    }
    const SwTextFormatColls *pColl = rExtDoc.GetTextFormatColls();
    for( SwTextFormatColls::size_type i = 0; i < pColl->size(); ++i )
        (*pColl)[ i ]->ResetFormatAttr( RES_PAGEDESC, RES_BREAK );
    SwNodeIndex aIndx( rExtDoc.GetNodes().GetEndOfExtras() );
    ++aEndOfDoc;
    while( aIndx < aEndOfDoc )
    {
        bool bDelete = false;
        SwNode *pNode = &aIndx.GetNode();
        if( pNode->IsTextNode() )
        {
            SwTextNode *pNd = pNode->GetTextNode();
            if( pNd->HasSwAttrSet() )
                pNd->ResetAttr( RES_PAGEDESC, RES_BREAK );
            if( bImpress )
            {
                SwTextFormatColl* pMyColl = pNd->GetTextColl();
 
                const sal_uInt16 nHeadLine = o3tl::narrowing<sal_uInt16>(
                            !pMyColl->IsAssignedToListLevelOfOutlineStyle()
                            ? RES_POOLCOLL_HEADLINE2
                            : RES_POOLCOLL_HEADLINE1 );
                pMyColl = rExtDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nHeadLine );
                pNd->ChgFormatColl( pMyColl );
            }
            if( !pNd->Len() &&
                pNd->StartOfSectionIndex()+SwNodeOffset(2) < pNd->EndOfSectionIndex() )
            {
                bDelete = true;
                rExtDoc.GetNodes().Delete( aIndx );
            }
        }
        if( !bDelete )
            ++aIndx;
    }
    ::EndProgress( GetDocShell() );
}
 
namespace
{
void RemoveOrDeleteContents(SwTextNode* pTextNd, IDocumentContentOperations& xOperations)
{
    SwPaM aPam(*pTextNd, 0, *pTextNd, pTextNd->GetText().getLength());
 
    // Remove hidden paragraph or delete contents:
    // Delete contents if
    // 1. removing the paragraph would result in an empty section or
    // 2. if the paragraph is the last paragraph in the section and
    //    there is no paragraph in front of the paragraph:
    if ((SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
        || (SwNodeOffset(1) == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex()
            && !pTextNd->GetNodes()[pTextNd->GetIndex() - 1]->GetTextNode()))
    {
        xOperations.DeleteRange(aPam);
    }
    else
    {
        aPam.DeleteMark();
        xOperations.DelFullPara(aPam);
    }
}
// Returns if the data was actually modified
bool HandleHidingField(SwFormatField& rFormatField, const SwNodes& rNodes,
                       IDocumentContentOperations& xOperations)
{
    if( !rFormatField.GetTextField() )
        return false;
    SwTextNode* pTextNd = rFormatField.GetTextField()->GetpTextNode();
    if( pTextNd
        && pTextNd->GetpSwpHints() && pTextNd->IsHiddenByParaField()
        && &pTextNd->GetNodes() == &rNodes)
    {
        RemoveOrDeleteContents(pTextNd, xOperations);
        return true;
    }
    return false;
}
}
 
// The greater the returned value, the more weight has this field type on deciding the final
// paragraph state
int SwDoc::FieldCanHideParaWeight(SwFieldIds eFieldId) const
{
    switch (eFieldId)
    {
        case SwFieldIds::HiddenPara:
            return 20;
        case SwFieldIds::Database:
            return GetDocumentSettingManager().get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA)
                       ? 10
                       : 0;
        default:
            return 0;
    }
}
 
bool SwDoc::FieldHidesPara(const SwField& rField) const
{
    switch (rField.GetTyp()->Which())
    {
        case SwFieldIds::HiddenPara:
            return static_cast<const SwHiddenParaField&>(rField).IsHidden();
        case SwFieldIds::Database:
            return FieldCanHideParaWeight(SwFieldIds::Database)
                   && rField.ExpandField(true, nullptr).isEmpty();
        default:
            return false;
    }
}
 
/// Remove the invisible content from the document e.g. hidden areas, hidden paragraphs
// Returns if the data was actually modified
bool SwDoc::RemoveInvisibleContent()
{
    bool bRet = false;
    GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
 
    {
        class FieldTypeGuard : public SwClient
        {
        public:
            explicit FieldTypeGuard(SwFieldType* pType)
                : SwClient(pType)
            {
            }
            const SwFieldType* get() const
            {
                return static_cast<const SwFieldType*>(GetRegisteredIn());
            }
        };
        // Removing some nodes for one SwFieldIds::Database type might remove the type from
        // document's field types, invalidating iterators. So, we need to create own list of
        // matching types prior to processing them.
        std::vector<std::unique_ptr<FieldTypeGuard>> aHidingFieldTypes;
        for (std::unique_ptr<SwFieldType> const & pType : *getIDocumentFieldsAccess().GetFieldTypes())
        {
            if (FieldCanHideParaWeight(pType->Which()))
                aHidingFieldTypes.push_back(std::make_unique<FieldTypeGuard>(pType.get()));
        }
        for (const auto& pTypeGuard : aHidingFieldTypes)
        {
            if (const SwFieldType* pType = pTypeGuard->get())
            {
                std::vector<SwFormatField*> vFields;
                pType->GatherFields(vFields);
                for(auto pFormatField: vFields)
                    bRet |= HandleHidingField(*pFormatField, GetNodes(), getIDocumentContentOperations());
            }
        }
    }
 
    // Remove any hidden paragraph (hidden text attribute)
    for( SwNodeOffset n = GetNodes().Count(); n; )
    {
        SwTextNode* pTextNd = GetNodes()[ --n ]->GetTextNode();
        if ( pTextNd )
        {
            bool bRemoved = false;
            if ( pTextNd->HasHiddenCharAttribute( true ) )
            {
                bRemoved = true;
                bRet = true;
 
                if (SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
                {
                    SwFrameFormat *const pFormat = pTextNd->StartOfSectionNode()->GetFlyFormat();
                    if (nullptr != pFormat)
                    {
                        // remove hidden text frame
                        getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
                    }
                    else
                    {
                        // default, remove hidden paragraph
                        RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
                    }
                }
                else
                {
                    // default, remove hidden paragraph
                    RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
                }
            }
            else if ( pTextNd->HasHiddenCharAttribute( false ) )
            {
                bRemoved = true;
                bRet = true;
                SwScriptInfo::DeleteHiddenRanges( *pTextNd );
            }
 
            // Footnotes/Frames may have been removed, therefore we have
            // to reset n:
            if ( bRemoved )
            {
                // [n] has to be inside [0 .. GetNodes().Count()] range
                if (n > GetNodes().Count())
                    n = GetNodes().Count();
            }
        }
    }
 
    {
        // Delete/empty all hidden areas
        o3tl::sorted_vector<SwSectionFormat*> aSectFormats;
        SwSectionFormats& rSectFormats = GetSections();
 
        for( SwSectionFormats::size_type n = rSectFormats.size(); n; )
        {
            SwSectionFormat* pSectFormat = rSectFormats[ --n ];
            // don't add sections in Undo/Redo
            if( !pSectFormat->IsInNodesArr())
                continue;
            SwSection* pSect = pSectFormat->GetSection();
            if( pSect->CalcHiddenFlag() )
            {
                SwSection* pParent = pSect, *pTmp;
                while( nullptr != (pTmp = pParent->GetParent() ))
                {
                    if( pTmp->IsHiddenFlag() )
                        pSect = pTmp;
                    pParent = pTmp;
                }
 
                aSectFormats.insert( pSect->GetFormat() );
            }
            if( !pSect->GetCondition().isEmpty() )
            {
                SwSectionData aSectionData( *pSect );
                aSectionData.SetCondition( OUString() );
                aSectionData.SetHidden( false );
                UpdateSection( n, aSectionData );
            }
        }
 
        auto n = aSectFormats.size();
 
        if( 0 != n )
        {
            while( n )
            {
                SwSectionFormat* pSectFormat = aSectFormats[ --n ];
                SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
                if( pSectNd )
                {
                    bRet = true;
                    SwPaM aPam( *pSectNd );
 
                    if( pSectNd->StartOfSectionNode()->StartOfSectionIndex() ==
                        pSectNd->GetIndex() - 1 &&
                        pSectNd->StartOfSectionNode()->EndOfSectionIndex() ==
                        pSectNd->EndOfSectionIndex() + 1 )
                    {
                        // only delete the content
                        SwContentNode* pCNd = SwNodes::GoNext(aPam.GetPoint());
                        aPam.SetMark();
                        aPam.GetPoint()->Assign( *pSectNd->EndOfSectionNode() );
                        pCNd = SwNodes::GoPrevious( aPam.GetPoint() );
                        assert(pCNd); // keep coverity happy
                        aPam.GetPoint()->SetContent( pCNd->Len() );
 
                        getIDocumentContentOperations().DeleteRange( aPam );
                    }
                    else
                    {
                        // delete the whole section
                        aPam.SetMark();
                        aPam.GetPoint()->Assign( *pSectNd->EndOfSectionNode() );
                        getIDocumentContentOperations().DelFullPara( aPam );
                    }
 
                }
            }
        }
    }
 
    if( bRet )
        getIDocumentState().SetModified();
    GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
    return bRet;
}
 
bool SwDoc::HasInvisibleContent() const
{
    std::vector<SwFormatField*> vFields;
    getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara)->GatherFields(vFields);
    if(vFields.size())
        return true;
 
    // Search for any hidden paragraph (hidden text attribute)
    for( SwNodeOffset n = GetNodes().Count()-SwNodeOffset(1); n; --n)
    {
        SwTextNode* pTextNd = GetNodes()[ n ]->GetTextNode();
        if ( pTextNd &&
             ( pTextNd->HasHiddenCharAttribute( true ) || pTextNd->HasHiddenCharAttribute( false ) ) )
            return true;
    }
 
    for(auto pSectFormat : GetSections())
    {
        // don't add sections in Undo/Redo
        if( !pSectFormat->IsInNodesArr())
            continue;
        SwSection* pSect = pSectFormat->GetSection();
        if( pSect->IsHidden() )
            return true;
    }
    return false;
}
 
bool SwDoc::RestoreInvisibleContent()
{
    SwUndoId nLastUndoId(SwUndoId::EMPTY);
    if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
        && (SwUndoId::UI_DELETE_INVISIBLECNTNT == nLastUndoId))
    {
        GetIDocumentUndoRedo().Undo();
        GetIDocumentUndoRedo().ClearRedo();
        return true;
    }
    return false;
}
 
static bool IsMailMergeField(SwFieldIds fieldId)
{
    switch (fieldId)
    {
        case SwFieldIds::Database: // Mail merge fields
        case SwFieldIds::DatabaseName: // Database name
        case SwFieldIds::HiddenText: // Hidden text may use database fields in condition
        case SwFieldIds::HiddenPara: // Hidden paragraph may use database fields in condition
        case SwFieldIds::DbNextSet: // Moving to next mail merge record
        case SwFieldIds::DbNumSet: // Moving to a specific mail merge record
        case SwFieldIds::DbSetNumber: // Number of current mail merge record
            return true;
        default:
            return false;
    }
}
 
bool SwDoc::ConvertFieldsToText(SwRootFrame const& rLayout)
{
    bool bRet = false;
    getIDocumentFieldsAccess().LockExpFields();
    GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE, nullptr );
 
    const bool bOnlyConvertDBFields
        = officecfg::Office::Writer::FormLetter::ConvertToTextOnlyMMFields::get();
 
    const SwFieldTypes* pMyFieldTypes = getIDocumentFieldsAccess().GetFieldTypes();
    const SwFieldTypes::size_type nCount = pMyFieldTypes->size();
    //go backward, field types are removed
    for(SwFieldTypes::size_type nType = nCount; nType > 0; --nType)
    {
        const SwFieldType *pCurType = (*pMyFieldTypes)[nType - 1].get();
 
        if ( SwFieldIds::Postit == pCurType->Which() )
            continue;
 
        if (bOnlyConvertDBFields && !IsMailMergeField(pCurType->Which()))
            continue;
 
        std::vector<SwFormatField*> vFieldFormats;
        pCurType->GatherFields(vFieldFormats, false);
        for(const auto& rpFieldFormat : vFieldFormats)
        {
            const SwTextField *pTextField = rpFieldFormat->GetTextField();
            // skip fields that are currently not in the document
            // e.g. fields in undo or redo array
 
            bool bSkip = !pTextField ||
                         !pTextField->GetpTextNode()->GetNodes().IsDocNodes();
            if (bSkip)
                continue;
 
            bool bInHeaderFooter = IsInHeaderFooter(*pTextField->GetpTextNode());
            const SwFormatField& rFormatField = pTextField->GetFormatField();
            const SwField*  pField = rFormatField.GetField();
 
            //#i55595# some fields have to be excluded in headers/footers
            SwFieldIds nWhich = pField->GetTyp()->Which();
            if(!bInHeaderFooter ||
                    (nWhich != SwFieldIds::PageNumber &&
                    nWhich != SwFieldIds::Chapter &&
                    nWhich != SwFieldIds::GetExp&&
                    nWhich != SwFieldIds::SetExp&&
                    nWhich != SwFieldIds::Input&&
                    nWhich != SwFieldIds::RefPageGet&&
                    nWhich != SwFieldIds::RefPageSet))
            {
                OUString sText = pField->ExpandField(true, &rLayout);
 
                // database fields should not convert their command into text
                if( SwFieldIds::Database == pCurType->Which() && !static_cast<const SwDBField*>(pField)->IsInitialized())
                    sText.clear();
 
                SwPaM aInsertPam(*pTextField->GetpTextNode(), pTextField->GetStart());
                aInsertPam.SetMark();
 
                // go to the end of the field
                const SwTextField *pFieldAtEnd = sw::DocumentFieldsManager::GetTextFieldAtPos(*aInsertPam.End());
                if (pFieldAtEnd && pFieldAtEnd->Which() == RES_TXTATR_INPUTFIELD)
                {
                    SwPosition &rEndPos = *aInsertPam.GetPoint();
                    rEndPos.SetContent( SwCursorShell::EndOfInputFieldAtPos( *aInsertPam.End() ) );
                }
                else
                {
                    aInsertPam.Move();
                }
 
                // first insert the text after field to keep the field's attributes,
                // then delete the field
                if (!sText.isEmpty())
                {
                    // to keep the position after insert
                    SwPaM aDelPam( *aInsertPam.GetMark(), *aInsertPam.GetPoint() );
                    aDelPam.Move( fnMoveBackward );
                    aInsertPam.DeleteMark();
 
                    getIDocumentContentOperations().InsertString( aInsertPam, sText );
 
                    aDelPam.Move();
                    // finally remove the field
                    getIDocumentContentOperations().DeleteAndJoin( aDelPam );
                }
                else
                {
                    getIDocumentContentOperations().DeleteAndJoin( aInsertPam );
                }
 
                bRet = true;
            }
        }
    }
 
    if( bRet )
        getIDocumentState().SetModified();
    GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_REPLACE, nullptr );
    getIDocumentFieldsAccess().UnlockExpFields();
    return bRet;
 
}
 
bool SwDoc::IsInsTableFormatNum() const
{
    return SW_MOD()->IsInsTableFormatNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
}
 
bool SwDoc::IsInsTableChangeNumFormat() const
{
    return SW_MOD()->IsInsTableChangeNumFormat(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
}
 
bool SwDoc::IsInsTableAlignNum() const
{
    return SW_MOD()->IsInsTableAlignNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
}
 
bool SwDoc::IsSplitVerticalByDefault() const
{
    return SW_MOD()->IsSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
}
 
void SwDoc::SetSplitVerticalByDefault(bool value)
{
    SW_MOD()->SetSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE), value);
}
 
/// Set up the InsertDB as Undo table
void SwDoc::AppendUndoForInsertFromDB( const SwPaM& rPam, bool bIsTable )
{
    if( bIsTable )
    {
        const SwTableNode* pTableNd = rPam.GetPoint()->GetNode().FindTableNode();
        if( pTableNd )
        {
            std::unique_ptr<SwUndoCpyTable> pUndo(new SwUndoCpyTable(*this));
            pUndo->SetTableSttIdx( pTableNd->GetIndex() );
            GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
        }
    }
    else if( rPam.HasMark() )
    {
        std::unique_ptr<SwUndoCpyDoc> pUndo(new SwUndoCpyDoc( rPam ));
        pUndo->SetInsertRange( rPam, false );
        GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
    }
}
 
void SwDoc::ChangeTOX(SwTOXBase & rTOX, const SwTOXBase & rNew)
{
    assert(dynamic_cast<const SwTOXBaseSection*>(&rTOX));
    SwTOXBaseSection& rTOXSect(static_cast<SwTOXBaseSection&>(rTOX));
 
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo(
            std::make_unique<SwUndoTOXChange>(*this, rTOXSect, rNew));
    }
 
    rTOX = rNew;
 
    // note: do not Update the ToX here - the caller will do it, with a ViewShell!
}
 
OUString SwDoc::GetPaMDescr(const SwPaM & rPam)
{
    if (&rPam.GetPointNode() == &rPam.GetMarkNode())
    {
        SwTextNode * pTextNode = rPam.GetPointNode().GetTextNode();
 
        if (nullptr != pTextNode)
        {
            const sal_Int32 nStart = rPam.Start()->GetContentIndex();
            const sal_Int32 nEnd = rPam.End()->GetContentIndex();
 
            return SwResId(STR_START_QUOTE)
                + ShortenString(pTextNode->GetText().copy(nStart, nEnd - nStart),
                                nUndoStringLength,
                                SwResId(STR_LDOTS))
                + SwResId(STR_END_QUOTE);
        }
    }
    else
    {
        return SwResId(STR_PARAGRAPHS);
    }
 
    return u"??"_ustr;
}
 
bool SwDoc::ContainsHiddenChars() const
{
    for( SwNodeOffset n = GetNodes().Count(); n; )
    {
        SwNode* pNd = GetNodes()[ --n ];
        if ( pNd->IsTextNode() && pNd->GetTextNode()->HasHiddenCharAttribute( false ) )
            return true;
    }
 
    return false;
}
 
std::shared_ptr<SwUnoCursor> SwDoc::CreateUnoCursor( const SwPosition& rPos, bool bTableCursor )
{
    std::shared_ptr<SwUnoCursor> pNew;
    if( bTableCursor )
        pNew = std::make_shared<SwUnoTableCursor>(rPos);
    else
        pNew = std::make_shared<SwUnoCursor>(rPos);
 
    mvUnoCursorTable.push_back( pNew );
    return pNew;
}
 
void SwDoc::ChkCondColls()
{
     for (SwTextFormatColls::size_type n = 0; n < mpTextFormatCollTable->size(); ++n)
     {
        SwTextFormatColl *pColl = (*mpTextFormatCollTable)[n];
        if (RES_CONDTXTFMTCOLL == pColl->Which())
            pColl->CallSwClientNotify( SwAttrHint() );
     }
}
 
uno::Reference< script::vba::XVBAEventProcessor > const &
SwDoc::GetVbaEventProcessor()
{
    return mxVbaEvents;
}
 
void SwDoc::SetVbaEventProcessor()
{
#if HAVE_FEATURE_SCRIPTING
    if (mpDocShell && ooo::vba::isAlienWordDoc(*mpDocShell))
    {
        try
        {
            uno::Reference< frame::XModel > xModel( mpDocShell->GetModel(), uno::UNO_SET_THROW );
            uno::Sequence< uno::Any > aArgs{ uno::Any(xModel) };
            mxVbaEvents.set( ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "com.sun.star.script.vba.VBATextEventProcessor" , aArgs ), uno::UNO_QUERY_THROW );
        }
        catch( uno::Exception& )
        {
        }
    }
#endif
}
 
void SwDoc::SetMissingDictionaries( bool bIsMissing )
{
    if (!bIsMissing)
        meDictionaryMissing = MissingDictionary::False;
    else if (meDictionaryMissing == MissingDictionary::Undefined)
        meDictionaryMissing = MissingDictionary::True;
};
 
void SwDoc::SetLanguage(const LanguageType eLang, const sal_uInt16 nId)
{
    mpAttrPool->SetUserDefaultItem(SvxLanguageItem(eLang, nId));
}
 
bool SwDoc::HasParagraphDirectFormatting(const SwPosition& rPos)
{
    rtl::Reference<SwXTextRange> xRange(SwXTextRange::CreateXTextRange(rPos.GetDoc(), rPos,
                                                                           &rPos));
    uno::Reference<container::XEnumeration> xParaEnum = xRange->createEnumeration();
    uno::Reference<text::XTextRange> xThisParagraphRange(xParaEnum->nextElement(), uno::UNO_QUERY);
    if (xThisParagraphRange.is())
    {
        const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID,
                    UNO_NAME_PARA_IS_NUMBERING_RESTART,
                    UNO_NAME_PARA_STYLE_NAME,
                    UNO_NAME_PARA_CONDITIONAL_STYLE_NAME,
                    UNO_NAME_PAGE_STYLE_NAME,
                    UNO_NAME_NUMBERING_START_VALUE,
                    UNO_NAME_NUMBERING_IS_NUMBER,
                    UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE,
                    UNO_NAME_CHAR_STYLE_NAME,
                    UNO_NAME_NUMBERING_LEVEL,
                    UNO_NAME_SORTED_TEXT_ID,
                    UNO_NAME_PARRSID,
                    UNO_NAME_CHAR_COLOR_THEME,
                    UNO_NAME_CHAR_COLOR_TINT_OR_SHADE };
 
        SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(
                                               PROPERTY_MAP_PARA_AUTO_STYLE));
        SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
 
        uno::Reference<beans::XPropertySet> xPropertySet(xThisParagraphRange,
                                                         uno::UNO_QUERY_THROW);
        uno::Reference<beans::XPropertyState> xPropertyState(xThisParagraphRange,
                                                             uno::UNO_QUERY_THROW);
        const uno::Sequence<beans::Property> aProperties
                = xPropertySet->getPropertySetInfo()->getProperties();
        for (const beans::Property& rProperty : aProperties)
        {
            const OUString& rPropName = rProperty.Name;
            if (!rMap.hasPropertyByName(rPropName))
                continue;
            if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName)
                    != aHiddenProperties.end())
                continue;
            if (xPropertyState->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE)
                return true;
        }
    }
    return false;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.