diff options
Diffstat (limited to 'sw/source/core/doc/docbm.cxx')
-rw-r--r-- | sw/source/core/doc/docbm.cxx | 2124 |
1 files changed, 2124 insertions, 0 deletions
diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx new file mode 100644 index 0000000000..00681135b4 --- /dev/null +++ b/sw/source/core/doc/docbm.cxx @@ -0,0 +1,2124 @@ +/* -*- 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 <utility> + +#include <MarkManager.hxx> +#include <bookmark.hxx> +#include <crossrefbookmark.hxx> +#include <crsrsh.hxx> +#include <annotationmark.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentState.hxx> +#include <IDocumentUndoRedo.hxx> +#include <docary.hxx> +#include <xmloff/odffields.hxx> +#include <mvsave.hxx> +#include <ndtxt.hxx> +#include <node.hxx> +#include <pam.hxx> +#include <redline.hxx> +#include <rolbck.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <sal/log.hxx> +#include <UndoBookmark.hxx> +#include <tools/datetimeutils.hxx> +#include <txtfrm.hxx> +#include <view.hxx> + +#include <libxml/xmlstring.h> +#include <libxml/xmlwriter.h> +#include <comphelper/lok.hxx> +#include <strings.hrc> + +constexpr OUString S_ANNOTATION_BOOKMARK = u"____"_ustr; + +using namespace ::sw::mark; + +std::vector<::sw::mark::MarkBase*>::const_iterator const& +IDocumentMarkAccess::iterator::get() const +{ + return *m_pIter; +} + +IDocumentMarkAccess::iterator::iterator(std::vector<::sw::mark::MarkBase*>::const_iterator const& rIter) + : m_pIter(rIter) +{ +} + +IDocumentMarkAccess::iterator::iterator(iterator const& rOther) + : m_pIter(rOther.m_pIter) +{ +} + +auto IDocumentMarkAccess::iterator::operator=(iterator const& rOther) -> iterator& +{ + m_pIter = rOther.m_pIter; + return *this; +} + +IDocumentMarkAccess::iterator::iterator(iterator && rOther) noexcept + : m_pIter(std::move(rOther.m_pIter)) +{ +} + +auto IDocumentMarkAccess::iterator::operator=(iterator && rOther) noexcept -> iterator& +{ + m_pIter = std::move(rOther.m_pIter); + return *this; +} + +// ARGH why does it *need* to return const& ? +::sw::mark::IMark* /*const&*/ +IDocumentMarkAccess::iterator::operator*() const +{ + return static_cast<sw::mark::IMark*>(**m_pIter); +} + +auto IDocumentMarkAccess::iterator::operator++() -> iterator& +{ + ++(*m_pIter); + return *this; +} +auto IDocumentMarkAccess::iterator::operator++(int) -> iterator +{ + iterator tmp(*this); + ++(*m_pIter); + return tmp; +} + +bool IDocumentMarkAccess::iterator::operator==(iterator const& rOther) const +{ + return *m_pIter == *rOther.m_pIter; +} + +bool IDocumentMarkAccess::iterator::operator!=(iterator const& rOther) const +{ + return *m_pIter != *rOther.m_pIter; +} + +IDocumentMarkAccess::iterator::iterator() + : m_pIter(std::in_place) +{ +} + +auto IDocumentMarkAccess::iterator::operator--() -> iterator& +{ + --(*m_pIter); + return *this; +} + +auto IDocumentMarkAccess::iterator::operator--(int) -> iterator +{ + iterator tmp(*this); + --(*m_pIter); + return tmp; +} + +auto IDocumentMarkAccess::iterator::operator+=(difference_type const n) -> iterator& +{ + (*m_pIter) += n; + return *this; +} + +auto IDocumentMarkAccess::iterator::operator+(difference_type const n) const -> iterator +{ + return iterator(*m_pIter + n); +} + +auto IDocumentMarkAccess::iterator::operator-=(difference_type const n) -> iterator& +{ + (*m_pIter) -= n; + return *this; +} + +auto IDocumentMarkAccess::iterator::operator-(difference_type const n) const -> iterator +{ + return iterator(*m_pIter - n); +} + +auto IDocumentMarkAccess::iterator::operator-(iterator const& rOther) const -> difference_type +{ + return *m_pIter - *rOther.m_pIter; +} + +auto IDocumentMarkAccess::iterator::operator[](difference_type const n) const -> value_type +{ + return static_cast<sw::mark::IMark*>((*m_pIter)[n]); +} + +bool IDocumentMarkAccess::iterator::operator<(iterator const& rOther) const +{ + return *m_pIter < *rOther.m_pIter; +} +bool IDocumentMarkAccess::iterator::operator>(iterator const& rOther) const +{ + return *m_pIter > *rOther.m_pIter; +} +bool IDocumentMarkAccess::iterator::operator<=(iterator const& rOther) const +{ + return *m_pIter <= *rOther.m_pIter; +} +bool IDocumentMarkAccess::iterator::operator>=(iterator const& rOther) const +{ + return *m_pIter >= *rOther.m_pIter; +} + + +namespace +{ + bool lcl_GreaterThan( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx ) + { + return oContentIdx.has_value() + ? ( rPos.GetNode() > rNdIdx + || ( rPos.GetNode() == rNdIdx + && rPos.GetContentIndex() >= *oContentIdx ) ) + : rPos.GetNode() >= rNdIdx; + } + + bool lcl_Lower( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx ) + { + if (rPos.GetNode() < rNdIdx) + return true; + + if (rPos.GetNode() != rNdIdx || !oContentIdx) + return false; + + if (rPos.GetContentIndex() < *oContentIdx) + return true; + + // paragraph end selected? + return rNdIdx.IsTextNode() && *oContentIdx == rNdIdx.GetTextNode()->Len(); + } + + bool lcl_MarkOrderingByStart(const ::sw::mark::MarkBase *const pFirst, + const ::sw::mark::MarkBase *const pSecond) + { + SwPosition const& rFirstStart(pFirst->GetMarkStart()); + SwPosition const& rSecondStart(pSecond->GetMarkStart()); + if (rFirstStart.GetNode() != rSecondStart.GetNode()) + { + return rFirstStart.GetNode() < rSecondStart.GetNode(); + } + const sal_Int32 nFirstContent = rFirstStart.GetContentIndex(); + const sal_Int32 nSecondContent = rSecondStart.GetContentIndex(); + if (nFirstContent != 0 || nSecondContent != 0) + { + return nFirstContent < nSecondContent; + } + SwContentNode const*const pFirstNode(rFirstStart.nContent.GetContentNode()); + SwContentNode const*const pSecondNode(rSecondStart.nContent.GetContentNode()); + if ((pFirstNode != nullptr) != (pSecondNode != nullptr)) + { // consistency with SwPosition::operator< + return pSecondNode != nullptr; + } + auto *const pCRFirst (dynamic_cast<::sw::mark::CrossRefBookmark const*>(pFirst)); + auto *const pCRSecond(dynamic_cast<::sw::mark::CrossRefBookmark const*>(pSecond)); + if ((pCRFirst == nullptr) == (pCRSecond == nullptr)) + { + return false; // equal + } + return pCRFirst != nullptr; // cross-ref sorts *before* + } + + bool lcl_MarkOrderingByEnd(const ::sw::mark::MarkBase *const pFirst, + const ::sw::mark::MarkBase *const pSecond) + { + return pFirst->GetMarkEnd() < pSecond->GetMarkEnd(); + } + + void lcl_InsertMarkSorted(MarkManager::container_t& io_vMarks, + ::sw::mark::MarkBase *const pMark) + { + io_vMarks.insert( + lower_bound( + io_vMarks.begin(), + io_vMarks.end(), + pMark, + &lcl_MarkOrderingByStart), + pMark); + } + + void lcl_PositionFromContentNode( + std::optional<SwPosition>& rFoundPos, + const SwContentNode * const pContentNode, + const bool bAtEnd) + { + rFoundPos.emplace(*pContentNode, bAtEnd ? pContentNode->Len() : 0); + } + + // 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 + void lcl_FindExpelPosition( + std::optional<SwPosition>& rFoundPos, + const SwNode& rStt, + const SwNode& rEnd, + const SwPosition& rOtherPosition) + { + const SwContentNode * pNode = rEnd.GetContentNode(); + bool bPosAtEndOfNode = false; + if ( pNode == nullptr) + { + SwNodeIndex aEnd(rEnd); + pNode = rEnd.GetNodes().GoNext( &aEnd ); + bPosAtEndOfNode = false; + } + if ( pNode == nullptr ) + { + SwNodeIndex aStt(rStt); + pNode = SwNodes::GoPrevious(&aStt); + bPosAtEndOfNode = true; + } + if ( pNode != nullptr ) + { + lcl_PositionFromContentNode( rFoundPos, pNode, bPosAtEndOfNode ); + return; + } + + rFoundPos = rOtherPosition; + } + + struct CompareIMarkStartsBefore + { + bool operator()(SwPosition const& rPos, + const sw::mark::IMark* pMark) + { + return rPos < pMark->GetMarkStart(); + } + bool operator()(const sw::mark::IMark* pMark, + SwPosition const& rPos) + { + return pMark->GetMarkStart() < rPos; + } + }; + + // Apple llvm-g++ 4.2.1 with _GLIBCXX_DEBUG won't eat boost::bind for this + // Neither will MSVC 2008 with _DEBUG + struct CompareIMarkStartsAfter + { + bool operator()(SwPosition const& rPos, + const sw::mark::IMark* pMark) + { + return pMark->GetMarkStart() > rPos; + } + }; + + + IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos, + bool bLoop) + { + auto const pMarkAfter = upper_bound( + rMarks.begin(), + rMarks.end(), + rPos, + CompareIMarkStartsAfter()); + if(pMarkAfter == rMarks.end()) + { + if (bLoop && rMarks.begin() != rMarks.end()) + return *rMarks.begin(); + + return nullptr; + } + return *pMarkAfter; + }; + + IMark* lcl_getMarkBefore(const MarkManager::container_t& rMarks, const SwPosition& rPos, + bool bLoop) + { + // candidates from which to choose the mark before + MarkManager::container_t vCandidates; + // no need to consider marks starting after rPos + auto const pCandidatesEnd = upper_bound( + rMarks.begin(), + rMarks.end(), + rPos, + CompareIMarkStartsAfter()); + vCandidates.reserve(pCandidatesEnd - rMarks.begin()); + // only marks ending before are candidates + remove_copy_if( + rMarks.begin(), + pCandidatesEnd, + back_inserter(vCandidates), + [&rPos] (const ::sw::mark::MarkBase *const pMark) { return !(pMark->GetMarkEnd() < rPos); } ); + // no candidate left => we are in front of the first mark or there are none + if(vCandidates.empty()) + { + if (bLoop && rMarks.begin() != rMarks.end()) + return *(rMarks.end() - 1); + + return nullptr; + } + // return the highest (last) candidate using mark end ordering + return *max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd); + } + + 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().GetNode().FindTableBoxStartNode() != + io_pMark->GetMarkPos().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 ::sw::mark::MarkBase *const pFirst, + const ::sw::mark::MarkBase *const pSecond) + { + return !lcl_MarkOrderingByStart(pFirst, pSecond) && + !lcl_MarkOrderingByStart(pSecond, pFirst); + } + + MarkManager::container_t::const_iterator lcl_FindMark( + MarkManager::container_t& rMarks, + const ::sw::mark::MarkBase *const pMarkToFind) + { + auto ppCurrentMark = lower_bound( + rMarks.begin(), rMarks.end(), + pMarkToFind, &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, pMarkToFind)) + { + if(*ppCurrentMark == pMarkToFind) + { + return MarkManager::container_t::const_iterator(std::move(ppCurrentMark)); + } + ++ppCurrentMark; + } + // reached a mark starting on a later start pos or the end of the + // vector => not found + return rMarks.end(); + }; + + MarkManager::container_t::const_iterator lcl_FindMarkAtPos( + MarkManager::container_t& rMarks, + const SwPosition& rPos, + const IDocumentMarkAccess::MarkType eType) + { + for (auto ppCurrentMark = lower_bound( + rMarks.begin(), rMarks.end(), + rPos, + CompareIMarkStartsBefore()); + ppCurrentMark != rMarks.end(); + ++ppCurrentMark) + { + // Once we reach a mark starting after the target pos + // we do not need to continue + if((*ppCurrentMark)->GetMarkStart() > rPos) + break; + if(IDocumentMarkAccess::GetType(**ppCurrentMark) == eType) + { + return MarkManager::container_t::const_iterator(std::move(ppCurrentMark)); + } + } + // reached a mark starting on a later start pos or the end of the + // vector => not found + return rMarks.end(); + }; + + MarkManager::container_t::const_iterator lcl_FindMarkByName( + const OUString& rName, + const MarkManager::container_t::const_iterator& ppMarksBegin, + const MarkManager::container_t::const_iterator& ppMarksEnd) + { + return find_if( + ppMarksBegin, + ppMarksEnd, + [&rName] (::sw::mark::MarkBase const*const pMark) { return pMark->GetName() == rName; } ); + } + + void lcl_DebugMarks(MarkManager::container_t const& rMarks) + { +#if OSL_DEBUG_LEVEL > 0 + SAL_INFO("sw.core", rMarks.size() << " Marks"); + for (auto ppMark = rMarks.begin(); + ppMark != rMarks.end(); + ++ppMark) + { + IMark* pMark = *ppMark; + const SwPosition* const pStPos = &pMark->GetMarkStart(); + const SwPosition* const pEndPos = &pMark->GetMarkEnd(); + SAL_INFO("sw.core", + sal_Int32(pStPos->GetNodeIndex()) << "," << + pStPos->GetContentIndex() << " " << + sal_Int32(pEndPos->GetNodeIndex()) << "," << + pEndPos->GetContentIndex() << " " << + 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(DropDownFieldmark)) + return MarkType::DROPDOWN_FIELDMARK; + else if(*pMarkTypeInfo == typeid(DateFieldmark)) + return MarkType::DATE_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 "__RefHeading__"; +} + +bool IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( const SwPaM& rPaM ) +{ + return rPaM.Start()->GetNode().IsTextNode() && + rPaM.Start()->GetContentIndex() == 0 && + ( !rPaM.HasMark() || + ( rPaM.GetMark()->GetNode() == rPaM.GetPoint()->GetNode() && + rPaM.End()->GetContentIndex() == rPaM.End()->GetNode().GetTextNode()->Len() ) ); +} + +void IDocumentMarkAccess::DeleteFieldmarkCommand(::sw::mark::IFieldmark const& rMark) +{ + if (GetType(rMark) != MarkType::TEXT_FIELDMARK) + { + return; // TODO FORMDATE has no command? + } + SwPaM pam(sw::mark::FindFieldSep(rMark), rMark.GetMarkStart()); + pam.GetPoint()->AdjustContent(+1); // skip CH_TXT_ATR_FIELDSTART + pam.GetDoc().getIDocumentContentOperations().DeleteAndJoin(pam); +} + +namespace sw::mark +{ + MarkManager::MarkManager(SwDoc& rDoc) + : m_rDoc(rDoc) + , m_pLastActiveFieldmark(nullptr) + { } + + ::sw::mark::IMark* MarkManager::makeMark(const SwPaM& rPaM, + const OUString& rName, + const IDocumentMarkAccess::MarkType eType, + sw::mark::InsertMode const eMode, + SwPosition const*const pSepPos) + { +#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 << " " << + sal_Int32(pPos1->GetNodeIndex() )<< "," << + pPos1->GetContentIndex() << " " << + sal_Int32(pPos2->GetNodeIndex()) << "," << + pPos2->GetContentIndex()); + } +#endif + if ( (!rPaM.GetPoint()->GetNode().IsTextNode() + && (eType != MarkType::UNO_BOOKMARK + // SwXTextRange can be on table node or plain start node (FLY_AT_FLY) + || !rPaM.GetPoint()->GetNode().IsStartNode())) + || (!rPaM.GetMark()->GetNode().IsTextNode() + && (eType != MarkType::UNO_BOOKMARK + || !rPaM.GetMark()->GetNode().IsStartNode()))) + { + SAL_WARN("sw.core", "MarkManager::makeMark(..)" + " - refusing to create mark on non-textnode"); + return nullptr; + } + // 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; + } + + if ((eType == MarkType::CHECKBOX_FIELDMARK || eType == MarkType::DROPDOWN_FIELDMARK) + && (eMode == InsertMode::New + ? *rPaM.GetPoint() != *rPaM.GetMark() + // CopyText: pam covers CH_TXT_ATR_FORMELEMENT + : (rPaM.GetPoint()->GetNode() != rPaM.GetMark()->GetNode() + || rPaM.Start()->GetContentIndex() + 1 != rPaM.End()->GetContentIndex()))) + { + SAL_WARN("sw.core", "MarkManager::makeMark(..)" + " - invalid range on point fieldmark"); + return nullptr; + } + + if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK) + && (rPaM.GetPoint()->GetNode().StartOfSectionNode() != rPaM.GetMark()->GetNode().StartOfSectionNode() + || (pSepPos && rPaM.GetPoint()->GetNode().StartOfSectionNode() != pSepPos->GetNode().StartOfSectionNode()))) + { + SAL_WARN("sw.core", "MarkManager::makeMark(..)" + " - invalid range on fieldmark, different nodes array sections"); + return nullptr; + } + + if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK) + // can't check for Copy - it asserts - but it's also obviously unnecessary + && eMode == InsertMode::New + && sw::mark::IsFieldmarkOverlap(rPaM)) + { + SAL_WARN("sw.core", "MarkManager::makeMark(..)" + " - invalid range on fieldmark, overlaps existing fieldmark or meta-field"); + return nullptr; + } + + // create mark + std::unique_ptr<::sw::mark::MarkBase> pMark; + switch(eType) + { + case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: + pMark = std::make_unique<TextFieldmark>(rPaM, rName); + break; + case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: + pMark = std::make_unique<CheckboxFieldmark>(rPaM, rName); + break; + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + pMark = std::make_unique<DropDownFieldmark>(rPaM, rName); + break; + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + pMark = std::make_unique<DateFieldmark>(rPaM); + break; + case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: + pMark = std::make_unique<NavigatorReminder>(rPaM); + break; + case IDocumentMarkAccess::MarkType::BOOKMARK: + pMark = std::make_unique<Bookmark>(rPaM, vcl::KeyCode(), rName); + break; + case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: + pMark = std::make_unique<DdeBookmark>(rPaM); + break; + case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: + pMark = std::make_unique<CrossRefHeadingBookmark>(rPaM, vcl::KeyCode(), rName); + break; + case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: + pMark = std::make_unique<CrossRefNumItemBookmark>(rPaM, vcl::KeyCode(), rName); + break; + case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: + pMark = std::make_unique<UnoMark>(rPaM); + break; + case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: + pMark = std::make_unique<AnnotationMark>( rPaM, rName ); + break; + } + assert(pMark && "MarkManager::makeMark(..) - Mark was not created."); + + if(pMark->GetMarkPos() != pMark->GetMarkStart()) + pMark->Swap(); + + // for performance reasons, we trust UnoMarks to have a (generated) unique name + if ( eType != IDocumentMarkAccess::MarkType::UNO_BOOKMARK ) + pMark->SetName( getUniqueMarkName( pMark->GetName() ) ); + + // insert any dummy chars before inserting into sorted vectors + pMark->InitDoc(m_rDoc, eMode, pSepPos); + + // register mark + lcl_InsertMarkSorted(m_vAllMarks, pMark.get()); + switch(eType) + { + case IDocumentMarkAccess::MarkType::BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: + lcl_InsertMarkSorted(m_vBookmarks, pMark.get()); + break; + case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: + case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + lcl_InsertMarkSorted(m_vFieldmarks, pMark.get()); + break; + case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: + lcl_InsertMarkSorted( m_vAnnotationMarks, pMark.get() ); + break; + case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: + case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: + case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: + // no special array for these + break; + } + if (eMode == InsertMode::New + && (eType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK + || eType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK)) + { + // due to sw::InsertText notifications everything is visible now - tell + // layout to hide as appropriate + // note: we don't know how many layouts there are and which + // parts they hide, so just notify the entire fieldmark, it + // should give the right result if not in the most efficient way + // note2: can't be done in InitDoc() because it requires the mark + // to be inserted in the vectors. + SwPaM const tmp(pMark->GetMarkPos(), pMark->GetOtherMarkPos()); + sw::UpdateFramesForAddDeleteRedline(m_rDoc, tmp); + } + + 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.release(); + } + + ::sw::mark::IFieldmark* MarkManager::makeFieldBookmark( + const SwPaM& rPaM, + const OUString& rName, + const OUString& rType, + SwPosition const*const pSepPos) + { + + // Disable undo, because we handle it using SwUndoInsTextFieldmark + bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo(); + m_rDoc.GetIDocumentUndoRedo().DoUndo(false); + + sw::mark::IMark* pMark = nullptr; + if(rType == ODF_FORMDATE) + { + pMark = makeMark(rPaM, rName, + IDocumentMarkAccess::MarkType::DATE_FIELDMARK, + sw::mark::InsertMode::New, + pSepPos); + } + else + { + pMark = makeMark(rPaM, rName, + IDocumentMarkAccess::MarkType::TEXT_FIELDMARK, + sw::mark::InsertMode::New, + pSepPos); + } + sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark ); + if (pFieldMark) + pFieldMark->SetFieldname( rType ); + + if (bUndoIsEnabled) + { + m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled); + if (pFieldMark) + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsTextFieldmark>(*pFieldMark)); + } + + return pFieldMark; + } + + ::sw::mark::IFieldmark* MarkManager::makeNoTextFieldBookmark( + const SwPaM& rPaM, + const OUString& rName, + const OUString& rType) + { + // Disable undo, because we handle it using SwUndoInsNoTextFieldmark + bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo(); + m_rDoc.GetIDocumentUndoRedo().DoUndo(false); + + bool bEnableSetModified = m_rDoc.getIDocumentState().IsEnableSetModified(); + m_rDoc.getIDocumentState().SetEnableSetModified(false); + + sw::mark::IMark* pMark = nullptr; + if(rType == ODF_FORMCHECKBOX) + { + pMark = makeMark( rPaM, rName, + IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK, + sw::mark::InsertMode::New); + } + else if(rType == ODF_FORMDROPDOWN) + { + pMark = makeMark( rPaM, rName, + IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK, + sw::mark::InsertMode::New); + } + else if(rType == ODF_FORMDATE) + { + pMark = makeMark( rPaM, rName, + IDocumentMarkAccess::MarkType::DATE_FIELDMARK, + sw::mark::InsertMode::New); + } + + sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark ); + if (pFieldMark) + pFieldMark->SetFieldname( rType ); + + if (bUndoIsEnabled) + { + m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled); + if (pFieldMark) + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsNoTextFieldmark>(*pFieldMark)); + } + + m_rDoc.getIDocumentState().SetEnableSetModified(bEnableSetModified); + m_rDoc.getIDocumentState().SetModified(); + + return pFieldMark; + } + + ::sw::mark::IMark* MarkManager::getMarkForTextNode( + const SwTextNode& rTextNode, + const IDocumentMarkAccess::MarkType eType ) + { + SwPosition aPos(rTextNode); + auto const ppExistingMark = lcl_FindMarkAtPos(m_vBookmarks, aPos, eType); + if(ppExistingMark != m_vBookmarks.end()) + return *ppExistingMark; + 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_rDoc && + "<MarkManager::repositionMark(..)>" + " - Mark is not in my doc."); + MarkBase* const pMarkBase = dynamic_cast< MarkBase* >(io_pMark); + if (!pMarkBase) + return; + + pMarkBase->InvalidateFrames(); + + pMarkBase->SetMarkPos(*(rPaM.GetPoint())); + if(rPaM.HasMark()) + pMarkBase->SetOtherMarkPos(*(rPaM.GetMark())); + else + pMarkBase->ClearOtherMarkPos(); + + if(pMarkBase->GetMarkPos() != pMarkBase->GetMarkStart()) + pMarkBase->Swap(); + + pMarkBase->InvalidateFrames(); + + sortMarks(); + } + + bool MarkManager::renameMark( + ::sw::mark::IMark* io_pMark, + const OUString& rNewName ) + { + assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc && + "<MarkManager::renameMark(..)>" + " - Mark is not in my doc."); + if ( io_pMark->GetName() == rNewName ) + return true; + if (lcl_FindMarkByName(rNewName, m_vAllMarks.begin(), m_vAllMarks.end()) != m_vAllMarks.end()) + return false; + if (::sw::mark::MarkBase* pMarkBase = dynamic_cast< ::sw::mark::MarkBase* >(io_pMark)) + { + const OUString sOldName(pMarkBase->GetName()); + pMarkBase->SetName(rNewName); + + if (dynamic_cast< ::sw::mark::Bookmark* >(io_pMark)) + { + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoRenameBookmark>(sOldName, rNewName, m_rDoc)); + } + m_rDoc.getIDocumentState().SetModified(); + } + } + return true; + } + + void MarkManager::correctMarksAbsolute( + const SwNode& rOldNode, + const SwPosition& rNewPos, + const sal_Int32 nOffset) + { + const SwNode* const pOldNode = &rOldNode; + SwPosition aNewPos(rNewPos); + aNewPos.AdjustContent(nOffset); + bool isSortingNeeded = false; + + for (auto ppMark = m_vAllMarks.begin(); + ppMark != m_vAllMarks.end(); + ++ppMark) + { + ::sw::mark::MarkBase *const pMark = *ppMark; + // correction of non-existent non-MarkBase instances cannot be done + assert(pMark); + // is on position ?? + bool bChangedPos = false; + if(&pMark->GetMarkPos().GetNode() == pOldNode) + { + pMark->SetMarkPos(aNewPos); + bChangedPos = true; + isSortingNeeded = true; + } + bool bChangedOPos = false; + if (pMark->IsExpanded() && + &pMark->GetOtherMarkPos().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 SwNode& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset) + { + const SwNode* const pOldNode = &rOldNode; + SwPosition aNewPos(rNewPos); + aNewPos.AdjustContent(nOffset); + bool isSortingNeeded = false; + + for (auto ppMark = m_vAllMarks.begin(); + ppMark != m_vAllMarks.end(); + ++ppMark) + { + // is on position ?? + bool bChangedPos = false, bChangedOPos = false; + ::sw::mark::MarkBase* const pMark = *ppMark; + // correction of non-existent non-MarkBase instances cannot be done + assert(pMark); + if(&pMark->GetMarkPos().GetNode() == pOldNode) + { + SwPosition aNewPosRel(aNewPos); + if (dynamic_cast< ::sw::mark::CrossRefBookmark *>(pMark)) + { + // ensure that cross ref bookmark always starts at 0 + aNewPosRel.SetContent(0); // HACK for WW8 import + isSortingNeeded = true; // and sort them to be safe... + } + aNewPosRel.AdjustContent(pMark->GetMarkPos().GetContentIndex()); + pMark->SetMarkPos(aNewPosRel); + bChangedPos = true; + } + if(pMark->IsExpanded() && + &pMark->GetOtherMarkPos().GetNode() == pOldNode) + { + SwPosition aNewPosRel(aNewPos); + aNewPosRel.AdjustContent(pMark->GetOtherMarkPos().GetContentIndex()); + 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); + } + + static bool isDeleteMark( + ::sw::mark::MarkBase const*const pMark, + bool const isReplace, + SwNode const& rStt, + SwNode const& rEnd, + std::optional<sal_Int32> oStartContentIdx, + std::optional<sal_Int32> oEndContentIdx, + bool & rbIsPosInRange, + bool & rbIsOtherPosInRange) + { + assert(pMark); + // navigator marks should not be moved + // TODO: Check if this might make them invalid + if (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER) + { + return false; + } + + // on position ?? + rbIsPosInRange = lcl_GreaterThan(pMark->GetMarkPos(), rStt, oStartContentIdx) + && lcl_Lower(pMark->GetMarkPos(), rEnd, oEndContentIdx); + rbIsOtherPosInRange = pMark->IsExpanded() + && lcl_GreaterThan(pMark->GetOtherMarkPos(), rStt, oStartContentIdx) + && lcl_Lower(pMark->GetOtherMarkPos(), rEnd, oEndContentIdx); + // special case: completely in range, touching the end? + if ( oEndContentIdx.has_value() + && !(isReplace && IDocumentMarkAccess::GetType(*pMark) + == IDocumentMarkAccess::MarkType::BOOKMARK) + && ( ( rbIsOtherPosInRange + && pMark->GetMarkPos().GetNode() == rEnd + && pMark->GetMarkPos().GetContentIndex() == *oEndContentIdx ) + || ( rbIsPosInRange + && pMark->IsExpanded() + && pMark->GetOtherMarkPos().GetNode() == rEnd + && pMark->GetOtherMarkPos().GetContentIndex() == *oEndContentIdx ) ) ) + { + rbIsPosInRange = true; + rbIsOtherPosInRange = true; + } + + if (rbIsPosInRange + && (rbIsOtherPosInRange + || !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 = rbIsOtherPosInRange + || pMark->IsExpanded() + || !oStartContentIdx.has_value() + || pMark->GetMarkPos().GetNode() != rStt + || pMark->GetMarkPos().GetContentIndex() != *oStartContentIdx; + break; + default: + bDeleteMark = true; + break; + } + } + return bDeleteMark; + } + return false; + } + + bool MarkManager::isBookmarkDeleted(SwPaM const& rPaM, bool const isReplace) const + { + SwPosition const& rStart(*rPaM.Start()); + SwPosition const& rEnd(*rPaM.End()); + for (auto ppMark = m_vBookmarks.begin(); + ppMark != m_vBookmarks.end(); + ++ppMark) + { + bool bIsPosInRange(false); + bool bIsOtherPosInRange(false); + bool const bDeleteMark = isDeleteMark(*ppMark, isReplace, + rStart.GetNode(), rEnd.GetNode(), rStart.GetContentIndex(), rEnd.GetContentIndex(), + bIsPosInRange, bIsOtherPosInRange); + if (bDeleteMark + && IDocumentMarkAccess::GetType(**ppMark) == MarkType::BOOKMARK) + { + return true; + } + } + return false; + } + + void MarkManager::deleteMarks( + const SwNode& rStt, + const SwNode& rEnd, + std::vector<SaveBookmark>* pSaveBkmk, + std::optional<sal_Int32> oStartContentIdx, + std::optional<sal_Int32> oEndContentIdx, + bool const isReplace) + { + 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 (auto ppMark = m_vAllMarks.begin(); + ppMark != m_vAllMarks.end(); + ++ppMark) + { + ::sw::mark::MarkBase *const pMark = *ppMark; + bool bIsPosInRange(false); + bool bIsOtherPosInRange(false); + bool const bDeleteMark = isDeleteMark(pMark, isReplace, rStt, rEnd, + oStartContentIdx, oEndContentIdx, bIsPosInRange, bIsOtherPosInRange); + + if ( bIsPosInRange + && ( bIsOtherPosInRange + || !pMark->IsExpanded() ) ) + { + if ( bDeleteMark ) + { + if ( pSaveBkmk ) + { + pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, oStartContentIdx ) ); + } + 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::optional< SwPosition > oNewPos; + if ( oEndContentIdx ) + { + oNewPos.emplace( *rEnd.GetContentNode(), *oEndContentIdx ); + } + else + { + lcl_FindExpelPosition( oNewPos, 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().GetNode() != oNewPos->GetNode(); + 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(*oNewPos); + else + pMark->SetOtherMarkPos(*oNewPos); + 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::unique_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, pSaveBkmk != nullptr)); + } + } // 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); + } + + namespace { + + struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter + { + std::unique_ptr<Fieldmark> m_pFieldmark; + SwDoc& m_rDoc; + bool const m_isMoveNodes; + LazyFieldmarkDeleter(Fieldmark *const pMark, SwDoc& rDoc, bool const isMoveNodes) + : m_pFieldmark(pMark), m_rDoc(rDoc), m_isMoveNodes(isMoveNodes) + { + assert(m_pFieldmark); + } + virtual ~LazyFieldmarkDeleter() override + { + // note: because of the call chain from SwUndoDelete, the field + // command *cannot* be deleted here as it would create a separate + // SwUndoDelete that's interleaved with the SwHistory of the outer + // one - only delete the CH_TXT_ATR_FIELD*! + if (!m_isMoveNodes) + { + m_pFieldmark->ReleaseDoc(m_rDoc); + } + } + }; + + // Call DeregisterFromDoc() lazily, because it can call selection change listeners, which + // may mutate the marks container + struct LazyDdeBookmarkDeleter : public IDocumentMarkAccess::ILazyDeleter + { + std::unique_ptr<DdeBookmark> m_pDdeBookmark; + SwDoc& m_rDoc; + LazyDdeBookmarkDeleter(DdeBookmark *const pDdeBookmark, SwDoc& rDoc) + : m_pDdeBookmark(pDdeBookmark), m_rDoc(rDoc) + { + assert(pDdeBookmark); + } + virtual ~LazyDdeBookmarkDeleter() override + { + m_pDdeBookmark->DeregisterFromDoc(m_rDoc); + } + }; + + } + + std::unique_ptr<IDocumentMarkAccess::ILazyDeleter> + MarkManager::deleteMark(const const_iterator_t& ppMark, bool const isMoveNodes) + { + std::unique_ptr<ILazyDeleter> ret; + if (ppMark.get() == m_vAllMarks.end()) + return ret; + IMark* pMark = *ppMark; + + switch(IDocumentMarkAccess::GetType(*pMark)) + { + case IDocumentMarkAccess::MarkType::BOOKMARK: + { + auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get()); + if ( ppBookmark != m_vBookmarks.end() ) + { + Bookmark* pBookmark = dynamic_cast<Bookmark*>(*ppBookmark); + + if(pBookmark) + pBookmark->sendLOKDeleteCallback(); + + m_vBookmarks.erase(ppBookmark); + } + else + { + assert(false && + "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container."); + } + } + break; + case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: + { + auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get()); + 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: + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + { + auto const ppFieldmark = lcl_FindMark(m_vFieldmarks, *ppMark.get()); + if ( ppFieldmark != m_vFieldmarks.end() ) + { + if(m_pLastActiveFieldmark == *ppFieldmark) + ClearFieldActivation(); + + m_vFieldmarks.erase(ppFieldmark); + ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_rDoc, isMoveNodes)); + } + else + { + assert(false && + "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container."); + } + } + break; + + case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: + { + auto const ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, *ppMark.get()); + assert(ppAnnotationMark != m_vAnnotationMarks.end() && + "<MarkManager::deleteMark(..)> - Annotation Mark not found in Annotation Mark container."); + m_vAnnotationMarks.erase(ppAnnotationMark); + } + break; + + case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: + case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: + case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: + // no special marks container + break; + } + //Effective STL Item 27, get a non-const iterator aI at the same + //position as const iterator ppMark was + auto aI = m_vAllMarks.begin(); + std::advance(aI, std::distance<container_t::const_iterator>(aI, ppMark.get())); + DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(pMark); + if (pDdeBookmark) + { + ret.reset(new LazyDdeBookmarkDeleter(pDdeBookmark, m_rDoc)); + } + + m_vAllMarks.erase(aI); + // If we don't have a lazy deleter + if (!ret) + // delete after we remove from the list, because the destructor can + // recursively call into this method. + delete pMark; + return ret; + } + + void MarkManager::deleteMark(const IMark* const pMark) + { + assert(&pMark->GetMarkPos().GetDoc() == &m_rDoc && + "<MarkManager::deleteMark(..)>" + " - Mark is not in my doc."); + // finds the last Mark that is starting before pMark + // (pMarkLow < pMark) + auto [it, endIt] = equal_range( + m_vAllMarks.begin(), + m_vAllMarks.end(), + pMark->GetMarkStart(), + CompareIMarkStartsBefore()); + for ( ; it != endIt; ++it) + if (*it == pMark) + { + deleteMark(iterator(it), false); + break; + } + } + + void MarkManager::clearAllMarks() + { + ClearFieldActivation(); + m_vFieldmarks.clear(); + m_vBookmarks.clear(); + m_vAnnotationMarks.clear(); + for (const auto & p : m_vAllMarks) + delete p; + m_vAllMarks.clear(); + } + + IDocumentMarkAccess::const_iterator_t MarkManager::findMark(const OUString& rName) const + { + auto const ret = lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end()); + return IDocumentMarkAccess::iterator(ret); + } + + IDocumentMarkAccess::const_iterator_t MarkManager::findBookmark(const OUString& rName) const + { + auto const ret = lcl_FindMarkByName(rName, m_vBookmarks.begin(), m_vBookmarks.end()); + return IDocumentMarkAccess::iterator(ret); + } + + 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(); } + + IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksBegin() const + { return m_vFieldmarks.begin(); } + + IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksEnd() const + { return m_vFieldmarks.end(); } + + sal_Int32 MarkManager::getFieldmarksCount() const { return m_vFieldmarks.size(); } + + + // finds the first that is starting after + IDocumentMarkAccess::const_iterator_t MarkManager::findFirstBookmarkStartsAfter(const SwPosition& rPos) const + { + return std::upper_bound( + m_vBookmarks.begin(), + m_vBookmarks.end(), + rPos, + CompareIMarkStartsAfter()); + } + + IFieldmark* MarkManager::getFieldmarkAt(const SwPosition& rPos) const + { + auto const pFieldmark = find_if( + m_vFieldmarks.begin(), + m_vFieldmarks.end(), + [&rPos] (::sw::mark::MarkBase const*const pMark) { + return pMark->GetMarkStart() == rPos + // end position includes the CH_TXT_ATR_FIELDEND + || (pMark->GetMarkEnd().GetContentIndex() == rPos.GetContentIndex() + 1 + && pMark->GetMarkEnd().GetNode() == rPos.GetNode()); + } ); + return (pFieldmark == m_vFieldmarks.end()) + ? nullptr + : dynamic_cast<IFieldmark*>(*pFieldmark); + } + + IFieldmark* MarkManager::getInnerFieldmarkFor(const SwPosition& rPos) const + { + auto itFieldmark = find_if( + m_vFieldmarks.begin(), + m_vFieldmarks.end(), + [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } ); + if (itFieldmark == m_vFieldmarks.end()) + return nullptr; + auto pFieldmark(*itFieldmark); + + // See if any fieldmarks after the first hit are closer to rPos. + ++itFieldmark; + for ( ; itFieldmark != m_vFieldmarks.end() + && (**itFieldmark).GetMarkStart() <= rPos; ++itFieldmark) + { // find the innermost fieldmark + if (rPos < (**itFieldmark).GetMarkEnd() + && (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart() + || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd())) + { + pFieldmark = *itFieldmark; + } + } + return dynamic_cast<IFieldmark*>(pFieldmark); + } + + IMark* MarkManager::getOneInnermostBookmarkFor(const SwPosition& rPos) const + { + auto it = std::find_if(m_vBookmarks.begin(), m_vBookmarks.end(), + [&rPos](const sw::mark::MarkBase* pMark) + { return pMark->IsCoveringPosition(rPos); }); + if (it == m_vBookmarks.end()) + { + return nullptr; + } + sw::mark::IMark* pBookmark = *it; + + // See if any bookmarks after the first hit are closer to rPos. + ++it; + + for (; it != m_vBookmarks.end() && (*it)->GetMarkStart() <= rPos; ++it) + { + // Find the innermost bookmark. + if (rPos < (*it)->GetMarkEnd() + && (pBookmark->GetMarkStart() < (*it)->GetMarkStart() + || (*it)->GetMarkEnd() < pBookmark->GetMarkEnd())) + { + pBookmark = *it; + } + } + return pBookmark; + } + + void MarkManager::deleteFieldmarkAt(const SwPosition& rPos) + { + auto const pFieldmark = dynamic_cast<Fieldmark*>(getFieldmarkAt(rPos)); + assert(pFieldmark); // currently all callers require it to be there + + deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark), false); + } + + ::sw::mark::IFieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType) + { + bool bActualChange = false; + if(rNewType == ODF_FORMDROPDOWN) + { + if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) + bActualChange = true; + if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown + return nullptr; + } + else if(rNewType == ODF_FORMCHECKBOX) + { + if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) + bActualChange = true; + if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown + return nullptr; + } + else if(rNewType == ODF_FORMDATE) + { + if (!dynamic_cast<::sw::mark::DateFieldmark*>(pFieldmark)) + bActualChange = true; + if (!dynamic_cast<::sw::mark::TextFieldmark*>(pFieldmark)) // only allowed converting between date field <-> text field + return nullptr; + } + + if (!bActualChange) + return nullptr; + + // Store attributes needed to create the new fieldmark + OUString sName = pFieldmark->GetName(); + SwPaM const aPaM(pFieldmark->GetMarkStart()); + + // Remove the old fieldmark and create a new one with the new type + if (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX) + { + SwPosition aNewPos (*aPaM.GetPoint()); + deleteFieldmarkAt(aNewPos); + return makeNoTextFieldBookmark(aPaM, sName, rNewType); + } + else if(rNewType == ODF_FORMDATE) + { + SwPosition aPos (*aPaM.GetPoint()); + SwPaM aNewPaM(pFieldmark->GetMarkStart(), pFieldmark->GetMarkEnd()); + deleteFieldmarkAt(aPos); + // HACK: hard-code the separator position here at the start because + // writerfilter put it in the wrong place (at the end) on attach() + SwPosition const sepPos(*aNewPaM.Start()); + return makeFieldBookmark(aNewPaM, sName, rNewType, &sepPos); + } + return nullptr; + } + + void MarkManager::NotifyCursorUpdate(const SwCursorShell& rCursorShell) + { + SwView* pSwView = dynamic_cast<SwView *>(rCursorShell.GetSfxViewShell()); + if(!pSwView) + return; + + SwEditWin& rEditWin = pSwView->GetEditWin(); + SwPosition aPos(*rCursorShell.GetCursor()->GetPoint()); + IFieldmark* pFieldBM = getInnerFieldmarkFor(aPos); + FieldmarkWithDropDownButton* pNewActiveFieldmark = nullptr; + if ((!pFieldBM || (pFieldBM->GetFieldname() != ODF_FORMDROPDOWN && pFieldBM->GetFieldname() != ODF_FORMDATE)) + && aPos.GetContentIndex() > 0 ) + { + aPos.AdjustContent(-1); + pFieldBM = getInnerFieldmarkFor(aPos); + } + + if ( pFieldBM && (pFieldBM->GetFieldname() == ODF_FORMDROPDOWN || + pFieldBM->GetFieldname() == ODF_FORMDATE)) + { + if (m_pLastActiveFieldmark != pFieldBM) + { + FieldmarkWithDropDownButton& rFormField = dynamic_cast<FieldmarkWithDropDownButton&>(*pFieldBM); + pNewActiveFieldmark = &rFormField; + } + else + { + pNewActiveFieldmark = m_pLastActiveFieldmark; + } + } + + if(pNewActiveFieldmark != m_pLastActiveFieldmark) + { + ClearFieldActivation(); + m_pLastActiveFieldmark = pNewActiveFieldmark; + if(pNewActiveFieldmark) + pNewActiveFieldmark->ShowButton(&rEditWin); + } + + LOKUpdateActiveField(pSwView); + } + + void MarkManager::ClearFieldActivation() + { + if(m_pLastActiveFieldmark) + m_pLastActiveFieldmark->RemoveButton(); + + m_pLastActiveFieldmark = nullptr; + } + + void MarkManager::LOKUpdateActiveField(const SfxViewShell* pViewShell) + { + if (!comphelper::LibreOfficeKit::isActive()) + return; + + if (m_pLastActiveFieldmark) + { + if (auto pDrowDown = m_pLastActiveFieldmark->GetFieldname() == ODF_FORMDROPDOWN ? + dynamic_cast<::sw::mark::DropDownFieldmark*>(m_pLastActiveFieldmark) : + nullptr) + { + pDrowDown->SendLOKShowMessage(pViewShell); + } + } + else + { + // Check whether we have any drop down fieldmark at all. + bool bDropDownFieldExist = false; + for (auto aIter = m_vFieldmarks.begin(); aIter != m_vFieldmarks.end(); ++aIter) + { + IFieldmark *pMark = dynamic_cast<IFieldmark*>(*aIter); + if (pMark && pMark->GetFieldname() == ODF_FORMDROPDOWN) + { + bDropDownFieldExist = true; + break; + } + } + + if (bDropDownFieldExist) + ::sw::mark::DropDownFieldmark::SendLOKHideMessage(pViewShell); + } + } + + IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const + { + IFieldmark *pMark = getFieldmarkAt(rPos); + if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN) + return nullptr; + return pMark; + } + + std::vector<IFieldmark*> MarkManager::getNoTextFieldmarksIn(const SwPaM &rPaM) const + { + std::vector<IFieldmark*> aRet; + + for (auto aI = m_vFieldmarks.begin(), + aEnd = m_vFieldmarks.end(); aI != aEnd; ++aI) + { + ::sw::mark::IMark* pI = *aI; + const SwPosition &rStart = pI->GetMarkPos(); + if (!rPaM.ContainsPosition(rStart)) + continue; + + IFieldmark *pMark = dynamic_cast<IFieldmark*>(pI); + if (!pMark || (pMark->GetFieldname() != ODF_FORMDROPDOWN + && pMark->GetFieldname() != ODF_FORMCHECKBOX)) + { + continue; + } + + aRet.push_back(pMark); + } + + return aRet; + } + + IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos, bool bLoop) const + { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos, bLoop)); } + + IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const + { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos, bLoop)); } + + 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 + { + auto const ret = lcl_FindMarkByName( rName, m_vAnnotationMarks.begin(), m_vAnnotationMarks.end() ); + return IDocumentMarkAccess::iterator(ret); + } + + IMark* MarkManager::getAnnotationMarkFor(const SwPosition& rPos) const + { + auto const pAnnotationMark = find_if( + m_vAnnotationMarks.begin(), + m_vAnnotationMarks.end(), + [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } ); + if (pAnnotationMark == m_vAnnotationMarks.end()) + return nullptr; + return *pAnnotationMark; + } + + // finds the first that is starting after + IDocumentMarkAccess::const_iterator_t MarkManager::findFirstAnnotationStartsAfter(const SwPosition& rPos) const + { + return std::upper_bound( + m_vAnnotationMarks.begin(), + m_vAnnotationMarks.end(), + rPos, + CompareIMarkStartsAfter()); + } + + // create helper bookmark for annotations on tracked deletions + ::sw::mark::IMark* MarkManager::makeAnnotationBookmark(const SwPaM& rPaM, + const OUString& rName, + const IDocumentMarkAccess::MarkType eType, + sw::mark::InsertMode const eMode, + SwPosition const*const pSepPos) + { + OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK); + return makeMark( rPaM, sAnnotationBookmarkName, eType, eMode, pSepPos); + } + + // find helper bookmark of annotations on tracked deletions + IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationBookmark(const OUString& rName) const + { + OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK); + return findBookmark(sAnnotationBookmarkName); + } + + // restore text ranges of annotations on tracked deletions + // based on the helper bookmarks (which can survive I/O and hiding redlines) + void MarkManager::restoreAnnotationMarks(bool bDelete) + { + for (auto iter = getBookmarksBegin(); + iter != getBookmarksEnd(); ) + { + const OUString & rBookmarkName = (**iter).GetName(); + sal_Int32 nPos; + if ( rBookmarkName.startsWith("__Annotation__") && + (nPos = rBookmarkName.indexOf(S_ANNOTATION_BOOKMARK)) > -1 ) + { + ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo()); + IDocumentMarkAccess::const_iterator_t pMark = findAnnotationMark(rBookmarkName.copy(0, nPos)); + if ( pMark != getAnnotationMarksEnd() ) + { + const SwPaM aPam((**iter).GetMarkStart(), (**pMark).GetMarkEnd()); + repositionMark(*pMark, aPam); + } + if (bDelete) + { + deleteMark(&**iter); + // this invalidates iter, have to start over... + iter = getBookmarksBegin(); + } + else + ++iter; + } + else + ++iter; + } + } + + OUString MarkManager::getUniqueMarkName(const OUString& rName) const + { + OSL_ENSURE(rName.getLength(), + "<MarkManager::getUniqueMarkName(..)> - a name should be proposed"); + if( m_rDoc.IsInMailMerge()) + { + OUString newName = rName + "MailMergeMark" + + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US ) + + OUString::number( m_vAllMarks.size() + 1 ); + return newName; + } + + if (lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end()) + { + return rName; + } + OUString sTmp; + + // try the name "<rName>XXX" (where XXX is a number starting from 1) unless there is + // an 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; + OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rName); + while(nCnt < SAL_MAX_INT32) + { + sTmp = aPrefix + OUString::number(nCnt); + nCnt++; + if (lcl_FindMarkByName(sTmp, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end()) + { + break; + } + } + m_aMarkBasenameMapUniqueOffset[rName] = nCnt; + + return sTmp; + } + + void MarkManager::assureSortedMarkContainers() const + { + const_cast< MarkManager* >(this)->sortMarks(); + } + + void MarkManager::sortSubsetMarks() + { + stable_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} + }; + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager")); + for (const auto & rContainer : aContainers) + { + if (!rContainer.pContainer->empty()) + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName)); + for (auto it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it) + (*it)->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); + } + } + (void)xmlTextWriterEndElement(pWriter); +} + +} // namespace ::sw::mark + +namespace +{ + bool lcl_Greater( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx ) + { + return rPos.GetNode() > rNdIdx || + ( oContentIdx && rPos.GetNode() == rNdIdx && rPos.GetContentIndex() > *oContentIdx ); + } +} + +// 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 SwNode& rMvPos, + std::optional<sal_Int32> oContentIdx) + : m_aName(rBkmk.GetName()) + , m_bHidden(false) + , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk)) +{ + const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk); + if(pBookmark) + { + m_aShortName = pBookmark->GetShortName(); + m_aCode = pBookmark->GetKeyCode(); + m_bHidden = pBookmark->IsHidden(); + m_aHideCondition = pBookmark->GetHideCondition(); + + ::sfx2::Metadatable const*const pMetadatable( + dynamic_cast< ::sfx2::Metadatable const* >(pBookmark)); + if (pMetadatable) + { + m_pMetadataUndo = pMetadatable->CreateUndo(); + } + } + m_nNode1 = rBkmk.GetMarkPos().GetNodeIndex(); + m_nContent1 = rBkmk.GetMarkPos().GetContentIndex(); + + m_nNode1 -= rMvPos.GetIndex(); + if(oContentIdx && !m_nNode1) + m_nContent1 -= *oContentIdx; + + if(rBkmk.IsExpanded()) + { + m_nNode2 = rBkmk.GetOtherMarkPos().GetNodeIndex(); + m_nContent2 = rBkmk.GetOtherMarkPos().GetContentIndex(); + + m_nNode2 -= rMvPos.GetIndex(); + if(oContentIdx && !m_nNode2) + m_nContent2 -= *oContentIdx; + } + else + { + m_nNode2 = NODE_OFFSET_MAX; + m_nContent2 = -1; + } +} + +void SaveBookmark::SetInDoc( + SwDoc* pDoc, + const SwNode& rNewPos, + std::optional<sal_Int32> oContentIdx) +{ + SwPaM aPam(rNewPos); + if(oContentIdx) + { + if (aPam.GetPoint()->GetNode().IsContentNode()) + aPam.GetPoint()->SetContent( *oContentIdx ); + else + SAL_WARN("sw", "trying to sent content index, but point node is not a content node"); + } + + if(NODE_OFFSET_MAX != m_nNode2) + { + aPam.SetMark(); + + aPam.GetMark()->Adjust(m_nNode2); + if (aPam.GetMark()->GetNode().IsContentNode()) + { + if(oContentIdx && !m_nNode2) + aPam.GetMark()->SetContent(*oContentIdx + m_nContent2); + else + aPam.GetMark()->SetContent(m_nContent2); + } + else + SAL_WARN("sw", "trying to sent content index, but mark node is not a content node"); + } + + aPam.GetPoint()->Adjust(m_nNode1); + + if (aPam.GetPoint()->GetNode().IsContentNode()) + { + if(oContentIdx && !m_nNode1) + aPam.GetPoint()->SetContent(*oContentIdx + m_nContent1); + else + aPam.GetPoint()->SetContent(m_nContent1); + } + + if(aPam.HasMark() + && !CheckNodesRange(aPam.GetPoint()->GetNode(), aPam.GetMark()->GetNode(), true)) + return; + + ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>( + pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName, + m_eOrigBkmType, sw::mark::InsertMode::CopyText)); + if(!pBookmark) + return; + + pBookmark->SetKeyCode(m_aCode); + pBookmark->SetShortName(m_aShortName); + pBookmark->Hide(m_bHidden); + pBookmark->SetHideCondition(m_aHideCondition); + + 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( + SwNode& rStt, + const SwNode& rEnd, + std::vector<SaveBookmark> * pSaveBkmk, + std::optional<sal_Int32> oStartContentIdx, + std::optional<sal_Int32> oEndContentIdx, + bool const isReplace) +{ + // illegal range ?? + if(rStt.GetIndex() > rEnd.GetIndex() + || (&rStt == &rEnd && (!oStartContentIdx || !oEndContentIdx || *oStartContentIdx >= *oEndContentIdx))) + return; + SwDoc& rDoc = rStt.GetDoc(); + + rDoc.getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk, + oStartContentIdx, + oEndContentIdx, + isReplace); + + // 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 = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); + for(SwRangeRedline* pRedl : rTable) + { + // Is at position? + auto [pRStt, pREnd] = pRedl->StartEnd(); + + if( lcl_Greater( *pRStt, rStt, oStartContentIdx ) && lcl_Lower( *pRStt, rEnd, oEndContentIdx )) + { + pRStt->Assign( rEnd ); + if( oEndContentIdx ) + pRStt->SetContent( *oEndContentIdx ); + else + { + bool bStt = true; + SwContentNode* pCNd = pRStt->GetNode().GetContentNode(); + if( !pCNd ) + pCNd = rDoc.GetNodes().GoNext( pRStt ); + if (!pCNd) + { + bStt = false; + pRStt->Assign(rStt); + pCNd = SwNodes::GoPrevious( pRStt ); + if( !pCNd ) + { + *pRStt = *pREnd; + pCNd = pRStt->GetNode().GetContentNode(); + } + } + if (pCNd && !bStt) + pRStt->AssignEndIndex( *pCNd ); + } + } + if( lcl_Greater( *pREnd, rStt, oStartContentIdx ) && lcl_Lower( *pREnd, rEnd, oEndContentIdx )) + { + pREnd->Assign( rStt ); + if (oStartContentIdx && rStt.IsContentNode()) + pREnd->SetContent( *oStartContentIdx ); + else + { + bool bStt = false; + SwContentNode* pCNd = pREnd->GetNode().GetContentNode(); + if( !pCNd ) + pCNd = SwNodes::GoPrevious( pREnd ); + if( !pCNd ) + { + bStt = true; + pREnd->Assign(rEnd); + pCNd = rDoc.GetNodes().GoNext( pREnd ); + if( !pCNd ) + { + *pREnd = *pRStt; + pCNd = pREnd->GetNode().GetContentNode(); + } + } + if (pCNd && !bStt) + pREnd->AssignEndIndex( *pCNd ); + } + } + } +} + +namespace sw { + +InsertText MakeInsertText(SwTextNode& rNode, const sal_Int32 nPos, const sal_Int32 nLen) +{ + SwCursor cursor(SwPosition(rNode, nPos), nullptr); + bool isInsideFieldmarkCommand(false); + bool isInsideFieldmarkResult(false); + while (auto const*const pMark = rNode.GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(*cursor.GetPoint())) + { + if (sw::mark::FindFieldSep(*pMark) < *cursor.GetPoint()) + { + isInsideFieldmarkResult = true; + } + else + { + isInsideFieldmarkCommand = true; + } + *cursor.GetPoint() = pMark->GetMarkStart(); + if (!cursor.Left(1)) + { + break; + } + } + return InsertText(nPos, nLen, isInsideFieldmarkCommand, isInsideFieldmarkResult); +} + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |