    /* -*- 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 <memory>
    #include <MarkManager.hxx>
    #include <bookmrk.hxx>
    #include <cntfrm.hxx>
    #include <crossrefbookmark.hxx>
    #include <annotationmark.hxx>
    #include <dcontact.hxx>
    #include <doc.hxx>
    #include <IDocumentRedlineAccess.hxx>
    #include <IDocumentState.hxx>
    #include <IDocumentUndoRedo.hxx>
    #include <docary.hxx>
    #include <xmloff/odffields.hxx>
    #include <editsh.hxx>
    #include <fmtanchr.hxx>
    #include <frmfmt.hxx>
    #include <functional>
    #include <hintids.hxx>
    #include <mvsave.hxx>
    #include <ndtxt.hxx>
    #include <node.hxx>
    #include <pam.hxx>
    #include <redline.hxx>
    #include <rolbck.hxx>
    #include <o3tl/make_unique.hxx>
    #include <rtl/ustrbuf.hxx>
    #include <rtl/ustring.hxx>
    #include <sal/types.h>
    #include <sal/log.hxx>
    #include <sortedobjs.hxx>
    #include <sfx2/linkmgr.hxx>
    #include <swserv.hxx>
    #include <swundo.hxx>
    #include <UndoBookmark.hxx>
    #include <unocrsr.hxx>
    #include <viscrs.hxx>
    #include <edimp.hxx>
    #include <tools/datetimeutils.hxx>

    using namespace ::sw::mark;

    namespace
    {
        bool lcl_GreaterThan( const SwPosition& rPos, const SwNodeIndex& rNdIdx, const SwIndex* pIdx )
        {
            return pIdx != nullptr
                   ? ( rPos.nNode > rNdIdx
                       || ( rPos.nNode == rNdIdx
                            && rPos.nContent >= pIdx->GetIndex() ) )
                   : rPos.nNode >= rNdIdx;
        }

        bool lcl_Lower( const SwPosition& rPos, const SwNodeIndex& rNdIdx, const SwIndex* pIdx )
        {
            return rPos.nNode < rNdIdx
                   || ( pIdx != nullptr
                        && rPos.nNode == rNdIdx
                        && rPos.nContent < pIdx->GetIndex() );
        }

        bool lcl_MarkOrderingByStart(const IDocumentMarkAccess::pMark_t& rpFirst,
            const IDocumentMarkAccess::pMark_t& rpSecond)
        {
            auto const& rFirstStart(rpFirst->GetMarkStart());
            auto const& rSecondStart(rpSecond->GetMarkStart());
            if (rFirstStart.nNode != rSecondStart.nNode)
            {
                return rFirstStart.nNode < rSecondStart.nNode;
            }
            const sal_Int32 nFirstContent = rFirstStart.nContent.GetIndex();
            const sal_Int32 nSecondContent = rSecondStart.nContent.GetIndex();
            if (nFirstContent != 0 || nSecondContent != 0)
            {
                return nFirstContent < nSecondContent;
            }
            auto *const pCRFirst (dynamic_cast<::sw::mark::CrossRefBookmark const*>(rpFirst.get()));
            auto *const pCRSecond(dynamic_cast<::sw::mark::CrossRefBookmark const*>(rpSecond.get()));
            if ((pCRFirst == nullptr) == (pCRSecond == nullptr))
            {
                return false; // equal
            }
            return pCRFirst != nullptr; // cross-ref sorts *before*
        }

        bool lcl_MarkOrderingByEnd(const IDocumentMarkAccess::pMark_t& rpFirst,
            const IDocumentMarkAccess::pMark_t& rpSecond)
        {
            return rpFirst->GetMarkEnd() < rpSecond->GetMarkEnd();
        }

        void lcl_InsertMarkSorted(IDocumentMarkAccess::container_t& io_vMarks,
            const IDocumentMarkAccess::pMark_t& pMark)
        {
            io_vMarks.insert(
                lower_bound(
                    io_vMarks.begin(),
                    io_vMarks.end(),
                    pMark,
                    &lcl_MarkOrderingByStart),
                pMark);
        }

        inline std::unique_ptr<SwPosition> lcl_PositionFromContentNode(
            SwContentNode * const pContentNode,
            const bool bAtEnd)
        {
            std::unique_ptr<SwPosition> pResult(new SwPosition(*pContentNode));
            pResult->nContent.Assign(pContentNode, bAtEnd ? pContentNode->Len() : 0);
            return pResult;
        }

        // return a position at the begin of rEnd, if it is a ContentNode
        // else set it to the begin of the Node after rEnd, if there is one
        // else set it to the end of the node before rStt
        // else set it to the ContentNode of the Pos outside the Range
        inline std::unique_ptr<SwPosition> lcl_FindExpelPosition(
            const SwNodeIndex& rStt,
            const SwNodeIndex& rEnd,
            const SwPosition& rOtherPosition)
        {
            SwContentNode * pNode = rEnd.GetNode().GetContentNode();
            bool bPosAtEndOfNode = false;
            if ( pNode == nullptr)
            {
                SwNodeIndex aEnd = SwNodeIndex(rEnd);
                pNode = rEnd.GetNodes().GoNext( &aEnd );
                bPosAtEndOfNode = false;
            }
            if ( pNode == nullptr )
            {
                SwNodeIndex aStt = SwNodeIndex(rStt);
                pNode = SwNodes::GoPrevious(&aStt);
                bPosAtEndOfNode = true;
            }
            if ( pNode != nullptr )
            {
                return lcl_PositionFromContentNode( pNode, bPosAtEndOfNode );
            }

            return o3tl::make_unique<SwPosition>(rOtherPosition);
        }

        IMark* lcl_getMarkAfter(const IDocumentMarkAccess::container_t& rMarks, const SwPosition& rPos)
        {
            IDocumentMarkAccess::const_iterator_t pMarkAfter = upper_bound(
                rMarks.begin(),
                rMarks.end(),
                rPos,
                sw::mark::CompareIMarkStartsAfter());
            if(pMarkAfter == rMarks.end()) return nullptr;
            return pMarkAfter->get();
        };

        IMark* lcl_getMarkBefore(const IDocumentMarkAccess::container_t& rMarks, const SwPosition& rPos)
        {
            // candidates from which to choose the mark before
            IDocumentMarkAccess::container_t vCandidates;
            // no need to consider marks starting after rPos
            IDocumentMarkAccess::const_iterator_t pCandidatesEnd = upper_bound(
                rMarks.begin(),
                rMarks.end(),
                rPos,
                sw::mark::CompareIMarkStartsAfter());
            vCandidates.reserve(pCandidatesEnd - rMarks.begin());
            // only marks ending before are candidates
            remove_copy_if(
                rMarks.begin(),
                pCandidatesEnd,
                back_inserter(vCandidates),
                [&rPos] (IDocumentMarkAccess::pMark_t const& rpMark) { return !rpMark->EndsBefore(rPos); } );
            // no candidate left => we are in front of the first mark or there are none
            if(vCandidates.empty()) return nullptr;
            // return the highest (last) candidate using mark end ordering
            return max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd)->get();
        }

        bool lcl_FixCorrectedMark(
            const bool bChangedPos,
            const bool bChangedOPos,
            MarkBase* io_pMark )
        {
            if ( IDocumentMarkAccess::GetType(*io_pMark) == IDocumentMarkAccess::MarkType::ANNOTATIONMARK )
            {
                // annotation marks are allowed to span a table cell range.
                // but trigger sorting to be save
                return true;
            }

            if ( ( bChangedPos || bChangedOPos )
                 && io_pMark->IsExpanded()
                 && io_pMark->GetOtherMarkPos().nNode.GetNode().FindTableBoxStartNode() !=
                        io_pMark->GetMarkPos().nNode.GetNode().FindTableBoxStartNode() )
            {
                if ( !bChangedOPos )
                {
                    io_pMark->SetMarkPos( io_pMark->GetOtherMarkPos() );
                }
                io_pMark->ClearOtherMarkPos();
                DdeBookmark * const pDdeBkmk = dynamic_cast< DdeBookmark*>(io_pMark);
                if ( pDdeBkmk != nullptr
                     && pDdeBkmk->IsServer() )
                {
                    pDdeBkmk->SetRefObject(nullptr);
                }
                return true;
            }
            return false;
        }

        bool lcl_MarkEqualByStart(const IDocumentMarkAccess::pMark_t& rpFirst,
                                  const IDocumentMarkAccess::pMark_t& rpSecond)
        {
            return !lcl_MarkOrderingByStart(rpFirst, rpSecond) &&
                   !lcl_MarkOrderingByStart(rpSecond, rpFirst);
        }

        IDocumentMarkAccess::iterator_t lcl_FindMark(
            IDocumentMarkAccess::container_t& rMarks,
            const IDocumentMarkAccess::pMark_t& rpMarkToFind)
        {
            IDocumentMarkAccess::iterator_t ppCurrentMark = lower_bound(
                rMarks.begin(), rMarks.end(),
                rpMarkToFind, &lcl_MarkOrderingByStart);
            // since there are usually not too many marks on the same start
            // position, we are not doing a bisect search for the upper bound
            // but instead start to iterate from pMarkLow directly
            while (ppCurrentMark != rMarks.end() && lcl_MarkEqualByStart(*ppCurrentMark, rpMarkToFind))
            {
                if(ppCurrentMark->get() == rpMarkToFind.get())
                {
                    return ppCurrentMark;
                }
                ++ppCurrentMark;
            }
            // reached a mark starting on a later start pos or the end of the
            // vector => not found
            return rMarks.end();
        };

        IDocumentMarkAccess::iterator_t lcl_FindMarkAtPos(
            IDocumentMarkAccess::container_t& rMarks,
            const SwPosition& rPos,
            const IDocumentMarkAccess::MarkType eType)
        {
            for(IDocumentMarkAccess::iterator_t ppCurrentMark = lower_bound(
                    rMarks.begin(), rMarks.end(),
                    rPos,
                    sw::mark::CompareIMarkStartsBefore());
                ppCurrentMark != rMarks.end();
                ++ppCurrentMark)
            {
                // Once we reach a mark starting after the target pos
                // we do not need to continue
                if(ppCurrentMark->get()->StartsAfter(rPos))
                    break;
                if(IDocumentMarkAccess::GetType(**ppCurrentMark) == eType)
                {
                    return ppCurrentMark;
                }
            }
            // reached a mark starting on a later start pos or the end of the
            // vector => not found
            return rMarks.end();
        };

        IDocumentMarkAccess::const_iterator_t lcl_FindMarkByName(
            const OUString& rName,
            const IDocumentMarkAccess::const_iterator_t& ppMarksBegin,
            const  IDocumentMarkAccess::const_iterator_t& ppMarksEnd)
        {
            return find_if(
                ppMarksBegin,
                ppMarksEnd,
                [&rName] (IDocumentMarkAccess::pMark_t const& rpMark) { return rpMark->GetName() == rName; } );
        }

        void lcl_DebugMarks(IDocumentMarkAccess::container_t const& rMarks)
        {
    #if OSL_DEBUG_LEVEL > 0
            SAL_INFO("sw.core", rMarks.size() << " Marks");
            for (IDocumentMarkAccess::const_iterator_t ppMark = rMarks.begin();
                 ppMark != rMarks.end();
                 ++ppMark)
            {
                IMark* pMark = ppMark->get();
                const SwPosition* const pStPos = &pMark->GetMarkStart();
                const SwPosition* const pEndPos = &pMark->GetMarkEnd();
                SAL_INFO("sw.core",
                    pStPos->nNode.GetIndex() << "," <<
                    pStPos->nContent.GetIndex() << " " <<
                    pEndPos->nNode.GetIndex() << "," <<
                    pEndPos->nContent.GetIndex() << " " <<
                    typeid(*pMark).name() << " " <<
                    pMark->GetName());
            }
    #else
            (void) rMarks;
    #endif
            assert(std::is_sorted(rMarks.begin(), rMarks.end(), lcl_MarkOrderingByStart));
        };
    }

    IDocumentMarkAccess::MarkType IDocumentMarkAccess::GetType(const IMark& rBkmk)
    {
        const std::type_info* const pMarkTypeInfo = &typeid(rBkmk);
        // not using dynamic_cast<> here for performance
        if(*pMarkTypeInfo == typeid(UnoMark))
            return MarkType::UNO_BOOKMARK;
        else if(*pMarkTypeInfo == typeid(DdeBookmark))
            return MarkType::DDE_BOOKMARK;
        else if(*pMarkTypeInfo == typeid(Bookmark))
            return MarkType::BOOKMARK;
        else if(*pMarkTypeInfo == typeid(CrossRefHeadingBookmark))
            return MarkType::CROSSREF_HEADING_BOOKMARK;
        else if(*pMarkTypeInfo == typeid(CrossRefNumItemBookmark))
            return MarkType::CROSSREF_NUMITEM_BOOKMARK;
        else if(*pMarkTypeInfo == typeid(AnnotationMark))
            return MarkType::ANNOTATIONMARK;
        else if(*pMarkTypeInfo == typeid(TextFieldmark))
            return MarkType::TEXT_FIELDMARK;
        else if(*pMarkTypeInfo == typeid(CheckboxFieldmark))
            return MarkType::CHECKBOX_FIELDMARK;
        else if(*pMarkTypeInfo == typeid(NavigatorReminder))
            return MarkType::NAVIGATOR_REMINDER;
        else
        {
            assert(false && "IDocumentMarkAccess::GetType(..)"
                " - unknown MarkType. This needs to be fixed!");
            return MarkType::UNO_BOOKMARK;
        }
    }

    OUString IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()
    {
        return OUString("__RefHeading__");
    }

    bool IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( const SwPaM& rPaM )
    {
        return rPaM.Start()->nNode.GetNode().IsTextNode() &&
               rPaM.Start()->nContent.GetIndex() == 0 &&
               ( !rPaM.HasMark() ||
                 ( rPaM.GetMark()->nNode == rPaM.GetPoint()->nNode &&
                   rPaM.End()->nContent.GetIndex() == rPaM.End()->nNode.GetNode().GetTextNode()->Len() ) );
    }

    namespace sw { namespace mark
    {
        MarkManager::MarkManager(SwDoc& rDoc)
            : m_vAllMarks()
            , m_vBookmarks()
            , m_vFieldmarks()
            , m_vAnnotationMarks()
            , m_pDoc(&rDoc)
        { }

        ::sw::mark::IMark* MarkManager::makeMark(const SwPaM& rPaM,
            const OUString& rName,
            const IDocumentMarkAccess::MarkType eType,
            sw::mark::InsertMode const eMode)
        {
    #if OSL_DEBUG_LEVEL > 0
            {
                const SwPosition* const pPos1 = rPaM.GetPoint();
                const SwPosition* pPos2 = pPos1;
                if(rPaM.HasMark())
                    pPos2 = rPaM.GetMark();
                SAL_INFO("sw.core",
                    rName << " " <<
                    pPos1->nNode.GetIndex() << "," <<
                    pPos1->nContent.GetIndex() << " " <<
                    pPos2->nNode.GetIndex() << "," <<
                    pPos2->nContent.GetIndex());
            }
    #endif
            // see for example _SaveContentIdx, Shells
            OSL_PRECOND(m_vAllMarks.size() < USHRT_MAX,
                "MarkManager::makeMark(..)"
                " - more than USHRT_MAX marks are not supported correctly");
            // There should only be one CrossRefBookmark per Textnode per Type
            if ((eType == MarkType::CROSSREF_NUMITEM_BOOKMARK || eType == MarkType::CROSSREF_HEADING_BOOKMARK)
                && (lcl_FindMarkAtPos(m_vBookmarks, *rPaM.Start(), eType) != m_vBookmarks.end()))
            {   // this can happen via UNO API
                SAL_WARN("sw.core", "MarkManager::makeMark(..)"
                    " - refusing to create duplicate CrossRefBookmark");
                return nullptr;
            }

            // create mark
            pMark_t pMark;
            switch(eType)
            {
                case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
                    pMark = std::shared_ptr<IMark>(new TextFieldmark(rPaM));
                    break;
                case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
                    pMark = std::shared_ptr<IMark>(new CheckboxFieldmark(rPaM));
                    break;
                case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
                    pMark = std::shared_ptr<IMark>(new NavigatorReminder(rPaM));
                    break;
                case IDocumentMarkAccess::MarkType::BOOKMARK:
                    pMark = std::shared_ptr<IMark>(new Bookmark(rPaM, vcl::KeyCode(), rName));
                    break;
                case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
                    pMark = std::shared_ptr<IMark>(new DdeBookmark(rPaM));
                    break;
                case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
                    pMark = std::shared_ptr<IMark>(new CrossRefHeadingBookmark(rPaM, vcl::KeyCode(), rName));
                    break;
                case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
                    pMark = std::shared_ptr<IMark>(new CrossRefNumItemBookmark(rPaM, vcl::KeyCode(), rName));
                    break;
                case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
                    pMark = std::shared_ptr<IMark>(new UnoMark(rPaM));
                    break;
                case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
                    pMark = std::shared_ptr<IMark>(new AnnotationMark( rPaM, rName ));
                    break;
            }
            assert(pMark.get() &&
                "MarkManager::makeMark(..)"
                " - Mark was not created.");
            MarkBase* pMarkBase = dynamic_cast<MarkBase*>(pMark.get());

            if (!pMarkBase)
                return nullptr;

            if(pMark->GetMarkPos() != pMark->GetMarkStart())
                pMarkBase->Swap();

            // for performance reasons, we trust UnoMarks to have a (generated) unique name
            if ( eType != IDocumentMarkAccess::MarkType::UNO_BOOKMARK )
                pMarkBase->SetName( getUniqueMarkName( pMarkBase->GetName() ) );

            // register mark
            m_aMarkNamesSet.insert(pMarkBase->GetName());
            lcl_InsertMarkSorted(m_vAllMarks, pMark);
            switch(eType)
            {
                case IDocumentMarkAccess::MarkType::BOOKMARK:
                case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
                case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
                    lcl_InsertMarkSorted(m_vBookmarks, pMark);
                    break;
                case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
                case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
                    lcl_InsertMarkSorted(m_vFieldmarks, pMark);
                    break;
                case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
                    lcl_InsertMarkSorted( m_vAnnotationMarks, pMark );
                    break;
                case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
                case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
                case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
                    // no special array for these
                    break;
            }
            pMarkBase->InitDoc(m_pDoc, eMode);
            SAL_INFO("sw.core", "--- makeType ---");
            SAL_INFO("sw.core", "Marks");
            lcl_DebugMarks(m_vAllMarks);
            SAL_INFO("sw.core", "Bookmarks");
            lcl_DebugMarks(m_vBookmarks);
            SAL_INFO("sw.core", "Fieldmarks");
            lcl_DebugMarks(m_vFieldmarks);

            return pMark.get();
        }

        ::sw::mark::IFieldmark* MarkManager::makeFieldBookmark(
            const SwPaM& rPaM,
            const OUString& rName,
            const OUString& rType )
        {
            sw::mark::IMark* pMark = makeMark( rPaM, rName,
                    IDocumentMarkAccess::MarkType::TEXT_FIELDMARK,
                    sw::mark::InsertMode::New);
            sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
            if (pFieldMark)
                pFieldMark->SetFieldname( rType );

            return pFieldMark;
        }

        ::sw::mark::IFieldmark* MarkManager::makeNoTextFieldBookmark(
            const SwPaM& rPaM,
            const OUString& rName,
            const OUString& rType)
        {
            sw::mark::IMark* pMark = makeMark( rPaM, rName,
                    IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK,
                    sw::mark::InsertMode::New);
            sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
            if (pFieldMark)
                pFieldMark->SetFieldname( rType );

            return pFieldMark;
        }

        ::sw::mark::IMark* MarkManager::getMarkForTextNode(
            const SwTextNode& rTextNode,
            const IDocumentMarkAccess::MarkType eType )
        {
            SwPosition aPos(rTextNode);
            aPos.nContent.Assign(&const_cast<SwTextNode&>(rTextNode), 0);
            const iterator_t ppExistingMark = lcl_FindMarkAtPos(m_vBookmarks, aPos, eType);
            if(ppExistingMark != m_vBookmarks.end())
                return ppExistingMark->get();
            const SwPaM aPaM(aPos);
            return makeMark(aPaM, OUString(), eType, sw::mark::InsertMode::New);
        }

        sw::mark::IMark* MarkManager::makeAnnotationMark(
            const SwPaM& rPaM,
            const OUString& rName )
        {
            return makeMark(rPaM, rName, IDocumentMarkAccess::MarkType::ANNOTATIONMARK,
                    sw::mark::InsertMode::New);
        }

        void MarkManager::repositionMark(
            ::sw::mark::IMark* const io_pMark,
            const SwPaM& rPaM)
        {
            assert(io_pMark->GetMarkPos().GetDoc() == m_pDoc &&
                "<MarkManager::repositionMark(..)>"
                " - Mark is not in my doc.");
            MarkBase* const pMarkBase = dynamic_cast< MarkBase* >(io_pMark);
            if (!pMarkBase)
                return;

            pMarkBase->SetMarkPos(*(rPaM.GetPoint()));
            if(rPaM.HasMark())
                pMarkBase->SetOtherMarkPos(*(rPaM.GetMark()));
            else
                pMarkBase->ClearOtherMarkPos();

            if(pMarkBase->GetMarkPos() != pMarkBase->GetMarkStart())
                pMarkBase->Swap();

            sortMarks();
        }

        bool MarkManager::renameMark(
            ::sw::mark::IMark* io_pMark,
            const OUString& rNewName )
        {
            assert(io_pMark->GetMarkPos().GetDoc() == m_pDoc &&
                "<MarkManager::renameMark(..)>"
                " - Mark is not in my doc.");
            if ( io_pMark->GetName() == rNewName )
                return true;
            if ( findMark(rNewName) != m_vAllMarks.end() )
                return false;
            if (::sw::mark::MarkBase* pMarkBase = dynamic_cast< ::sw::mark::MarkBase* >(io_pMark))
            {
                const OUString sOldName(pMarkBase->GetName());
                m_aMarkNamesSet.erase(sOldName);
                m_aMarkNamesSet.insert(rNewName);
                pMarkBase->SetName(rNewName);

                if (dynamic_cast< ::sw::mark::Bookmark* >(io_pMark))
                {
                    if (m_pDoc->GetIDocumentUndoRedo().DoesUndo())
                    {
                        m_pDoc->GetIDocumentUndoRedo().AppendUndo(
                                new SwUndoRenameBookmark(sOldName, rNewName, m_pDoc));
                    }
                    m_pDoc->getIDocumentState().SetModified();
                }
            }
            return true;
        }

        void MarkManager::correctMarksAbsolute(
            const SwNodeIndex& rOldNode,
            const SwPosition& rNewPos,
            const sal_Int32 nOffset)
        {
            const SwNode* const pOldNode = &rOldNode.GetNode();
            SwPosition aNewPos(rNewPos);
            aNewPos.nContent += nOffset;
            bool isSortingNeeded = false;

            for(iterator_t ppMark = m_vAllMarks.begin();
                ppMark != m_vAllMarks.end();
                ++ppMark)
            {
                ::sw::mark::MarkBase* pMark = dynamic_cast< ::sw::mark::MarkBase* >(ppMark->get());
                // correction of non-existent non-MarkBase instances cannot be done
                assert(pMark);
                // is on position ??
                bool bChangedPos = false;
                if(&pMark->GetMarkPos().nNode.GetNode() == pOldNode)
                {
                    pMark->SetMarkPos(aNewPos);
                    bChangedPos = true;
                    isSortingNeeded = true;
                }
                bool bChangedOPos = false;
                if (pMark->IsExpanded() &&
                    &pMark->GetOtherMarkPos().nNode.GetNode() == pOldNode)
                {
                    // shift the OtherMark to aNewPos
                    pMark->SetOtherMarkPos(aNewPos);
                    bChangedOPos= true;
                    isSortingNeeded = true;
                }
                // illegal selection? collapse the mark and restore sorting later
                isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
            }

            // restore sorting if needed
            if(isSortingNeeded)
                sortMarks();

            SAL_INFO("sw.core", "correctMarksAbsolute");
            lcl_DebugMarks(m_vAllMarks);
        }

        void MarkManager::correctMarksRelative(const SwNodeIndex& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset)
        {
            const SwNode* const pOldNode = &rOldNode.GetNode();
            SwPosition aNewPos(rNewPos);
            aNewPos.nContent += nOffset;
            bool isSortingNeeded = false;

            for(iterator_t ppMark = m_vAllMarks.begin();
                ppMark != m_vAllMarks.end();
                ++ppMark)
            {
                // is on position ??
                bool bChangedPos = false, bChangedOPos = false;
                ::sw::mark::MarkBase* const pMark = dynamic_cast< ::sw::mark::MarkBase* >(ppMark->get());
                // correction of non-existent non-MarkBase instances cannot be done
                assert(pMark);
                if(&pMark->GetMarkPos().nNode.GetNode() == pOldNode)
                {
                    SwPosition aNewPosRel(aNewPos);
                    if (dynamic_cast< ::sw::mark::CrossRefBookmark *>(pMark))
                    {
                        // ensure that cross ref bookmark always starts at 0
                        aNewPosRel.nContent = 0; // HACK for WW8 import
                        isSortingNeeded = true; // and sort them to be safe...
                    }
                    aNewPosRel.nContent += pMark->GetMarkPos().nContent.GetIndex();
                    pMark->SetMarkPos(aNewPosRel);
                    bChangedPos = true;
                }
                if(pMark->IsExpanded() &&
                    &pMark->GetOtherMarkPos().nNode.GetNode() == pOldNode)
                {
                    SwPosition aNewPosRel(aNewPos);
                    aNewPosRel.nContent += pMark->GetOtherMarkPos().nContent.GetIndex();
                    pMark->SetOtherMarkPos(aNewPosRel);
                    bChangedOPos = true;
                }
                // illegal selection? collapse the mark and restore sorting later
                isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
            }

            // restore sorting if needed
            if(isSortingNeeded)
                sortMarks();

            SAL_INFO("sw.core", "correctMarksRelative");
            lcl_DebugMarks(m_vAllMarks);
        }

        void MarkManager::deleteMarks(
                const SwNodeIndex& rStt,
                const SwNodeIndex& rEnd,
                std::vector<SaveBookmark>* pSaveBkmk,
                const SwIndex* pSttIdx,
                const SwIndex* pEndIdx )
        {
            std::vector<const_iterator_t> vMarksToDelete;
            bool bIsSortingNeeded = false;

            // boolean indicating, if at least one mark has been moved while collecting marks for deletion
            bool bMarksMoved = false;
            // have marks in the range been skipped instead of deleted
            bool bMarksSkipDeletion = false;

            // copy all bookmarks in the move area to a vector storing all position data as offset
            // reassignment is performed after the move
            for(iterator_t ppMark = m_vAllMarks.begin();
                ppMark != m_vAllMarks.end();
                ++ppMark)
            {
                // navigator marks should not be moved
                // TODO: Check if this might make them invalid
                if(IDocumentMarkAccess::GetType(**ppMark) == MarkType::NAVIGATOR_REMINDER)
                    continue;

                ::sw::mark::MarkBase* pMark = dynamic_cast< ::sw::mark::MarkBase* >(ppMark->get());

                if (!pMark)
                    continue;

                // on position ??
                bool bIsPosInRange = lcl_GreaterThan(pMark->GetMarkPos(), rStt, pSttIdx)
                                     && lcl_Lower(pMark->GetMarkPos(), rEnd, pEndIdx);
                bool bIsOtherPosInRange = pMark->IsExpanded()
                                          && lcl_GreaterThan(pMark->GetOtherMarkPos(), rStt, pSttIdx)
                                          && lcl_Lower(pMark->GetOtherMarkPos(), rEnd, pEndIdx);
                // special case: completely in range, touching the end?
                if ( pEndIdx != nullptr
                     && ( ( bIsOtherPosInRange
                            && pMark->GetMarkPos().nNode == rEnd
                            && pMark->GetMarkPos().nContent == *pEndIdx )
                          || ( bIsPosInRange
                               && pMark->IsExpanded()
                               && pMark->GetOtherMarkPos().nNode == rEnd
                               && pMark->GetOtherMarkPos().nContent == *pEndIdx ) ) )
                {
                    bIsPosInRange = true;
                    bIsOtherPosInRange = true;
                }

                if ( bIsPosInRange
                     && ( bIsOtherPosInRange
                          || !pMark->IsExpanded() ) )
                {
                    // completely in range

                    bool bDeleteMark = true;
                    {
                        switch ( IDocumentMarkAccess::GetType( *pMark ) )
                        {
                        case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
                        case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
                            // no delete of cross-reference bookmarks, if range is inside one paragraph
                            bDeleteMark = rStt != rEnd;
                            break;
                        case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
                            // no delete of UNO mark, if it is not expanded and only touches the start of the range
                            bDeleteMark = bIsOtherPosInRange
                                          || pMark->IsExpanded()
                                          || pSttIdx == nullptr
                                          || !( pMark->GetMarkPos().nNode == rStt
                                                && pMark->GetMarkPos().nContent == *pSttIdx );
                            break;
                        default:
                            bDeleteMark = true;
                            break;
                        }
                    }

                    if ( bDeleteMark )
                    {
                        if ( pSaveBkmk )
                        {
                            pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, pSttIdx ) );
                        }
                        vMarksToDelete.emplace_back(ppMark);
                    }
                    else
                    {
                        bMarksSkipDeletion = true;
                    }
                }
                else if ( bIsPosInRange != bIsOtherPosInRange )
                {
                    // the bookmark is partially in the range
                    // move position of that is in the range out of it

                    std::unique_ptr< SwPosition > pNewPos;
                    {
                        if ( pEndIdx != nullptr )
                        {
                            pNewPos = o3tl::make_unique< SwPosition >( rEnd, *pEndIdx );
                        }
                        else
                        {
                            pNewPos =
                                lcl_FindExpelPosition( rStt, rEnd, bIsPosInRange ? pMark->GetOtherMarkPos() : pMark->GetMarkPos() );
                        }
                    }

                    bool bMoveMark = true;
                    {
                        switch ( IDocumentMarkAccess::GetType( *pMark ) )
                        {
                        case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
                        case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
                            // no move of cross-reference bookmarks, if move occurs inside a certain node
                            bMoveMark = pMark->GetMarkPos().nNode != pNewPos->nNode;
                            break;
                        case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
                            // no move of annotation marks, if method is called to collect deleted marks
                            bMoveMark = pSaveBkmk == nullptr;
                            break;
                        default:
                            bMoveMark = true;
                            break;
                        }
                    }
                    if ( bMoveMark )
                    {
                        if ( bIsPosInRange )
                            pMark->SetMarkPos(*pNewPos);
                        else
                            pMark->SetOtherMarkPos(*pNewPos);
                        bMarksMoved = true;

                        // illegal selection? collapse the mark and restore sorting later
                        bIsSortingNeeded |= lcl_FixCorrectedMark( bIsPosInRange, bIsOtherPosInRange, pMark );
                    }
                }
            }

            {
                // fdo#61016 delay the deletion of the fieldmark characters
                // to prevent that from deleting the marks on that position
                // which would invalidate the iterators in vMarksToDelete
                std::vector< std::shared_ptr<ILazyDeleter> > vDelay;
                vDelay.reserve(vMarksToDelete.size());

                // If needed, sort mark containers containing subsets of the marks
                // in order to assure sorting.  The sorting is critical for the
                // deletion of a mark as it is searched in these container for
                // deletion.
                if ( !vMarksToDelete.empty() && bMarksMoved )
                {
                    sortSubsetMarks();
                }
                // we just remembered the iterators to delete, so we do not need to search
                // for the shared_ptr<> (the entry in m_vAllMarks) again
                // reverse iteration, since erasing an entry invalidates iterators
                // behind it (the iterators in vMarksToDelete are sorted)
                for ( std::vector< const_iterator_t >::reverse_iterator pppMark = vMarksToDelete.rbegin();
                      pppMark != vMarksToDelete.rend();
                      ++pppMark )
                {
                    vDelay.push_back(deleteMark(*pppMark));
                }
            } // scope to kill vDelay

            // also need to sort if both marks were moved and not-deleted because
            // the not-deleted marks could be in wrong order vs. the moved ones
            if (bIsSortingNeeded || (bMarksMoved && bMarksSkipDeletion))
            {
                sortMarks();
            }

            SAL_INFO("sw.core", "deleteMarks");
            lcl_DebugMarks(m_vAllMarks);
        }

        struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
        {
            std::shared_ptr<IMark> const m_pFieldmark;
            SwDoc *const m_pDoc;
            LazyFieldmarkDeleter(
                    std::shared_ptr<IMark> const& pMark, SwDoc *const pDoc)
                : m_pFieldmark(pMark), m_pDoc(pDoc)
            { }
            virtual ~LazyFieldmarkDeleter() override
            {
                Fieldmark *const pFieldMark(
                        dynamic_cast<Fieldmark*>(m_pFieldmark.get()));
                assert(pFieldMark);
                pFieldMark->ReleaseDoc(m_pDoc);
            }
        };

        std::shared_ptr<IDocumentMarkAccess::ILazyDeleter>
            MarkManager::deleteMark(const const_iterator_t& ppMark)
        {
            std::shared_ptr<ILazyDeleter> ret;
            if (ppMark == m_vAllMarks.end()) return ret;

            switch(IDocumentMarkAccess::GetType(**ppMark))
            {
                case IDocumentMarkAccess::MarkType::BOOKMARK:
                case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
                case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
                    {
                        IDocumentMarkAccess::iterator_t ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark);
                        if ( ppBookmark != m_vBookmarks.end() )
                        {
                            m_vBookmarks.erase(ppBookmark);
                        }
                        else
                        {
                            assert(false &&
                                "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
                        }
                    }
                    break;

                case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
                case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
                    {
                        IDocumentMarkAccess::iterator_t ppFieldmark = lcl_FindMark(m_vFieldmarks, *ppMark);
                        if ( ppFieldmark != m_vFieldmarks.end() )
                        {
                            m_vFieldmarks.erase(ppFieldmark);
                            ret.reset(new LazyFieldmarkDeleter(*ppMark, m_pDoc));
                        }
                        else
                        {
                            assert(false &&
                                "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container.");
                        }
                    }
                    break;

                case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
                    {
                        IDocumentMarkAccess::iterator_t ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, *ppMark);
                        assert(ppAnnotationMark != m_vAnnotationMarks.end() &&
                            "<MarkManager::deleteMark(..)> - Annotation Mark not found in Annotation Mark container.");
                        m_vAnnotationMarks.erase(ppAnnotationMark);
                    }
                    break;

                case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
                case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
                case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
                    // no special marks container
                    break;
            }
            DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(ppMark->get());
            if(pDdeBookmark)
                pDdeBookmark->DeregisterFromDoc(m_pDoc);
            //Effective STL Item 27, get a non-const iterator aI at the same
            //position as const iterator ppMark was
            iterator_t aI = m_vAllMarks.begin();
            std::advance(aI, std::distance<const_iterator_t>(aI, ppMark));

            //fdo#37974
            //a) a mark destructor may callback into this method.
            //b) vector::erase first calls the destructor of the object, then
            //removes it from the vector.
            //So if the only reference to the object is the one
            //in the vector then we may reenter this method when the mark
            //is destructed but before it is removed, i.e. findMark still
            //finds the object whose destructor is being run. Take a temp
            //extra reference on the shared_ptr, remove the entry from the
            //vector, and on xHoldPastErase release findMark won't find
            //it anymore.
            pMark_t xHoldPastErase = *aI;
            m_aMarkNamesSet.erase(ppMark->get()->GetName());
            m_vAllMarks.erase(aI);
            return ret;
        }

        void MarkManager::deleteMark(const IMark* const pMark)
        {
            assert(pMark->GetMarkPos().GetDoc() == m_pDoc &&
                "<MarkManager::deleteMark(..)>"
                " - Mark is not in my doc.");
            // finds the last Mark that is starting before pMark
            // (pMarkLow < pMark)
            iterator_t pMarkLow =
                lower_bound(
                    m_vAllMarks.begin(),
                    m_vAllMarks.end(),
                    pMark->GetMarkStart(),
                    sw::mark::CompareIMarkStartsBefore());
            iterator_t pMarkHigh = m_vAllMarks.end();
            iterator_t pMarkFound =
                find_if(
                    pMarkLow,
                    pMarkHigh,
                    [pMark] (pMark_t const& rpMark) { return rpMark.get() == pMark; } );
            if(pMarkFound != pMarkHigh)
                deleteMark(pMarkFound);
        }

        void MarkManager::clearAllMarks()
        {
            m_vFieldmarks.clear();
            m_vBookmarks.clear();
            m_aMarkNamesSet.clear();

            m_vAnnotationMarks.clear();

    #if OSL_DEBUG_LEVEL > 0
            for(iterator_t pBkmk = m_vAllMarks.begin();
                pBkmk != m_vAllMarks.end();
                ++pBkmk)
                OSL_ENSURE( pBkmk->use_count() == 1,
                            "<MarkManager::clearAllMarks(..)> - a Bookmark is still in use.");
    #endif
            m_vAllMarks.clear();
        }

        IDocumentMarkAccess::const_iterator_t MarkManager::findMark(const OUString& rName) const
        {
            return lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end());
        }

        IDocumentMarkAccess::const_iterator_t MarkManager::findBookmark(const OUString& rName) const
        {
            return lcl_FindMarkByName(rName, m_vBookmarks.begin(), m_vBookmarks.end());
        }

        IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksBegin() const
            { return m_vAllMarks.begin(); }

        IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksEnd() const
            { return m_vAllMarks.end(); }

        sal_Int32 MarkManager::getAllMarksCount() const
            { return m_vAllMarks.size(); }

        IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksBegin() const
            { return m_vBookmarks.begin(); }

        IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksEnd() const
            { return m_vBookmarks.end(); }

        sal_Int32 MarkManager::getBookmarksCount() const
            { return m_vBookmarks.size(); }

        IFieldmark* MarkManager::getFieldmarkFor(const SwPosition& rPos) const
        {
            const_iterator_t pFieldmark = find_if(
                m_vFieldmarks.begin(),
                m_vFieldmarks.end(),
                [&rPos] (pMark_t const& rpMark) { return rpMark->IsCoveringPosition(rPos); } );
            if(pFieldmark == m_vFieldmarks.end()) return nullptr;
            return dynamic_cast<IFieldmark*>(pFieldmark->get());
        }

        IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const
        {
            IFieldmark *pMark = getFieldmarkFor(rPos);
            if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN)
                return nullptr;
            return pMark;
        }

        std::vector<IFieldmark*> MarkManager::getDropDownsFor(const SwPaM &rPaM) const
        {
            std::vector<IFieldmark*> aRet;

            for (IDocumentMarkAccess::const_iterator_t aI = m_vFieldmarks.begin(),
                aEnd = m_vFieldmarks.end(); aI != aEnd; ++aI)
            {
                std::shared_ptr<IMark> xI = *aI;
                const SwPosition &rStart = xI->GetMarkPos();
                if (!rPaM.ContainsPosition(rStart))
                    continue;

                IFieldmark *pMark = dynamic_cast<IFieldmark*>(xI.get());
                if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN)
                    continue;

                aRet.push_back(pMark);
            }

            return aRet;
        }

        IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos) const
            { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos)); }

        IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos) const
            { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos)); }

        IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksBegin() const
        {
            return m_vAnnotationMarks.begin();
        }

        IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksEnd() const
        {
            return m_vAnnotationMarks.end();
        }

        sal_Int32 MarkManager::getAnnotationMarksCount() const
        {
            return m_vAnnotationMarks.size();
        }

        IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationMark( const OUString& rName ) const
        {
            return lcl_FindMarkByName( rName, m_vAnnotationMarks.begin(), m_vAnnotationMarks.end() );
        }

        IMark* MarkManager::getAnnotationMarkFor(const SwPosition& rPos) const
        {
            const_iterator_t pAnnotationMark = find_if(
                m_vAnnotationMarks.begin(),
                m_vAnnotationMarks.end(),
                [&rPos] (pMark_t const& rpMark) { return rpMark->IsCoveringPosition(rPos); } );
            if (pAnnotationMark == m_vAnnotationMarks.end())
                return nullptr;
            return pAnnotationMark->get();
        }

        OUString MarkManager::getUniqueMarkName(const OUString& rName) const
        {
            OSL_ENSURE(rName.getLength(),
                "<MarkManager::getUniqueMarkName(..)> - a name should be proposed");
            if( m_pDoc->IsInMailMerge())
            {
                OUString newName = rName + "MailMergeMark"
                        + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
                        + OUString::number( m_vAllMarks.size() + 1 );
                return newName;
            }

            if ( findMark(rName) == getAllMarksEnd() )
            {
                return rName;
            }
            OUStringBuffer sBuf;
            OUString sTmp;

            // try the name "<rName>XXX" (where XXX is a number starting from 1) unless there is
            // a unused name. Due to performance-reasons (especially in mailmerge-scenarios) there
            // is a map m_aMarkBasenameMapUniqueOffset which holds the next possible offset (XXX) for
            // rName (so there is no need to test for nCnt-values smaller than the offset).
            sal_Int32 nCnt = 1;
            MarkBasenameMapUniqueOffset_t::const_iterator aIter = m_aMarkBasenameMapUniqueOffset.find(rName);
            if(aIter != m_aMarkBasenameMapUniqueOffset.end()) nCnt = aIter->second;
            while(nCnt < SAL_MAX_INT32)
            {
                sTmp = sBuf.append(rName).append(nCnt).makeStringAndClear();
                nCnt++;
                if ( findMark(sTmp) == getAllMarksEnd() )
                {
                    break;
                }
            }
            m_aMarkBasenameMapUniqueOffset[rName] = nCnt;

            return sTmp;
        }

        void MarkManager::assureSortedMarkContainers() const
        {
            const_cast< MarkManager* >(this)->sortMarks();
        }

        void MarkManager::sortSubsetMarks()
        {
            sort(m_vBookmarks.begin(), m_vBookmarks.end(), &lcl_MarkOrderingByStart);
            sort(m_vFieldmarks.begin(), m_vFieldmarks.end(), &lcl_MarkOrderingByStart);
            sort(m_vAnnotationMarks.begin(), m_vAnnotationMarks.end(), &lcl_MarkOrderingByStart);
        }

        void MarkManager::sortMarks()
        {
            sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart);
            sortSubsetMarks();
        }

    void MarkManager::dumpAsXml(xmlTextWriterPtr pWriter) const
    {
        struct
        {
            const char* pName;
            const container_t* pContainer;
        } aContainers[] =
        {
            // UNO marks are only part of all marks.
            {"allmarks", &m_vAllMarks},
            {"bookmarks", &m_vBookmarks},
            {"fieldmarks", &m_vFieldmarks},
            {"annotationmarks", &m_vAnnotationMarks}
        };

        xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager"));
        for (const auto & rContainer : aContainers)
        {
            if (!rContainer.pContainer->empty())
            {
                xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName));
                for (const_iterator_t it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it)
                    (*it)->dumpAsXml(pWriter);
                xmlTextWriterEndElement(pWriter);
            }
        }
        xmlTextWriterEndElement(pWriter);
    }

    }} // namespace ::sw::mark

    namespace
    {
        inline bool lcl_Greater( const SwPosition& rPos, const SwNodeIndex& rNdIdx, const SwIndex* pIdx )
        {
            return rPos.nNode > rNdIdx || ( pIdx && rPos.nNode == rNdIdx && rPos.nContent > pIdx->GetIndex() );
        }
    }

    // IDocumentMarkAccess for SwDoc
    IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess()
        { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }

    const IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess() const
        { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }

    SaveBookmark::SaveBookmark(
        const IMark& rBkmk,
        const SwNodeIndex & rMvPos,
        const SwIndex* pIdx)
        : m_aName(rBkmk.GetName())
        , m_aShortName()
        , m_aCode()
        , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk))
    {
        const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk);
        if(pBookmark)
        {
            m_aShortName = pBookmark->GetShortName();
            m_aCode = pBookmark->GetKeyCode();

            ::sfx2::Metadatable const*const pMetadatable(
                    dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
            if (pMetadatable)
            {
                m_pMetadataUndo = pMetadatable->CreateUndo();
            }
        }
        m_nNode1 = rBkmk.GetMarkPos().nNode.GetIndex();
        m_nContent1 = rBkmk.GetMarkPos().nContent.GetIndex();

        m_nNode1 -= rMvPos.GetIndex();
        if(pIdx && !m_nNode1)
            m_nContent1 -= pIdx->GetIndex();

        if(rBkmk.IsExpanded())
        {
            m_nNode2 = rBkmk.GetOtherMarkPos().nNode.GetIndex();
            m_nContent2 = rBkmk.GetOtherMarkPos().nContent.GetIndex();

            m_nNode2 -= rMvPos.GetIndex();
            if(pIdx && !m_nNode2)
                m_nContent2 -= pIdx->GetIndex();
        }
        else
        {
            m_nNode2 = ULONG_MAX;
            m_nContent2 = -1;
        }
    }

    void SaveBookmark::SetInDoc(
        SwDoc* pDoc,
        const SwNodeIndex& rNewPos,
        const SwIndex* pIdx)
    {
        SwPaM aPam(rNewPos.GetNode());
        if(pIdx)
            aPam.GetPoint()->nContent = *pIdx;

        if(ULONG_MAX != m_nNode2)
        {
            aPam.SetMark();

            aPam.GetMark()->nNode += m_nNode2;
            if(pIdx && !m_nNode2)
                aPam.GetMark()->nContent += m_nContent2;
            else
                aPam.GetMark()->nContent.Assign(aPam.GetContentNode(false), m_nContent2);
        }

        aPam.GetPoint()->nNode += m_nNode1;

        if(pIdx && !m_nNode1)
            aPam.GetPoint()->nContent += m_nContent1;
        else
            aPam.GetPoint()->nContent.Assign(aPam.GetContentNode(), m_nContent1);

        if(!aPam.HasMark()
            || CheckNodesRange(aPam.GetPoint()->nNode, aPam.GetMark()->nNode, true))
        {
            ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>(
                pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName,
                    m_eOrigBkmType, sw::mark::InsertMode::New));
            if(pBookmark)
            {
                pBookmark->SetKeyCode(m_aCode);
                pBookmark->SetShortName(m_aShortName);
                if (m_pMetadataUndo)
                {
                    ::sfx2::Metadatable * const pMeta(
                        dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
                    assert(pMeta && "metadata undo, but not metadatable?");
                    if (pMeta)
                    {
                        pMeta->RestoreMetadata(m_pMetadataUndo);
                    }
                }
            }
        }
    }

    // DelBookmarks

    void DelBookmarks(
        const SwNodeIndex& rStt,
        const SwNodeIndex& rEnd,
        std::vector<SaveBookmark> * pSaveBkmk,
        const SwIndex* pSttIdx,
        const SwIndex* pEndIdx)
    {
        // illegal range ??
        if(rStt.GetIndex() > rEnd.GetIndex()
            || (rStt == rEnd && (!pSttIdx || pSttIdx->GetIndex() >= pEndIdx->GetIndex())))
            return;
        SwDoc* const pDoc = rStt.GetNode().GetDoc();

        pDoc->getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk, pSttIdx, pEndIdx);

        // Copy all Redlines which are in the move area into an array
        // which holds all position information as offset.
        // Assignment happens after moving.
        SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
        for(SwRangeRedline* pRedl : rTable)
        {
            // Is at position?
            SwPosition *const pRStt = pRedl->Start();
            SwPosition *const pREnd = pRedl->End();

            if( lcl_Greater( *pRStt, rStt, pSttIdx ) && lcl_Lower( *pRStt, rEnd, pEndIdx ))
            {
                pRStt->nNode = rEnd;
                if( pEndIdx )
                    pRStt->nContent = *pEndIdx;
                else
                {
                    bool bStt = true;
                    SwContentNode* pCNd = pRStt->nNode.GetNode().GetContentNode();
                    if( !pCNd && nullptr == ( pCNd = pDoc->GetNodes().GoNext( &pRStt->nNode )) )
                    {
                        bStt = false;
                        pRStt->nNode = rStt;
                        if( nullptr == ( pCNd = SwNodes::GoPrevious( &pRStt->nNode )) )
                        {
                            pRStt->nNode = pREnd->nNode;
                            pCNd = pRStt->nNode.GetNode().GetContentNode();
                        }
                    }
                    pRStt->nContent.Assign( pCNd, bStt ? 0 : pCNd->Len() );
                }
            }
            if( lcl_Greater( *pREnd, rStt, pSttIdx ) && lcl_Lower( *pREnd, rEnd, pEndIdx ))
            {
                pREnd->nNode = rStt;
                if( pSttIdx )
                    pREnd->nContent = *pSttIdx;
                else
                {
                    bool bStt = false;
                    SwContentNode* pCNd = pREnd->nNode.GetNode().GetContentNode();
                    if( !pCNd && nullptr == ( pCNd = SwNodes::GoPrevious( &pREnd->nNode )) )
                    {
                        bStt = true;
                        pREnd->nNode = rEnd;
                        if( nullptr == ( pCNd = pDoc->GetNodes().GoNext( &pREnd->nNode )) )
                        {
                            pREnd->nNode = pRStt->nNode;
                            pCNd = pREnd->nNode.GetNode().GetContentNode();
                        }
                    }
                    pREnd->nContent.Assign( pCNd, bStt ? 0 : pCNd->Len() );
                }
            }
        }
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V595 The 'pEndIdx' pointer was utilized before it was verified against nullptr. Check lines: 1328, 1347.

↑ V522 There might be dereferencing of a potential null pointer 'pFieldMark'.