xref: /core/sw/source/core/doc/docbm.cxx (revision 2ab4c442)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <utility>
22 
23 #include <MarkManager.hxx>
24 #include <bookmark.hxx>
25 #include <crossrefbookmark.hxx>
26 #include <crsrsh.hxx>
27 #include <annotationmark.hxx>
28 #include <doc.hxx>
29 #include <IDocumentRedlineAccess.hxx>
30 #include <IDocumentState.hxx>
31 #include <IDocumentUndoRedo.hxx>
32 #include <docary.hxx>
33 #include <xmloff/odffields.hxx>
34 #include <mvsave.hxx>
35 #include <ndtxt.hxx>
36 #include <node.hxx>
37 #include <pam.hxx>
38 #include <redline.hxx>
39 #include <rolbck.hxx>
40 #include <rtl/ustring.hxx>
41 #include <sal/types.h>
42 #include <sal/log.hxx>
43 #include <UndoBookmark.hxx>
44 #include <tools/datetimeutils.hxx>
45 #include <txtfrm.hxx>
46 #include <view.hxx>
47 
48 #include <libxml/xmlstring.h>
49 #include <libxml/xmlwriter.h>
50 #include <comphelper/lok.hxx>
51 #include <strings.hrc>
52 
53 constexpr OUString S_ANNOTATION_BOOKMARK = u"____"_ustr;
54 
55 using namespace ::sw::mark;
56 
57 std::vector<::sw::mark::MarkBase*>::const_iterator const&
get() const58 IDocumentMarkAccess::iterator::get() const
59 {
60     return *m_pIter;
61 }
62 
iterator(std::vector<::sw::mark::MarkBase * >::const_iterator const & rIter)63 IDocumentMarkAccess::iterator::iterator(std::vector<::sw::mark::MarkBase*>::const_iterator const& rIter)
64     : m_pIter(rIter)
65 {
66 }
67 
iterator(iterator const & rOther)68 IDocumentMarkAccess::iterator::iterator(iterator const& rOther)
69     : m_pIter(rOther.m_pIter)
70 {
71 }
72 
operator =(iterator const & rOther)73 auto IDocumentMarkAccess::iterator::operator=(iterator const& rOther) -> iterator&
74 {
75     m_pIter = rOther.m_pIter;
76     return *this;
77 }
78 
iterator(iterator && rOther)79 IDocumentMarkAccess::iterator::iterator(iterator && rOther) noexcept
80     : m_pIter(std::move(rOther.m_pIter))
81 {
82 }
83 
operator =(iterator && rOther)84 auto IDocumentMarkAccess::iterator::operator=(iterator && rOther) noexcept -> iterator&
85 {
86     m_pIter = std::move(rOther.m_pIter);
87     return *this;
88 }
89 
90 // ARGH why does it *need* to return const& ?
91 ::sw::mark::IMark* /*const&*/
operator *() const92 IDocumentMarkAccess::iterator::operator*() const
93 {
94     return static_cast<sw::mark::IMark*>(**m_pIter);
95 }
96 
operator ++()97 auto IDocumentMarkAccess::iterator::operator++() -> iterator&
98 {
99     ++(*m_pIter);
100     return *this;
101 }
operator ++(int)102 auto IDocumentMarkAccess::iterator::operator++(int) -> iterator
103 {
104     iterator tmp(*this);
105     ++(*m_pIter);
106     return tmp;
107 }
108 
operator ==(iterator const & rOther) const109 bool IDocumentMarkAccess::iterator::operator==(iterator const& rOther) const
110 {
111     return *m_pIter == *rOther.m_pIter;
112 }
113 
operator !=(iterator const & rOther) const114 bool IDocumentMarkAccess::iterator::operator!=(iterator const& rOther) const
115 {
116     return *m_pIter != *rOther.m_pIter;
117 }
118 
iterator()119 IDocumentMarkAccess::iterator::iterator()
120     : m_pIter(std::in_place)
121 {
122 }
123 
operator --()124 auto IDocumentMarkAccess::iterator::operator--() -> iterator&
125 {
126     --(*m_pIter);
127     return *this;
128 }
129 
operator --(int)130 auto IDocumentMarkAccess::iterator::operator--(int) -> iterator
131 {
132     iterator tmp(*this);
133     --(*m_pIter);
134     return tmp;
135 }
136 
operator +=(difference_type const n)137 auto IDocumentMarkAccess::iterator::operator+=(difference_type const n) -> iterator&
138 {
139     (*m_pIter) += n;
140     return *this;
141 }
142 
operator +(difference_type const n) const143 auto IDocumentMarkAccess::iterator::operator+(difference_type const n) const -> iterator
144 {
145     return iterator(*m_pIter + n);
146 }
147 
operator -=(difference_type const n)148 auto IDocumentMarkAccess::iterator::operator-=(difference_type const n) -> iterator&
149 {
150     (*m_pIter) -= n;
151     return *this;
152 }
153 
operator -(difference_type const n) const154 auto IDocumentMarkAccess::iterator::operator-(difference_type const n) const -> iterator
155 {
156     return iterator(*m_pIter - n);
157 }
158 
operator -(iterator const & rOther) const159 auto IDocumentMarkAccess::iterator::operator-(iterator const& rOther) const -> difference_type
160 {
161     return *m_pIter - *rOther.m_pIter;
162 }
163 
operator [](difference_type const n) const164 auto IDocumentMarkAccess::iterator::operator[](difference_type const n) const -> value_type
165 {
166     return static_cast<sw::mark::IMark*>((*m_pIter)[n]);
167 }
168 
operator <(iterator const & rOther) const169 bool IDocumentMarkAccess::iterator::operator<(iterator const& rOther) const
170 {
171     return *m_pIter < *rOther.m_pIter;
172 }
operator >(iterator const & rOther) const173 bool IDocumentMarkAccess::iterator::operator>(iterator const& rOther) const
174 {
175     return *m_pIter > *rOther.m_pIter;
176 }
operator <=(iterator const & rOther) const177 bool IDocumentMarkAccess::iterator::operator<=(iterator const& rOther) const
178 {
179     return *m_pIter <= *rOther.m_pIter;
180 }
operator >=(iterator const & rOther) const181 bool IDocumentMarkAccess::iterator::operator>=(iterator const& rOther) const
182 {
183     return *m_pIter >= *rOther.m_pIter;
184 }
185 
186 
187 namespace
188 {
lcl_GreaterThan(const SwPosition & rPos,const SwNode & rNdIdx,std::optional<sal_Int32> oContentIdx)189     bool lcl_GreaterThan( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
190     {
191         return oContentIdx.has_value()
192                ? ( rPos.GetNode() > rNdIdx
193                    || ( rPos.GetNode() == rNdIdx
194                         && rPos.GetContentIndex() >= *oContentIdx ) )
195                : rPos.GetNode() >= rNdIdx;
196     }
197 
lcl_Lower(const SwPosition & rPos,const SwNode & rNdIdx,std::optional<sal_Int32> oContentIdx)198     bool lcl_Lower( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
199     {
200         if (rPos.GetNode() < rNdIdx)
201             return true;
202 
203         if (rPos.GetNode() != rNdIdx || !oContentIdx)
204             return false;
205 
206         if (rPos.GetContentIndex() < *oContentIdx)
207             return true;
208 
209         // paragraph end selected?
210         return rNdIdx.IsTextNode() && *oContentIdx == rNdIdx.GetTextNode()->Len();
211     }
212 
lcl_MarkOrderingByStart(const::sw::mark::MarkBase * const pFirst,const::sw::mark::MarkBase * const pSecond)213     bool lcl_MarkOrderingByStart(const ::sw::mark::MarkBase *const pFirst,
214                                  const ::sw::mark::MarkBase *const pSecond)
215     {
216         SwPosition const& rFirstStart(pFirst->GetMarkStart());
217         SwPosition const& rSecondStart(pSecond->GetMarkStart());
218         if (rFirstStart.GetNode() != rSecondStart.GetNode())
219         {
220             return rFirstStart.GetNode() < rSecondStart.GetNode();
221         }
222         const sal_Int32 nFirstContent = rFirstStart.GetContentIndex();
223         const sal_Int32 nSecondContent = rSecondStart.GetContentIndex();
224         if (nFirstContent != 0 || nSecondContent != 0)
225         {
226             return nFirstContent < nSecondContent;
227         }
228         SwContentNode const*const pFirstNode(rFirstStart.nContent.GetContentNode());
229         SwContentNode const*const pSecondNode(rSecondStart.nContent.GetContentNode());
230         if ((pFirstNode != nullptr) != (pSecondNode != nullptr))
231         {   // consistency with SwPosition::operator<
232             return pSecondNode != nullptr;
233         }
234         auto *const pCRFirst (dynamic_cast<::sw::mark::CrossRefBookmark const*>(pFirst));
235         auto *const pCRSecond(dynamic_cast<::sw::mark::CrossRefBookmark const*>(pSecond));
236         if ((pCRFirst == nullptr) == (pCRSecond == nullptr))
237         {
238             return false; // equal
239         }
240         return pCRFirst != nullptr; // cross-ref sorts *before*
241     }
242 
lcl_MarkOrderingByEnd(const::sw::mark::MarkBase * const pFirst,const::sw::mark::MarkBase * const pSecond)243     bool lcl_MarkOrderingByEnd(const ::sw::mark::MarkBase *const pFirst,
244                                const ::sw::mark::MarkBase *const pSecond)
245     {
246         return pFirst->GetMarkEnd() < pSecond->GetMarkEnd();
247     }
248 
lcl_InsertMarkSorted(MarkManager::container_t & io_vMarks,::sw::mark::MarkBase * const pMark)249     void lcl_InsertMarkSorted(MarkManager::container_t& io_vMarks,
250                               ::sw::mark::MarkBase *const pMark)
251     {
252         io_vMarks.insert(
253             lower_bound(
254                 io_vMarks.begin(),
255                 io_vMarks.end(),
256                 pMark,
257                 &lcl_MarkOrderingByStart),
258             pMark);
259     }
260 
lcl_PositionFromContentNode(std::optional<SwPosition> & rFoundPos,const SwContentNode * const pContentNode,const bool bAtEnd)261     void lcl_PositionFromContentNode(
262         std::optional<SwPosition>& rFoundPos,
263         const SwContentNode * const pContentNode,
264         const bool bAtEnd)
265     {
266         rFoundPos.emplace(*pContentNode, bAtEnd ? pContentNode->Len() : 0);
267     }
268 
269     // return a position at the begin of rEnd, if it is a ContentNode
270     // else set it to the begin of the Node after rEnd, if there is one
271     // else set it to the end of the node before rStt
272     // else set it to the ContentNode of the Pos outside the Range
lcl_FindExpelPosition(std::optional<SwPosition> & rFoundPos,const SwNode & rStt,const SwNode & rEnd,const SwPosition & rOtherPosition)273     void lcl_FindExpelPosition(
274         std::optional<SwPosition>& rFoundPos,
275         const SwNode& rStt,
276         const SwNode& rEnd,
277         const SwPosition& rOtherPosition)
278     {
279         const SwContentNode * pNode = rEnd.GetContentNode();
280         bool bPosAtEndOfNode = false;
281         if ( pNode == nullptr)
282         {
283             SwNodeIndex aEnd(rEnd);
284             pNode = SwNodes::GoNext(&aEnd);
285             bPosAtEndOfNode = false;
286         }
287         if ( pNode == nullptr )
288         {
289             SwNodeIndex aStt(rStt);
290             pNode = SwNodes::GoPrevious(&aStt);
291             bPosAtEndOfNode = true;
292         }
293         if ( pNode != nullptr )
294         {
295             lcl_PositionFromContentNode( rFoundPos, pNode, bPosAtEndOfNode );
296             return;
297         }
298 
299         rFoundPos = rOtherPosition;
300     }
301 
302     struct CompareIMarkStartsBefore
303     {
operator ()__anon3b4322910111::CompareIMarkStartsBefore304         bool operator()(SwPosition const& rPos,
305                         const sw::mark::IMark* pMark)
306         {
307             return rPos < pMark->GetMarkStart();
308         }
operator ()__anon3b4322910111::CompareIMarkStartsBefore309         bool operator()(const sw::mark::IMark* pMark,
310                         SwPosition const& rPos)
311         {
312             return pMark->GetMarkStart() < rPos;
313         }
314     };
315 
316     // Apple llvm-g++ 4.2.1 with _GLIBCXX_DEBUG won't eat boost::bind for this
317     // Neither will MSVC 2008 with _DEBUG
318     struct CompareIMarkStartsAfter
319     {
operator ()__anon3b4322910111::CompareIMarkStartsAfter320         bool operator()(SwPosition const& rPos,
321                         const sw::mark::IMark* pMark)
322         {
323             return pMark->GetMarkStart() > rPos;
324         }
325     };
326 
327 
lcl_getMarkAfter(const MarkManager::container_t & rMarks,const SwPosition & rPos,bool bLoop)328     IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos,
329                             bool bLoop)
330     {
331         auto const pMarkAfter = upper_bound(
332             rMarks.begin(),
333             rMarks.end(),
334             rPos,
335             CompareIMarkStartsAfter());
336         if(pMarkAfter == rMarks.end())
337         {
338             if (bLoop && rMarks.begin() != rMarks.end())
339                 return *rMarks.begin();
340 
341             return nullptr;
342         }
343         return *pMarkAfter;
344     };
345 
lcl_getMarkBefore(const MarkManager::container_t & rMarks,const SwPosition & rPos,bool bLoop)346     IMark* lcl_getMarkBefore(const MarkManager::container_t& rMarks, const SwPosition& rPos,
347                              bool bLoop)
348     {
349         // candidates from which to choose the mark before
350         MarkManager::container_t vCandidates;
351         // no need to consider marks starting after rPos
352         auto const pCandidatesEnd = upper_bound(
353             rMarks.begin(),
354             rMarks.end(),
355             rPos,
356             CompareIMarkStartsAfter());
357         vCandidates.reserve(pCandidatesEnd - rMarks.begin());
358         // only marks ending before are candidates
359         remove_copy_if(
360             rMarks.begin(),
361             pCandidatesEnd,
362             back_inserter(vCandidates),
363             [&rPos] (const ::sw::mark::MarkBase *const pMark) { return !(pMark->GetMarkEnd() < rPos); } );
364         // no candidate left => we are in front of the first mark or there are none
365         if(vCandidates.empty())
366         {
367             if (bLoop && rMarks.begin() != rMarks.end())
368                 return *(rMarks.end() - 1);
369 
370             return nullptr;
371         }
372         // return the highest (last) candidate using mark end ordering
373         return *max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd);
374     }
375 
lcl_FixCorrectedMark(const bool bChangedPos,const bool bChangedOPos,MarkBase * io_pMark)376     bool lcl_FixCorrectedMark(
377         const bool bChangedPos,
378         const bool bChangedOPos,
379         MarkBase* io_pMark )
380     {
381         if ( IDocumentMarkAccess::GetType(*io_pMark) == IDocumentMarkAccess::MarkType::ANNOTATIONMARK )
382         {
383             // annotation marks are allowed to span a table cell range.
384             // but trigger sorting to be save
385             return true;
386         }
387 
388         if ( ( bChangedPos || bChangedOPos )
389              && io_pMark->IsExpanded()
390              && io_pMark->GetOtherMarkPos().GetNode().FindTableBoxStartNode() !=
391                     io_pMark->GetMarkPos().GetNode().FindTableBoxStartNode() )
392         {
393             if ( !bChangedOPos )
394             {
395                 io_pMark->SetMarkPos( io_pMark->GetOtherMarkPos() );
396             }
397             io_pMark->ClearOtherMarkPos();
398             DdeBookmark * const pDdeBkmk = dynamic_cast< DdeBookmark*>(io_pMark);
399             if ( pDdeBkmk != nullptr
400                  && pDdeBkmk->IsServer() )
401             {
402                 pDdeBkmk->SetRefObject(nullptr);
403             }
404             return true;
405         }
406         return false;
407     }
408 
lcl_MarkEqualByStart(const::sw::mark::MarkBase * const pFirst,const::sw::mark::MarkBase * const pSecond)409     bool lcl_MarkEqualByStart(const ::sw::mark::MarkBase *const pFirst,
410                               const ::sw::mark::MarkBase *const pSecond)
411     {
412         return !lcl_MarkOrderingByStart(pFirst, pSecond) &&
413                !lcl_MarkOrderingByStart(pSecond, pFirst);
414     }
415 
lcl_FindMark(MarkManager::container_t & rMarks,const::sw::mark::MarkBase * const pMarkToFind)416     MarkManager::container_t::const_iterator lcl_FindMark(
417         MarkManager::container_t& rMarks,
418         const ::sw::mark::MarkBase *const pMarkToFind)
419     {
420         auto ppCurrentMark = lower_bound(
421             rMarks.begin(), rMarks.end(),
422             pMarkToFind, &lcl_MarkOrderingByStart);
423         // since there are usually not too many marks on the same start
424         // position, we are not doing a bisect search for the upper bound
425         // but instead start to iterate from pMarkLow directly
426         while (ppCurrentMark != rMarks.end() && lcl_MarkEqualByStart(*ppCurrentMark, pMarkToFind))
427         {
428             if(*ppCurrentMark == pMarkToFind)
429             {
430                 return MarkManager::container_t::const_iterator(std::move(ppCurrentMark));
431             }
432             ++ppCurrentMark;
433         }
434         // reached a mark starting on a later start pos or the end of the
435         // vector => not found
436         return rMarks.end();
437     };
438 
lcl_FindMarkAtPos(MarkManager::container_t & rMarks,const SwPosition & rPos,const IDocumentMarkAccess::MarkType eType)439     MarkManager::container_t::const_iterator lcl_FindMarkAtPos(
440         MarkManager::container_t& rMarks,
441         const SwPosition& rPos,
442         const IDocumentMarkAccess::MarkType eType)
443     {
444         for (auto ppCurrentMark = lower_bound(
445                 rMarks.begin(), rMarks.end(),
446                 rPos,
447                 CompareIMarkStartsBefore());
448             ppCurrentMark != rMarks.end();
449             ++ppCurrentMark)
450         {
451             // Once we reach a mark starting after the target pos
452             // we do not need to continue
453             if((*ppCurrentMark)->GetMarkStart() > rPos)
454                 break;
455             if(IDocumentMarkAccess::GetType(**ppCurrentMark) == eType)
456             {
457                 return MarkManager::container_t::const_iterator(std::move(ppCurrentMark));
458             }
459         }
460         // reached a mark starting on a later start pos or the end of the
461         // vector => not found
462         return rMarks.end();
463     };
464 
lcl_FindMarkByName(const OUString & rName,const MarkManager::container_t::const_iterator & ppMarksBegin,const MarkManager::container_t::const_iterator & ppMarksEnd)465     MarkManager::container_t::const_iterator lcl_FindMarkByName(
466         const OUString& rName,
467         const MarkManager::container_t::const_iterator& ppMarksBegin,
468         const MarkManager::container_t::const_iterator& ppMarksEnd)
469     {
470         return find_if(
471             ppMarksBegin,
472             ppMarksEnd,
473             [&rName] (::sw::mark::MarkBase const*const pMark) { return pMark->GetName() == rName; } );
474     }
475 
lcl_DebugMarks(MarkManager::container_t const & rMarks)476     void lcl_DebugMarks(MarkManager::container_t const& rMarks)
477     {
478 #if OSL_DEBUG_LEVEL > 0
479         SAL_INFO("sw.core", rMarks.size() << " Marks");
480         for (auto ppMark = rMarks.begin();
481              ppMark != rMarks.end();
482              ++ppMark)
483         {
484             IMark* pMark = *ppMark;
485             const SwPosition* const pStPos = &pMark->GetMarkStart();
486             const SwPosition* const pEndPos = &pMark->GetMarkEnd();
487             SAL_INFO("sw.core",
488                 sal_Int32(pStPos->GetNodeIndex()) << "," <<
489                 pStPos->GetContentIndex() << " " <<
490                 sal_Int32(pEndPos->GetNodeIndex()) << "," <<
491                 pEndPos->GetContentIndex() << " " <<
492                 typeid(*pMark).name() << " " <<
493                 pMark->GetName());
494         }
495 #else
496         (void) rMarks;
497 #endif
498         assert(std::is_sorted(rMarks.begin(), rMarks.end(), lcl_MarkOrderingByStart));
499     };
500 }
501 
GetType(const IMark & rBkmk)502 IDocumentMarkAccess::MarkType IDocumentMarkAccess::GetType(const IMark& rBkmk)
503 {
504     const std::type_info* const pMarkTypeInfo = &typeid(rBkmk);
505     // not using dynamic_cast<> here for performance
506     if(*pMarkTypeInfo == typeid(UnoMark))
507         return MarkType::UNO_BOOKMARK;
508     else if(*pMarkTypeInfo == typeid(DdeBookmark))
509         return MarkType::DDE_BOOKMARK;
510     else if(*pMarkTypeInfo == typeid(Bookmark))
511         return MarkType::BOOKMARK;
512     else if(*pMarkTypeInfo == typeid(CrossRefHeadingBookmark))
513         return MarkType::CROSSREF_HEADING_BOOKMARK;
514     else if(*pMarkTypeInfo == typeid(CrossRefNumItemBookmark))
515         return MarkType::CROSSREF_NUMITEM_BOOKMARK;
516     else if(*pMarkTypeInfo == typeid(AnnotationMark))
517         return MarkType::ANNOTATIONMARK;
518     else if(*pMarkTypeInfo == typeid(TextFieldmark))
519         return MarkType::TEXT_FIELDMARK;
520     else if(*pMarkTypeInfo == typeid(CheckboxFieldmark))
521         return MarkType::CHECKBOX_FIELDMARK;
522     else if(*pMarkTypeInfo == typeid(DropDownFieldmark))
523         return MarkType::DROPDOWN_FIELDMARK;
524     else if(*pMarkTypeInfo == typeid(DateFieldmark))
525         return MarkType::DATE_FIELDMARK;
526     else if(*pMarkTypeInfo == typeid(NavigatorReminder))
527         return MarkType::NAVIGATOR_REMINDER;
528     else
529     {
530         assert(false && "IDocumentMarkAccess::GetType(..)"
531             " - unknown MarkType. This needs to be fixed!");
532         return MarkType::UNO_BOOKMARK;
533     }
534 }
535 
GetCrossRefHeadingBookmarkNamePrefix()536 OUString IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()
537 {
538     return u"__RefHeading__"_ustr;
539 }
540 
IsLegalPaMForCrossRefHeadingBookmark(const SwPaM & rPaM)541 bool IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( const SwPaM& rPaM )
542 {
543     return rPaM.Start()->GetNode().IsTextNode() &&
544            rPaM.Start()->GetContentIndex() == 0 &&
545            ( !rPaM.HasMark() ||
546              ( rPaM.GetMark()->GetNode() == rPaM.GetPoint()->GetNode() &&
547                rPaM.End()->GetContentIndex() == rPaM.End()->GetNode().GetTextNode()->Len() ) );
548 }
549 
DeleteFieldmarkCommand(::sw::mark::IFieldmark const & rMark)550 void IDocumentMarkAccess::DeleteFieldmarkCommand(::sw::mark::IFieldmark const& rMark)
551 {
552     if (GetType(rMark) != MarkType::TEXT_FIELDMARK)
553     {
554         return; // TODO FORMDATE has no command?
555     }
556     SwPaM pam(sw::mark::FindFieldSep(rMark), rMark.GetMarkStart());
557     pam.GetPoint()->AdjustContent(+1); // skip CH_TXT_ATR_FIELDSTART
558     pam.GetDoc().getIDocumentContentOperations().DeleteAndJoin(pam);
559 }
560 
561 namespace sw::mark
562 {
MarkManager(SwDoc & rDoc)563     MarkManager::MarkManager(SwDoc& rDoc)
564         : m_rDoc(rDoc)
565         , m_pLastActiveFieldmark(nullptr)
566     { }
567 
makeMark(const SwPaM & rPaM,const OUString & rName,const IDocumentMarkAccess::MarkType eType,sw::mark::InsertMode const eMode,SwPosition const * const pSepPos)568     ::sw::mark::IMark* MarkManager::makeMark(const SwPaM& rPaM,
569         const OUString& rName,
570         const IDocumentMarkAccess::MarkType eType,
571         sw::mark::InsertMode const eMode,
572         SwPosition const*const pSepPos)
573     {
574 #if OSL_DEBUG_LEVEL > 0
575         {
576             const SwPosition* const pPos1 = rPaM.GetPoint();
577             const SwPosition* pPos2 = pPos1;
578             if(rPaM.HasMark())
579                 pPos2 = rPaM.GetMark();
580             SAL_INFO("sw.core",
581                 rName << " " <<
582                 sal_Int32(pPos1->GetNodeIndex() )<< "," <<
583                 pPos1->GetContentIndex() << " " <<
584                 sal_Int32(pPos2->GetNodeIndex()) << "," <<
585                 pPos2->GetContentIndex());
586         }
587 #endif
588         if (   (!rPaM.GetPoint()->GetNode().IsTextNode()
589                 && (eType != MarkType::UNO_BOOKMARK
590                 // SwXTextRange can be on table node or plain start node (FLY_AT_FLY)
591                     || !rPaM.GetPoint()->GetNode().IsStartNode()))
592             || (!rPaM.GetMark()->GetNode().IsTextNode()
593                 && (eType != MarkType::UNO_BOOKMARK
594                     || !rPaM.GetMark()->GetNode().IsStartNode())))
595         {
596             SAL_WARN("sw.core", "MarkManager::makeMark(..)"
597                 " - refusing to create mark on non-textnode");
598             return nullptr;
599         }
600         // There should only be one CrossRefBookmark per Textnode per Type
601         if ((eType == MarkType::CROSSREF_NUMITEM_BOOKMARK || eType == MarkType::CROSSREF_HEADING_BOOKMARK)
602             && (lcl_FindMarkAtPos(m_vBookmarks, *rPaM.Start(), eType) != m_vBookmarks.end()))
603         {   // this can happen via UNO API
604             SAL_WARN("sw.core", "MarkManager::makeMark(..)"
605                 " - refusing to create duplicate CrossRefBookmark");
606             return nullptr;
607         }
608 
609         if ((eType == MarkType::CHECKBOX_FIELDMARK || eType == MarkType::DROPDOWN_FIELDMARK)
610             && (eMode == InsertMode::New
611                 ? *rPaM.GetPoint() != *rPaM.GetMark()
612                 // CopyText: pam covers CH_TXT_ATR_FORMELEMENT
613                 : (rPaM.GetPoint()->GetNode() != rPaM.GetMark()->GetNode()
614                     || rPaM.Start()->GetContentIndex() + 1 != rPaM.End()->GetContentIndex())))
615         {
616             SAL_WARN("sw.core", "MarkManager::makeMark(..)"
617                 " - invalid range on point fieldmark");
618             return nullptr;
619         }
620 
621         if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK)
622             && (rPaM.GetPoint()->GetNode().StartOfSectionNode() != rPaM.GetMark()->GetNode().StartOfSectionNode()
623                 || (pSepPos && rPaM.GetPoint()->GetNode().StartOfSectionNode() != pSepPos->GetNode().StartOfSectionNode())))
624         {
625             SAL_WARN("sw.core", "MarkManager::makeMark(..)"
626                 " - invalid range on fieldmark, different nodes array sections");
627             return nullptr;
628         }
629 
630         if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK)
631             // can't check for Copy - it asserts - but it's also obviously unnecessary
632             && eMode == InsertMode::New
633             && sw::mark::IsFieldmarkOverlap(rPaM))
634         {
635             SAL_WARN("sw.core", "MarkManager::makeMark(..)"
636                 " - invalid range on fieldmark, overlaps existing fieldmark or meta-field");
637             return nullptr;
638         }
639 
640         // create mark
641         std::unique_ptr<::sw::mark::MarkBase> pMark;
642         switch(eType)
643         {
644             case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
645                 pMark = std::make_unique<TextFieldmark>(rPaM, rName);
646                 break;
647             case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
648                 pMark = std::make_unique<CheckboxFieldmark>(rPaM, rName);
649                 break;
650             case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
651                 pMark = std::make_unique<DropDownFieldmark>(rPaM, rName);
652                 break;
653             case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
654                 pMark = std::make_unique<DateFieldmark>(rPaM);
655                 break;
656             case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
657                 pMark = std::make_unique<NavigatorReminder>(rPaM);
658                 break;
659             case IDocumentMarkAccess::MarkType::BOOKMARK:
660                 pMark = std::make_unique<Bookmark>(rPaM, vcl::KeyCode(), rName);
661                 break;
662             case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
663                 pMark = std::make_unique<DdeBookmark>(rPaM);
664                 break;
665             case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
666                 pMark = std::make_unique<CrossRefHeadingBookmark>(rPaM, vcl::KeyCode(), rName);
667                 break;
668             case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
669                 pMark = std::make_unique<CrossRefNumItemBookmark>(rPaM, vcl::KeyCode(), rName);
670                 break;
671             case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
672                 pMark = std::make_unique<UnoMark>(rPaM);
673                 break;
674             case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
675                 pMark = std::make_unique<AnnotationMark>( rPaM, rName );
676                 break;
677         }
678         assert(pMark && "MarkManager::makeMark(..) - Mark was not created.");
679 
680         if(pMark->GetMarkPos() != pMark->GetMarkStart())
681             pMark->Swap();
682 
683         // for performance reasons, we trust UnoMarks to have a (generated) unique name
684         if ( eType != IDocumentMarkAccess::MarkType::UNO_BOOKMARK )
685             pMark->SetName( getUniqueMarkName( pMark->GetName() ) );
686 
687         // insert any dummy chars before inserting into sorted vectors
688         pMark->InitDoc(m_rDoc, eMode, pSepPos);
689 
690         // register mark
691         lcl_InsertMarkSorted(m_vAllMarks, pMark.get());
692         switch(eType)
693         {
694             case IDocumentMarkAccess::MarkType::BOOKMARK:
695             case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
696             case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
697                 lcl_InsertMarkSorted(m_vBookmarks, pMark.get());
698                 break;
699             case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
700             case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
701             case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
702             case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
703                 lcl_InsertMarkSorted(m_vFieldmarks, pMark.get());
704                 break;
705             case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
706                 lcl_InsertMarkSorted( m_vAnnotationMarks, pMark.get() );
707                 break;
708             case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
709             case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
710             case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
711                 // no special array for these
712                 break;
713         }
714         if (eMode == InsertMode::New
715             && (eType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
716                 || eType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
717         {
718             // due to sw::InsertText notifications everything is visible now - tell
719             // layout to hide as appropriate
720             // note: we don't know how many layouts there are and which
721             // parts they hide, so just notify the entire fieldmark, it
722             // should give the right result if not in the most efficient way
723             // note2: can't be done in InitDoc() because it requires the mark
724             // to be inserted in the vectors.
725             SwPaM const tmp(pMark->GetMarkPos(), pMark->GetOtherMarkPos());
726             sw::UpdateFramesForAddDeleteRedline(m_rDoc, tmp);
727         }
728 
729         SAL_INFO("sw.core", "--- makeType ---");
730         SAL_INFO("sw.core", "Marks");
731         lcl_DebugMarks(m_vAllMarks);
732         SAL_INFO("sw.core", "Bookmarks");
733         lcl_DebugMarks(m_vBookmarks);
734         SAL_INFO("sw.core", "Fieldmarks");
735         lcl_DebugMarks(m_vFieldmarks);
736 
737         return pMark.release();
738     }
739 
makeFieldBookmark(const SwPaM & rPaM,const OUString & rName,const OUString & rType,SwPosition const * const pSepPos)740     ::sw::mark::IFieldmark* MarkManager::makeFieldBookmark(
741         const SwPaM& rPaM,
742         const OUString& rName,
743         const OUString& rType,
744         SwPosition const*const pSepPos)
745     {
746 
747         // Disable undo, because we handle it using SwUndoInsTextFieldmark
748         bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
749         m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
750 
751         sw::mark::IMark* pMark = nullptr;
752         if(rType == ODF_FORMDATE)
753         {
754             pMark = makeMark(rPaM, rName,
755                              IDocumentMarkAccess::MarkType::DATE_FIELDMARK,
756                              sw::mark::InsertMode::New,
757                              pSepPos);
758         }
759         else
760         {
761             pMark = makeMark(rPaM, rName,
762                              IDocumentMarkAccess::MarkType::TEXT_FIELDMARK,
763                              sw::mark::InsertMode::New,
764                              pSepPos);
765         }
766         sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
767         if (pFieldMark)
768             pFieldMark->SetFieldname( rType );
769 
770         if (bUndoIsEnabled)
771         {
772             m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled);
773             if (pFieldMark)
774                 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsTextFieldmark>(*pFieldMark));
775         }
776 
777         return pFieldMark;
778     }
779 
makeNoTextFieldBookmark(const SwPaM & rPaM,const OUString & rName,const OUString & rType)780     ::sw::mark::IFieldmark* MarkManager::makeNoTextFieldBookmark(
781         const SwPaM& rPaM,
782         const OUString& rName,
783         const OUString& rType)
784     {
785         // Disable undo, because we handle it using SwUndoInsNoTextFieldmark
786         bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
787         m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
788 
789         bool bEnableSetModified = m_rDoc.getIDocumentState().IsEnableSetModified();
790         m_rDoc.getIDocumentState().SetEnableSetModified(false);
791 
792         sw::mark::IMark* pMark = nullptr;
793         if(rType == ODF_FORMCHECKBOX)
794         {
795             pMark = makeMark( rPaM, rName,
796                     IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK,
797                     sw::mark::InsertMode::New);
798         }
799         else if(rType == ODF_FORMDROPDOWN)
800         {
801             pMark = makeMark( rPaM, rName,
802                     IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK,
803                     sw::mark::InsertMode::New);
804         }
805         else if(rType == ODF_FORMDATE)
806         {
807             pMark = makeMark( rPaM, rName,
808                     IDocumentMarkAccess::MarkType::DATE_FIELDMARK,
809                     sw::mark::InsertMode::New);
810         }
811 
812         sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
813         if (pFieldMark)
814             pFieldMark->SetFieldname( rType );
815 
816         if (bUndoIsEnabled)
817         {
818             m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled);
819             if (pFieldMark)
820                 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsNoTextFieldmark>(*pFieldMark));
821         }
822 
823         m_rDoc.getIDocumentState().SetEnableSetModified(bEnableSetModified);
824         m_rDoc.getIDocumentState().SetModified();
825 
826         return pFieldMark;
827     }
828 
getMarkForTextNode(const SwTextNode & rTextNode,const IDocumentMarkAccess::MarkType eType)829     ::sw::mark::IMark* MarkManager::getMarkForTextNode(
830         const SwTextNode& rTextNode,
831         const IDocumentMarkAccess::MarkType eType )
832     {
833         SwPosition aPos(rTextNode);
834         auto const ppExistingMark = lcl_FindMarkAtPos(m_vBookmarks, aPos, eType);
835         if(ppExistingMark != m_vBookmarks.end())
836             return *ppExistingMark;
837         const SwPaM aPaM(aPos);
838         return makeMark(aPaM, OUString(), eType, sw::mark::InsertMode::New);
839     }
840 
makeAnnotationMark(const SwPaM & rPaM,const OUString & rName)841     sw::mark::IMark* MarkManager::makeAnnotationMark(
842         const SwPaM& rPaM,
843         const OUString& rName )
844     {
845         return makeMark(rPaM, rName, IDocumentMarkAccess::MarkType::ANNOTATIONMARK,
846                 sw::mark::InsertMode::New);
847     }
848 
repositionMark(::sw::mark::IMark * const io_pMark,const SwPaM & rPaM)849     void MarkManager::repositionMark(
850         ::sw::mark::IMark* const io_pMark,
851         const SwPaM& rPaM)
852     {
853         assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc &&
854             "<MarkManager::repositionMark(..)>"
855             " - Mark is not in my doc.");
856         MarkBase* const pMarkBase = dynamic_cast< MarkBase* >(io_pMark);
857         if (!pMarkBase)
858             return;
859 
860         pMarkBase->InvalidateFrames();
861 
862         pMarkBase->SetMarkPos(*(rPaM.GetPoint()));
863         if(rPaM.HasMark())
864             pMarkBase->SetOtherMarkPos(*(rPaM.GetMark()));
865         else
866             pMarkBase->ClearOtherMarkPos();
867 
868         if(pMarkBase->GetMarkPos() != pMarkBase->GetMarkStart())
869             pMarkBase->Swap();
870 
871         pMarkBase->InvalidateFrames();
872 
873         sortMarks();
874     }
875 
renameMark(::sw::mark::IMark * io_pMark,const OUString & rNewName)876     bool MarkManager::renameMark(
877         ::sw::mark::IMark* io_pMark,
878         const OUString& rNewName )
879     {
880         assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc &&
881             "<MarkManager::renameMark(..)>"
882             " - Mark is not in my doc.");
883         if ( io_pMark->GetName() == rNewName )
884             return true;
885         if (lcl_FindMarkByName(rNewName, m_vAllMarks.begin(), m_vAllMarks.end()) != m_vAllMarks.end())
886             return false;
887         if (::sw::mark::MarkBase* pMarkBase = dynamic_cast< ::sw::mark::MarkBase* >(io_pMark))
888         {
889             const OUString sOldName(pMarkBase->GetName());
890             pMarkBase->SetName(rNewName);
891 
892             if (dynamic_cast< ::sw::mark::Bookmark* >(io_pMark))
893             {
894                 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
895                 {
896                     m_rDoc.GetIDocumentUndoRedo().AppendUndo(
897                             std::make_unique<SwUndoRenameBookmark>(sOldName, rNewName, m_rDoc));
898                 }
899                 m_rDoc.getIDocumentState().SetModified();
900             }
901         }
902         return true;
903     }
904 
correctMarksAbsolute(const SwNode & rOldNode,const SwPosition & rNewPos,const sal_Int32 nOffset)905     void MarkManager::correctMarksAbsolute(
906         const SwNode& rOldNode,
907         const SwPosition& rNewPos,
908         const sal_Int32 nOffset)
909     {
910         const SwNode* const pOldNode = &rOldNode;
911         SwPosition aNewPos(rNewPos);
912         aNewPos.AdjustContent(nOffset);
913         bool isSortingNeeded = false;
914 
915         for (auto ppMark = m_vAllMarks.begin();
916             ppMark != m_vAllMarks.end();
917             ++ppMark)
918         {
919             ::sw::mark::MarkBase *const pMark = *ppMark;
920             // correction of non-existent non-MarkBase instances cannot be done
921             assert(pMark);
922             // is on position ??
923             bool bChangedPos = false;
924             if(&pMark->GetMarkPos().GetNode() == pOldNode)
925             {
926                 pMark->SetMarkPos(aNewPos);
927                 bChangedPos = true;
928                 isSortingNeeded = true;
929             }
930             bool bChangedOPos = false;
931             if (pMark->IsExpanded() &&
932                 &pMark->GetOtherMarkPos().GetNode() == pOldNode)
933             {
934                 // shift the OtherMark to aNewPos
935                 pMark->SetOtherMarkPos(aNewPos);
936                 bChangedOPos= true;
937                 isSortingNeeded = true;
938             }
939             // illegal selection? collapse the mark and restore sorting later
940             isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
941         }
942 
943         // restore sorting if needed
944         if(isSortingNeeded)
945             sortMarks();
946 
947         SAL_INFO("sw.core", "correctMarksAbsolute");
948         lcl_DebugMarks(m_vAllMarks);
949     }
950 
correctMarksRelative(const SwNode & rOldNode,const SwPosition & rNewPos,const sal_Int32 nOffset)951     void MarkManager::correctMarksRelative(const SwNode& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset)
952     {
953         const SwNode* const pOldNode = &rOldNode;
954         SwPosition aNewPos(rNewPos);
955         aNewPos.AdjustContent(nOffset);
956         bool isSortingNeeded = false;
957 
958         for (auto ppMark = m_vAllMarks.begin();
959             ppMark != m_vAllMarks.end();
960             ++ppMark)
961         {
962             // is on position ??
963             bool bChangedPos = false, bChangedOPos = false;
964             ::sw::mark::MarkBase* const pMark = *ppMark;
965             // correction of non-existent non-MarkBase instances cannot be done
966             assert(pMark);
967             if(&pMark->GetMarkPos().GetNode() == pOldNode)
968             {
969                 SwPosition aNewPosRel(aNewPos);
970                 if (dynamic_cast< ::sw::mark::CrossRefBookmark *>(pMark))
971                 {
972                     // ensure that cross ref bookmark always starts at 0
973                     aNewPosRel.SetContent(0); // HACK for WW8 import
974                     isSortingNeeded = true; // and sort them to be safe...
975                 }
976                 aNewPosRel.AdjustContent(pMark->GetMarkPos().GetContentIndex());
977                 pMark->SetMarkPos(aNewPosRel);
978                 bChangedPos = true;
979             }
980             if(pMark->IsExpanded() &&
981                 &pMark->GetOtherMarkPos().GetNode() == pOldNode)
982             {
983                 SwPosition aNewPosRel(aNewPos);
984                 aNewPosRel.AdjustContent(pMark->GetOtherMarkPos().GetContentIndex());
985                 pMark->SetOtherMarkPos(aNewPosRel);
986                 bChangedOPos = true;
987             }
988             // illegal selection? collapse the mark and restore sorting later
989             isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
990         }
991 
992         // restore sorting if needed
993         if(isSortingNeeded)
994             sortMarks();
995 
996         SAL_INFO("sw.core", "correctMarksRelative");
997         lcl_DebugMarks(m_vAllMarks);
998     }
999 
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)1000     static bool isDeleteMark(
1001             ::sw::mark::MarkBase const*const pMark,
1002             bool const isReplace,
1003             SwNode const& rStt,
1004             SwNode const& rEnd,
1005             std::optional<sal_Int32> oStartContentIdx,
1006             std::optional<sal_Int32> oEndContentIdx,
1007             bool & rbIsPosInRange,
1008             bool & rbIsOtherPosInRange)
1009     {
1010         assert(pMark);
1011         // navigator marks should not be moved
1012         // TODO: Check if this might make them invalid
1013         if (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER)
1014         {
1015             return false;
1016         }
1017 
1018         // on position ??
1019         rbIsPosInRange = lcl_GreaterThan(pMark->GetMarkPos(), rStt, oStartContentIdx)
1020                             && lcl_Lower(pMark->GetMarkPos(), rEnd, oEndContentIdx);
1021         rbIsOtherPosInRange = pMark->IsExpanded()
1022                             && lcl_GreaterThan(pMark->GetOtherMarkPos(), rStt, oStartContentIdx)
1023                             && lcl_Lower(pMark->GetOtherMarkPos(), rEnd, oEndContentIdx);
1024         // special case: completely in range, touching the end?
1025         if ( oEndContentIdx.has_value()
1026              && !(isReplace && IDocumentMarkAccess::GetType(*pMark)
1027                                     == IDocumentMarkAccess::MarkType::BOOKMARK)
1028              && ( ( rbIsOtherPosInRange
1029                     && pMark->GetMarkPos().GetNode() == rEnd
1030                     && pMark->GetMarkPos().GetContentIndex() == *oEndContentIdx )
1031                   || ( rbIsPosInRange
1032                        && pMark->IsExpanded()
1033                        && pMark->GetOtherMarkPos().GetNode() == rEnd
1034                        && pMark->GetOtherMarkPos().GetContentIndex() == *oEndContentIdx ) ) )
1035         {
1036             rbIsPosInRange = true;
1037             rbIsOtherPosInRange = true;
1038         }
1039 
1040         if (rbIsPosInRange
1041              && (rbIsOtherPosInRange
1042                   || !pMark->IsExpanded()))
1043         {
1044             // completely in range
1045 
1046             bool bDeleteMark = true;
1047             {
1048                 switch ( IDocumentMarkAccess::GetType( *pMark ) )
1049                 {
1050                 case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
1051                 case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
1052                     // no delete of cross-reference bookmarks, if range is inside one paragraph
1053                     bDeleteMark = &rStt != &rEnd;
1054                     break;
1055                 case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
1056                     // no delete of UNO mark, if it is not expanded and only touches the start of the range
1057                     bDeleteMark = rbIsOtherPosInRange
1058                                   || pMark->IsExpanded()
1059                                   || !oStartContentIdx.has_value()
1060                                   || pMark->GetMarkPos().GetNode() != rStt
1061                                   || pMark->GetMarkPos().GetContentIndex() != *oStartContentIdx;
1062                     break;
1063                 default:
1064                     bDeleteMark = true;
1065                     break;
1066                 }
1067             }
1068             return bDeleteMark;
1069         }
1070         return false;
1071     }
1072 
isBookmarkDeleted(SwPaM const & rPaM,bool const isReplace) const1073     bool MarkManager::isBookmarkDeleted(SwPaM const& rPaM, bool const isReplace) const
1074     {
1075         SwPosition const& rStart(*rPaM.Start());
1076         SwPosition const& rEnd(*rPaM.End());
1077         for (auto ppMark = m_vBookmarks.begin();
1078             ppMark != m_vBookmarks.end();
1079             ++ppMark)
1080         {
1081             bool bIsPosInRange(false);
1082             bool bIsOtherPosInRange(false);
1083             bool const bDeleteMark = isDeleteMark(*ppMark, isReplace,
1084                 rStart.GetNode(), rEnd.GetNode(), rStart.GetContentIndex(), rEnd.GetContentIndex(),
1085                 bIsPosInRange, bIsOtherPosInRange);
1086             if (bDeleteMark
1087                 && IDocumentMarkAccess::GetType(**ppMark) == MarkType::BOOKMARK)
1088             {
1089                 return true;
1090             }
1091         }
1092         return false;
1093     }
1094 
deleteMarks(const SwNode & rStt,const SwNode & rEnd,std::vector<SaveBookmark> * pSaveBkmk,std::optional<sal_Int32> oStartContentIdx,std::optional<sal_Int32> oEndContentIdx,bool const isReplace)1095     void MarkManager::deleteMarks(
1096             const SwNode& rStt,
1097             const SwNode& rEnd,
1098             std::vector<SaveBookmark>* pSaveBkmk,
1099             std::optional<sal_Int32> oStartContentIdx,
1100             std::optional<sal_Int32> oEndContentIdx,
1101             bool const isReplace)
1102     {
1103         std::vector<const_iterator_t> vMarksToDelete;
1104         bool bIsSortingNeeded = false;
1105 
1106         // boolean indicating, if at least one mark has been moved while collecting marks for deletion
1107         bool bMarksMoved = false;
1108         // have marks in the range been skipped instead of deleted
1109         bool bMarksSkipDeletion = false;
1110 
1111         // copy all bookmarks in the move area to a vector storing all position data as offset
1112         // reassignment is performed after the move
1113         for (auto ppMark = m_vAllMarks.begin();
1114             ppMark != m_vAllMarks.end();
1115             ++ppMark)
1116         {
1117             ::sw::mark::MarkBase *const pMark = *ppMark;
1118             bool bIsPosInRange(false);
1119             bool bIsOtherPosInRange(false);
1120             bool const bDeleteMark = isDeleteMark(pMark, isReplace, rStt, rEnd,
1121                 oStartContentIdx, oEndContentIdx, bIsPosInRange, bIsOtherPosInRange);
1122 
1123             if ( bIsPosInRange
1124                  && ( bIsOtherPosInRange
1125                       || !pMark->IsExpanded() ) )
1126             {
1127                 if ( bDeleteMark )
1128                 {
1129                     if ( pSaveBkmk )
1130                     {
1131                         pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, oStartContentIdx ) );
1132                     }
1133                     vMarksToDelete.emplace_back(ppMark);
1134                 }
1135                 else
1136                 {
1137                     bMarksSkipDeletion = true;
1138                 }
1139             }
1140             else if ( bIsPosInRange != bIsOtherPosInRange )
1141             {
1142                 // the bookmark is partially in the range
1143                 // move position of that is in the range out of it
1144 
1145                 std::optional< SwPosition > oNewPos;
1146                 if ( oEndContentIdx )
1147                 {
1148                     oNewPos.emplace( *rEnd.GetContentNode(), *oEndContentIdx );
1149                 }
1150                 else
1151                 {
1152                     lcl_FindExpelPosition( oNewPos, rStt, rEnd, bIsPosInRange ? pMark->GetOtherMarkPos() : pMark->GetMarkPos() );
1153                 }
1154 
1155                 bool bMoveMark = true;
1156                 {
1157                     switch ( IDocumentMarkAccess::GetType( *pMark ) )
1158                     {
1159                     case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
1160                     case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
1161                         // no move of cross-reference bookmarks, if move occurs inside a certain node
1162                         bMoveMark = pMark->GetMarkPos().GetNode() != oNewPos->GetNode();
1163                         break;
1164                     case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
1165                         // no move of annotation marks, if method is called to collect deleted marks
1166                         bMoveMark = pSaveBkmk == nullptr;
1167                         break;
1168                     default:
1169                         bMoveMark = true;
1170                         break;
1171                     }
1172                 }
1173                 if ( bMoveMark )
1174                 {
1175                     if ( bIsPosInRange )
1176                         pMark->SetMarkPos(*oNewPos);
1177                     else
1178                         pMark->SetOtherMarkPos(*oNewPos);
1179                     bMarksMoved = true;
1180 
1181                     // illegal selection? collapse the mark and restore sorting later
1182                     bIsSortingNeeded |= lcl_FixCorrectedMark( bIsPosInRange, bIsOtherPosInRange, pMark );
1183                 }
1184             }
1185         }
1186 
1187         {
1188             // fdo#61016 delay the deletion of the fieldmark characters
1189             // to prevent that from deleting the marks on that position
1190             // which would invalidate the iterators in vMarksToDelete
1191             std::vector< std::unique_ptr<ILazyDeleter> > vDelay;
1192             vDelay.reserve(vMarksToDelete.size());
1193 
1194             // If needed, sort mark containers containing subsets of the marks
1195             // in order to assure sorting.  The sorting is critical for the
1196             // deletion of a mark as it is searched in these container for
1197             // deletion.
1198             if ( !vMarksToDelete.empty() && bMarksMoved )
1199             {
1200                 sortSubsetMarks();
1201             }
1202             // we just remembered the iterators to delete, so we do not need to search
1203             // for the shared_ptr<> (the entry in m_vAllMarks) again
1204             // reverse iteration, since erasing an entry invalidates iterators
1205             // behind it (the iterators in vMarksToDelete are sorted)
1206             for ( std::vector< const_iterator_t >::reverse_iterator pppMark = vMarksToDelete.rbegin();
1207                   pppMark != vMarksToDelete.rend();
1208                   ++pppMark )
1209             {
1210                 vDelay.push_back(deleteMark(*pppMark, pSaveBkmk != nullptr));
1211             }
1212         } // scope to kill vDelay
1213 
1214         // also need to sort if both marks were moved and not-deleted because
1215         // the not-deleted marks could be in wrong order vs. the moved ones
1216         if (bIsSortingNeeded || (bMarksMoved && bMarksSkipDeletion))
1217         {
1218             sortMarks();
1219         }
1220 
1221         SAL_INFO("sw.core", "deleteMarks");
1222         lcl_DebugMarks(m_vAllMarks);
1223     }
1224 
1225     namespace {
1226 
1227     struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
1228     {
1229         std::unique_ptr<Fieldmark> m_pFieldmark;
1230         SwDoc& m_rDoc;
1231         bool const m_isMoveNodes;
LazyFieldmarkDeletersw::mark::__anon3b4322910411::LazyFieldmarkDeleter1232         LazyFieldmarkDeleter(Fieldmark *const pMark, SwDoc& rDoc, bool const isMoveNodes)
1233             : m_pFieldmark(pMark), m_rDoc(rDoc), m_isMoveNodes(isMoveNodes)
1234         {
1235             assert(m_pFieldmark);
1236         }
~LazyFieldmarkDeletersw::mark::__anon3b4322910411::LazyFieldmarkDeleter1237         virtual ~LazyFieldmarkDeleter() override
1238         {
1239             // note: because of the call chain from SwUndoDelete, the field
1240             // command *cannot* be deleted here as it would create a separate
1241             // SwUndoDelete that's interleaved with the SwHistory of the outer
1242             // one - only delete the CH_TXT_ATR_FIELD*!
1243             if (!m_isMoveNodes)
1244             {
1245                 m_pFieldmark->ReleaseDoc(m_rDoc);
1246             }
1247         }
1248     };
1249 
1250     // Call DeregisterFromDoc() lazily, because it can call selection change listeners, which
1251     // may mutate the marks container
1252     struct LazyDdeBookmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
1253     {
1254         std::unique_ptr<DdeBookmark> m_pDdeBookmark;
1255         SwDoc& m_rDoc;
LazyDdeBookmarkDeletersw::mark::__anon3b4322910411::LazyDdeBookmarkDeleter1256         LazyDdeBookmarkDeleter(DdeBookmark *const pDdeBookmark, SwDoc& rDoc)
1257             : m_pDdeBookmark(pDdeBookmark), m_rDoc(rDoc)
1258         {
1259             assert(pDdeBookmark);
1260         }
~LazyDdeBookmarkDeletersw::mark::__anon3b4322910411::LazyDdeBookmarkDeleter1261         virtual ~LazyDdeBookmarkDeleter() override
1262         {
1263             m_pDdeBookmark->DeregisterFromDoc(m_rDoc);
1264         }
1265     };
1266 
1267     }
1268 
1269     std::unique_ptr<IDocumentMarkAccess::ILazyDeleter>
deleteMark(const const_iterator_t & ppMark,bool const isMoveNodes)1270         MarkManager::deleteMark(const const_iterator_t& ppMark, bool const isMoveNodes)
1271     {
1272         std::unique_ptr<ILazyDeleter> ret;
1273         if (ppMark.get() == m_vAllMarks.end())
1274             return ret;
1275         IMark* pMark = *ppMark;
1276 
1277         switch(IDocumentMarkAccess::GetType(*pMark))
1278         {
1279             case IDocumentMarkAccess::MarkType::BOOKMARK:
1280                 {
1281                     auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
1282                     if ( ppBookmark != m_vBookmarks.end() )
1283                     {
1284                         Bookmark* pBookmark = dynamic_cast<Bookmark*>(*ppBookmark);
1285 
1286                         if(pBookmark)
1287                             pBookmark->sendLOKDeleteCallback();
1288 
1289                         m_vBookmarks.erase(ppBookmark);
1290                     }
1291                     else
1292                     {
1293                         assert(false &&
1294                             "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
1295                     }
1296                 }
1297                 break;
1298             case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
1299             case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
1300                 {
1301                     auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
1302                     if ( ppBookmark != m_vBookmarks.end() )
1303                     {
1304                         m_vBookmarks.erase(ppBookmark);
1305                     }
1306                     else
1307                     {
1308                         assert(false &&
1309                             "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
1310                     }
1311                 }
1312                 break;
1313 
1314             case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
1315             case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
1316             case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
1317             case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
1318                 {
1319                     auto const ppFieldmark = lcl_FindMark(m_vFieldmarks, *ppMark.get());
1320                     if ( ppFieldmark != m_vFieldmarks.end() )
1321                     {
1322                         if(m_pLastActiveFieldmark == *ppFieldmark)
1323                             ClearFieldActivation();
1324 
1325                         m_vFieldmarks.erase(ppFieldmark);
1326                         ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_rDoc, isMoveNodes));
1327                     }
1328                     else
1329                     {
1330                         assert(false &&
1331                             "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container.");
1332                     }
1333                 }
1334                 break;
1335 
1336             case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
1337                 {
1338                     auto const ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, *ppMark.get());
1339                     assert(ppAnnotationMark != m_vAnnotationMarks.end() &&
1340                         "<MarkManager::deleteMark(..)> - Annotation Mark not found in Annotation Mark container.");
1341                     m_vAnnotationMarks.erase(ppAnnotationMark);
1342                 }
1343                 break;
1344 
1345             case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
1346             case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
1347             case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
1348                 // no special marks container
1349                 break;
1350         }
1351         //Effective STL Item 27, get a non-const iterator aI at the same
1352         //position as const iterator ppMark was
1353         auto aI = m_vAllMarks.begin();
1354         std::advance(aI, std::distance<container_t::const_iterator>(aI, ppMark.get()));
1355         DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(pMark);
1356         if (pDdeBookmark)
1357         {
1358             ret.reset(new LazyDdeBookmarkDeleter(pDdeBookmark, m_rDoc));
1359         }
1360 
1361         m_vAllMarks.erase(aI);
1362         // If we don't have a lazy deleter
1363         if (!ret)
1364             // delete after we remove from the list, because the destructor can
1365             // recursively call into this method.
1366             delete pMark;
1367         return ret;
1368     }
1369 
deleteMark(const IMark * const pMark)1370     void MarkManager::deleteMark(const IMark* const pMark)
1371     {
1372         assert(&pMark->GetMarkPos().GetDoc() == &m_rDoc &&
1373             "<MarkManager::deleteMark(..)>"
1374             " - Mark is not in my doc.");
1375         // finds the last Mark that is starting before pMark
1376         // (pMarkLow < pMark)
1377         auto [it, endIt] = equal_range(
1378                 m_vAllMarks.begin(),
1379                 m_vAllMarks.end(),
1380                 pMark->GetMarkStart(),
1381                 CompareIMarkStartsBefore());
1382         for ( ; it != endIt; ++it)
1383             if (*it == pMark)
1384             {
1385                 deleteMark(iterator(it), false);
1386                 break;
1387             }
1388     }
1389 
clearAllMarks()1390     void MarkManager::clearAllMarks()
1391     {
1392         ClearFieldActivation();
1393         m_vFieldmarks.clear();
1394         m_vBookmarks.clear();
1395         m_vAnnotationMarks.clear();
1396         for (const auto & p : m_vAllMarks)
1397             delete p;
1398         m_vAllMarks.clear();
1399     }
1400 
findMark(const OUString & rName) const1401     IDocumentMarkAccess::const_iterator_t MarkManager::findMark(const OUString& rName) const
1402     {
1403         auto const ret = lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end());
1404         return IDocumentMarkAccess::iterator(ret);
1405     }
1406 
findBookmark(const OUString & rName) const1407     IDocumentMarkAccess::const_iterator_t MarkManager::findBookmark(const OUString& rName) const
1408     {
1409         auto const ret = lcl_FindMarkByName(rName, m_vBookmarks.begin(), m_vBookmarks.end());
1410         return IDocumentMarkAccess::iterator(ret);
1411     }
1412 
1413     // find the first Mark that does not start before
findFirstMarkNotStartsBefore(const SwPosition & rPos) const1414     IDocumentMarkAccess::const_iterator_t MarkManager::findFirstMarkNotStartsBefore(const SwPosition& rPos) const
1415     {
1416         return std::lower_bound(
1417                 m_vAllMarks.begin(),
1418                 m_vAllMarks.end(),
1419                 rPos,
1420                 CompareIMarkStartsBefore());
1421     }
1422 
getAllMarksBegin() const1423     IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksBegin() const
1424         { return m_vAllMarks.begin(); }
1425 
getAllMarksEnd() const1426     IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksEnd() const
1427         { return m_vAllMarks.end(); }
1428 
getAllMarksCount() const1429     sal_Int32 MarkManager::getAllMarksCount() const
1430         { return m_vAllMarks.size(); }
1431 
getBookmarksBegin() const1432     IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksBegin() const
1433         { return m_vBookmarks.begin(); }
1434 
getBookmarksEnd() const1435     IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksEnd() const
1436         { return m_vBookmarks.end(); }
1437 
getBookmarksCount() const1438     sal_Int32 MarkManager::getBookmarksCount() const
1439         { return m_vBookmarks.size(); }
1440 
getFieldmarksBegin() const1441     IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksBegin() const
1442         { return m_vFieldmarks.begin(); }
1443 
getFieldmarksEnd() const1444     IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksEnd() const
1445         { return m_vFieldmarks.end(); }
1446 
getFieldmarksCount() const1447     sal_Int32 MarkManager::getFieldmarksCount() const { return m_vFieldmarks.size(); }
1448 
1449 
1450     // finds the first that is starting after
findFirstBookmarkStartsAfter(const SwPosition & rPos) const1451     IDocumentMarkAccess::const_iterator_t MarkManager::findFirstBookmarkStartsAfter(const SwPosition& rPos) const
1452     {
1453         return std::upper_bound(
1454             m_vBookmarks.begin(),
1455             m_vBookmarks.end(),
1456             rPos,
1457             CompareIMarkStartsAfter());
1458     }
1459 
getFieldmarkAt(const SwPosition & rPos) const1460     IFieldmark* MarkManager::getFieldmarkAt(const SwPosition& rPos) const
1461     {
1462         auto const pFieldmark = find_if(
1463             m_vFieldmarks.begin(),
1464             m_vFieldmarks.end(),
1465             [&rPos] (::sw::mark::MarkBase const*const pMark) {
1466                     return pMark->GetMarkStart() == rPos
1467                             // end position includes the CH_TXT_ATR_FIELDEND
1468                         || (pMark->GetMarkEnd().GetContentIndex() == rPos.GetContentIndex() + 1
1469                             && pMark->GetMarkEnd().GetNode() == rPos.GetNode());
1470                 } );
1471         return (pFieldmark == m_vFieldmarks.end())
1472             ? nullptr
1473             : dynamic_cast<IFieldmark*>(*pFieldmark);
1474     }
1475 
getInnerFieldmarkFor(const SwPosition & rPos) const1476     IFieldmark* MarkManager::getInnerFieldmarkFor(const SwPosition& rPos) const
1477     {
1478         auto itFieldmark = find_if(
1479             m_vFieldmarks.begin(),
1480             m_vFieldmarks.end(),
1481             [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
1482         if (itFieldmark == m_vFieldmarks.end())
1483             return nullptr;
1484         auto pFieldmark(*itFieldmark);
1485 
1486         // See if any fieldmarks after the first hit are closer to rPos.
1487         ++itFieldmark;
1488         for ( ; itFieldmark != m_vFieldmarks.end()
1489                 && (**itFieldmark).GetMarkStart() <= rPos; ++itFieldmark)
1490         {   // find the innermost fieldmark
1491             if (rPos < (**itFieldmark).GetMarkEnd()
1492                 && (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart()
1493                     || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd()))
1494             {
1495                 pFieldmark = *itFieldmark;
1496             }
1497         }
1498         return dynamic_cast<IFieldmark*>(pFieldmark);
1499     }
1500 
getOneInnermostBookmarkFor(const SwPosition & rPos) const1501     IMark* MarkManager::getOneInnermostBookmarkFor(const SwPosition& rPos) const
1502     {
1503         auto it = std::find_if(m_vBookmarks.begin(), m_vBookmarks.end(),
1504                                [&rPos](const sw::mark::MarkBase* pMark)
1505                                { return pMark->IsCoveringPosition(rPos); });
1506         if (it == m_vBookmarks.end())
1507         {
1508             return nullptr;
1509         }
1510         sw::mark::IMark* pBookmark = *it;
1511 
1512         // See if any bookmarks after the first hit are closer to rPos.
1513         ++it;
1514 
1515         for (; it != m_vBookmarks.end() && (*it)->GetMarkStart() <= rPos; ++it)
1516         {
1517             // Find the innermost bookmark.
1518             if (rPos < (*it)->GetMarkEnd()
1519                 && (pBookmark->GetMarkStart() < (*it)->GetMarkStart()
1520                     || (*it)->GetMarkEnd() < pBookmark->GetMarkEnd()))
1521             {
1522                 pBookmark = *it;
1523             }
1524         }
1525         return pBookmark;
1526     }
1527 
deleteFieldmarkAt(const SwPosition & rPos)1528     void MarkManager::deleteFieldmarkAt(const SwPosition& rPos)
1529     {
1530         auto const pFieldmark = dynamic_cast<Fieldmark*>(getFieldmarkAt(rPos));
1531         assert(pFieldmark); // currently all callers require it to be there
1532 
1533         deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark), false);
1534     }
1535 
changeFormFieldmarkType(::sw::mark::IFieldmark * pFieldmark,const OUString & rNewType)1536     ::sw::mark::IFieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType)
1537     {
1538         bool bActualChange = false;
1539         if(rNewType == ODF_FORMDROPDOWN)
1540         {
1541             if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark))
1542                 bActualChange = true;
1543             if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
1544                 return nullptr;
1545         }
1546         else if(rNewType == ODF_FORMCHECKBOX)
1547         {
1548             if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark))
1549                 bActualChange = true;
1550             if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
1551                 return nullptr;
1552         }
1553         else if(rNewType == ODF_FORMDATE)
1554         {
1555             if (!dynamic_cast<::sw::mark::DateFieldmark*>(pFieldmark))
1556                 bActualChange = true;
1557             if (!dynamic_cast<::sw::mark::TextFieldmark*>(pFieldmark)) // only allowed converting between date field <-> text field
1558                 return nullptr;
1559         }
1560 
1561         if (!bActualChange)
1562             return nullptr;
1563 
1564         // Store attributes needed to create the new fieldmark
1565         OUString sName = pFieldmark->GetName();
1566         SwPaM const aPaM(pFieldmark->GetMarkStart());
1567 
1568         // Remove the old fieldmark and create a new one with the new type
1569         if (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX)
1570         {
1571             SwPosition aNewPos (*aPaM.GetPoint());
1572             deleteFieldmarkAt(aNewPos);
1573             return makeNoTextFieldBookmark(aPaM, sName, rNewType);
1574         }
1575         else if(rNewType == ODF_FORMDATE)
1576         {
1577             SwPosition aPos (*aPaM.GetPoint());
1578             SwPaM aNewPaM(pFieldmark->GetMarkStart(), pFieldmark->GetMarkEnd());
1579             deleteFieldmarkAt(aPos);
1580             // HACK: hard-code the separator position here at the start because
1581             // writerfilter put it in the wrong place (at the end) on attach()
1582             SwPosition const sepPos(*aNewPaM.Start());
1583             return makeFieldBookmark(aNewPaM, sName, rNewType, &sepPos);
1584         }
1585         return nullptr;
1586     }
1587 
NotifyCursorUpdate(const SwCursorShell & rCursorShell)1588     void MarkManager::NotifyCursorUpdate(const SwCursorShell& rCursorShell)
1589     {
1590         SwView* pSwView = dynamic_cast<SwView *>(rCursorShell.GetSfxViewShell());
1591         if(!pSwView)
1592             return;
1593 
1594         SwEditWin& rEditWin = pSwView->GetEditWin();
1595         SwPosition aPos(*rCursorShell.GetCursor()->GetPoint());
1596         IFieldmark* pFieldBM = getInnerFieldmarkFor(aPos);
1597         FieldmarkWithDropDownButton* pNewActiveFieldmark = nullptr;
1598         if ((!pFieldBM || (pFieldBM->GetFieldname() != ODF_FORMDROPDOWN && pFieldBM->GetFieldname() != ODF_FORMDATE))
1599             && aPos.GetContentIndex() > 0 )
1600         {
1601             aPos.AdjustContent(-1);
1602             pFieldBM = getInnerFieldmarkFor(aPos);
1603         }
1604 
1605         if ( pFieldBM && (pFieldBM->GetFieldname() == ODF_FORMDROPDOWN ||
1606                           pFieldBM->GetFieldname() == ODF_FORMDATE))
1607         {
1608             if (m_pLastActiveFieldmark != pFieldBM)
1609             {
1610                 FieldmarkWithDropDownButton& rFormField = dynamic_cast<FieldmarkWithDropDownButton&>(*pFieldBM);
1611                 pNewActiveFieldmark = &rFormField;
1612             }
1613             else
1614             {
1615                 pNewActiveFieldmark = m_pLastActiveFieldmark;
1616             }
1617         }
1618 
1619         if(pNewActiveFieldmark != m_pLastActiveFieldmark)
1620         {
1621             ClearFieldActivation();
1622             m_pLastActiveFieldmark = pNewActiveFieldmark;
1623             if(pNewActiveFieldmark)
1624                 pNewActiveFieldmark->ShowButton(&rEditWin);
1625         }
1626 
1627         LOKUpdateActiveField(pSwView);
1628     }
1629 
ClearFieldActivation()1630     void MarkManager::ClearFieldActivation()
1631     {
1632         if(m_pLastActiveFieldmark)
1633             m_pLastActiveFieldmark->RemoveButton();
1634 
1635         m_pLastActiveFieldmark = nullptr;
1636     }
1637 
LOKUpdateActiveField(const SfxViewShell * pViewShell)1638     void MarkManager::LOKUpdateActiveField(const SfxViewShell* pViewShell)
1639     {
1640         if (!comphelper::LibreOfficeKit::isActive())
1641             return;
1642 
1643         if (m_pLastActiveFieldmark)
1644         {
1645             if (auto pDrowDown = m_pLastActiveFieldmark->GetFieldname() == ODF_FORMDROPDOWN ?
1646                                 dynamic_cast<::sw::mark::DropDownFieldmark*>(m_pLastActiveFieldmark) :
1647                                 nullptr)
1648             {
1649                 pDrowDown->SendLOKShowMessage(pViewShell);
1650             }
1651         }
1652         else
1653         {
1654             // Check whether we have any drop down fieldmark at all.
1655             bool bDropDownFieldExist = false;
1656             for (auto aIter = m_vFieldmarks.begin(); aIter != m_vFieldmarks.end(); ++aIter)
1657             {
1658                 IFieldmark *pMark = dynamic_cast<IFieldmark*>(*aIter);
1659                 if (pMark && pMark->GetFieldname() == ODF_FORMDROPDOWN)
1660                 {
1661                     bDropDownFieldExist = true;
1662                     break;
1663                 }
1664             }
1665 
1666             if (bDropDownFieldExist)
1667                 ::sw::mark::DropDownFieldmark::SendLOKHideMessage(pViewShell);
1668         }
1669     }
1670 
getDropDownFor(const SwPosition & rPos) const1671     IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const
1672     {
1673         IFieldmark *pMark = getFieldmarkAt(rPos);
1674         if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN)
1675             return nullptr;
1676         return pMark;
1677     }
1678 
getNoTextFieldmarksIn(const SwPaM & rPaM) const1679     std::vector<IFieldmark*> MarkManager::getNoTextFieldmarksIn(const SwPaM &rPaM) const
1680     {
1681         std::vector<IFieldmark*> aRet;
1682 
1683         for (auto aI = m_vFieldmarks.begin(),
1684             aEnd = m_vFieldmarks.end(); aI != aEnd; ++aI)
1685         {
1686             ::sw::mark::IMark* pI = *aI;
1687             const SwPosition &rStart = pI->GetMarkPos();
1688             if (!rPaM.ContainsPosition(rStart))
1689                 continue;
1690 
1691             IFieldmark *pMark = dynamic_cast<IFieldmark*>(pI);
1692             if (!pMark || (pMark->GetFieldname() != ODF_FORMDROPDOWN
1693                             && pMark->GetFieldname() != ODF_FORMCHECKBOX))
1694             {
1695                 continue;
1696             }
1697 
1698             aRet.push_back(pMark);
1699         }
1700 
1701         return aRet;
1702     }
1703 
getFieldmarkAfter(const SwPosition & rPos,bool bLoop) const1704     IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos, bool bLoop) const
1705         { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos, bLoop)); }
1706 
getFieldmarkBefore(const SwPosition & rPos,bool bLoop) const1707     IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const
1708         { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos, bLoop)); }
1709 
getAnnotationMarksBegin() const1710     IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksBegin() const
1711     {
1712         return m_vAnnotationMarks.begin();
1713     }
1714 
getAnnotationMarksEnd() const1715     IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksEnd() const
1716     {
1717         return m_vAnnotationMarks.end();
1718     }
1719 
getAnnotationMarksCount() const1720     sal_Int32 MarkManager::getAnnotationMarksCount() const
1721     {
1722         return m_vAnnotationMarks.size();
1723     }
1724 
findAnnotationMark(const OUString & rName) const1725     IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationMark( const OUString& rName ) const
1726     {
1727         auto const ret = lcl_FindMarkByName( rName, m_vAnnotationMarks.begin(), m_vAnnotationMarks.end() );
1728         return IDocumentMarkAccess::iterator(ret);
1729     }
1730 
getAnnotationMarkFor(const SwPosition & rPos) const1731     IMark* MarkManager::getAnnotationMarkFor(const SwPosition& rPos) const
1732     {
1733         auto const pAnnotationMark = find_if(
1734             m_vAnnotationMarks.begin(),
1735             m_vAnnotationMarks.end(),
1736             [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
1737         if (pAnnotationMark == m_vAnnotationMarks.end())
1738             return nullptr;
1739         return *pAnnotationMark;
1740     }
1741 
1742     // finds the first that is starting after
findFirstAnnotationStartsAfter(const SwPosition & rPos) const1743     IDocumentMarkAccess::const_iterator_t MarkManager::findFirstAnnotationStartsAfter(const SwPosition& rPos) const
1744     {
1745         return std::upper_bound(
1746             m_vAnnotationMarks.begin(),
1747             m_vAnnotationMarks.end(),
1748             rPos,
1749             CompareIMarkStartsAfter());
1750     }
1751 
1752     // create helper bookmark for annotations on tracked deletions
makeAnnotationBookmark(const SwPaM & rPaM,const OUString & rName,const IDocumentMarkAccess::MarkType eType,sw::mark::InsertMode const eMode,SwPosition const * const pSepPos)1753     ::sw::mark::IMark* MarkManager::makeAnnotationBookmark(const SwPaM& rPaM,
1754         const OUString& rName,
1755         const IDocumentMarkAccess::MarkType eType,
1756         sw::mark::InsertMode const eMode,
1757         SwPosition const*const pSepPos)
1758     {
1759         OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
1760         return makeMark( rPaM, sAnnotationBookmarkName, eType, eMode, pSepPos);
1761     }
1762 
1763     // find helper bookmark of annotations on tracked deletions
findAnnotationBookmark(const OUString & rName) const1764     IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationBookmark(const OUString& rName) const
1765     {
1766         OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
1767         return findBookmark(sAnnotationBookmarkName);
1768     }
1769 
1770     // restore text ranges of annotations on tracked deletions
1771     // based on the helper bookmarks (which can survive I/O and hiding redlines)
restoreAnnotationMarks(bool bDelete)1772     void MarkManager::restoreAnnotationMarks(bool bDelete)
1773     {
1774         for (auto iter = getBookmarksBegin();
1775               iter != getBookmarksEnd(); )
1776         {
1777             const OUString & rBookmarkName = (**iter).GetName();
1778             sal_Int32 nPos;
1779             if ( rBookmarkName.startsWith("__Annotation__") &&
1780                   (nPos = rBookmarkName.indexOf(S_ANNOTATION_BOOKMARK)) > -1 )
1781             {
1782                 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
1783                 IDocumentMarkAccess::const_iterator_t pMark = findAnnotationMark(rBookmarkName.copy(0, nPos));
1784                 if ( pMark != getAnnotationMarksEnd() )
1785                 {
1786                     const SwPaM aPam((**iter).GetMarkStart(), (**pMark).GetMarkEnd());
1787                     repositionMark(*pMark, aPam);
1788                 }
1789                 if (bDelete)
1790                 {
1791                     deleteMark(&**iter);
1792                     // this invalidates iter, have to start over...
1793                     iter = getBookmarksBegin();
1794                 }
1795                 else
1796                     ++iter;
1797             }
1798             else
1799                 ++iter;
1800         }
1801     }
1802 
getUniqueMarkName(const OUString & rName) const1803     OUString MarkManager::getUniqueMarkName(const OUString& rName) const
1804     {
1805         OSL_ENSURE(rName.getLength(),
1806             "<MarkManager::getUniqueMarkName(..)> - a name should be proposed");
1807         if( m_rDoc.IsInMailMerge())
1808         {
1809             OUString newName = rName + "MailMergeMark"
1810                     + DateTimeToOUString( DateTime( DateTime::SYSTEM ) )
1811                     + OUString::number( m_vAllMarks.size() + 1 );
1812             return newName;
1813         }
1814 
1815         if (lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
1816         {
1817             return rName;
1818         }
1819         OUString sTmp;
1820 
1821         // try the name "<rName>XXX" (where XXX is a number starting from 1) unless there is
1822         // an unused name. Due to performance-reasons (especially in mailmerge-scenarios) there
1823         // is a map m_aMarkBasenameMapUniqueOffset which holds the next possible offset (XXX) for
1824         // rName (so there is no need to test for nCnt-values smaller than the offset).
1825         sal_Int32 nCnt = 1;
1826         MarkBasenameMapUniqueOffset_t::const_iterator aIter = m_aMarkBasenameMapUniqueOffset.find(rName);
1827         if(aIter != m_aMarkBasenameMapUniqueOffset.end()) nCnt = aIter->second;
1828         OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rName);
1829         while(nCnt < SAL_MAX_INT32)
1830         {
1831             sTmp = aPrefix + OUString::number(nCnt);
1832             nCnt++;
1833             if (lcl_FindMarkByName(sTmp, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
1834             {
1835                 break;
1836             }
1837         }
1838         m_aMarkBasenameMapUniqueOffset[rName] = nCnt;
1839 
1840         return sTmp;
1841     }
1842 
assureSortedMarkContainers() const1843     void MarkManager::assureSortedMarkContainers() const
1844     {
1845         const_cast< MarkManager* >(this)->sortMarks();
1846     }
1847 
sortSubsetMarks()1848     void MarkManager::sortSubsetMarks()
1849     {
1850         stable_sort(m_vBookmarks.begin(), m_vBookmarks.end(), &lcl_MarkOrderingByStart);
1851         sort(m_vFieldmarks.begin(), m_vFieldmarks.end(), &lcl_MarkOrderingByStart);
1852         sort(m_vAnnotationMarks.begin(), m_vAnnotationMarks.end(), &lcl_MarkOrderingByStart);
1853     }
1854 
sortMarks()1855     void MarkManager::sortMarks()
1856     {
1857         sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart);
1858         sortSubsetMarks();
1859     }
1860 
dumpAsXml(xmlTextWriterPtr pWriter) const1861 void MarkManager::dumpAsXml(xmlTextWriterPtr pWriter) const
1862 {
1863     struct
1864     {
1865         const char* pName;
1866         const container_t* pContainer;
1867     } aContainers[] =
1868     {
1869         // UNO marks are only part of all marks.
1870         {"allmarks", &m_vAllMarks},
1871         {"bookmarks", &m_vBookmarks},
1872         {"fieldmarks", &m_vFieldmarks},
1873         {"annotationmarks", &m_vAnnotationMarks}
1874     };
1875 
1876     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager"));
1877     for (const auto & rContainer : aContainers)
1878     {
1879         if (!rContainer.pContainer->empty())
1880         {
1881             (void)xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName));
1882             for (auto it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it)
1883                 (*it)->dumpAsXml(pWriter);
1884             (void)xmlTextWriterEndElement(pWriter);
1885         }
1886     }
1887     (void)xmlTextWriterEndElement(pWriter);
1888 }
1889 
1890 } // namespace ::sw::mark
1891 
1892 namespace
1893 {
lcl_Greater(const SwPosition & rPos,const SwNode & rNdIdx,std::optional<sal_Int32> oContentIdx)1894     bool lcl_Greater( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
1895     {
1896         return rPos.GetNode() > rNdIdx ||
1897                 ( oContentIdx && rPos.GetNode() == rNdIdx && rPos.GetContentIndex() > *oContentIdx );
1898     }
1899 }
1900 
1901 // IDocumentMarkAccess for SwDoc
getIDocumentMarkAccess()1902 IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess()
1903     { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
1904 
getIDocumentMarkAccess() const1905 const IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess() const
1906     { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
1907 
SaveBookmark(const IMark & rBkmk,const SwNode & rMvPos,std::optional<sal_Int32> oContentIdx)1908 SaveBookmark::SaveBookmark(
1909     const IMark& rBkmk,
1910     const SwNode& rMvPos,
1911     std::optional<sal_Int32> oContentIdx)
1912     : m_aName(rBkmk.GetName())
1913     , m_bHidden(false)
1914     , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk))
1915 {
1916     const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk);
1917     if(pBookmark)
1918     {
1919         m_aShortName = pBookmark->GetShortName();
1920         m_aCode = pBookmark->GetKeyCode();
1921         m_bHidden = pBookmark->IsHidden();
1922         m_aHideCondition = pBookmark->GetHideCondition();
1923 
1924         ::sfx2::Metadatable const*const pMetadatable(
1925                 dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
1926         if (pMetadatable)
1927         {
1928             m_pMetadataUndo = pMetadatable->CreateUndo();
1929         }
1930     }
1931     m_nNode1 = rBkmk.GetMarkPos().GetNodeIndex();
1932     m_nContent1 = rBkmk.GetMarkPos().GetContentIndex();
1933 
1934     m_nNode1 -= rMvPos.GetIndex();
1935     if(oContentIdx && !m_nNode1)
1936         m_nContent1 -= *oContentIdx;
1937 
1938     if(rBkmk.IsExpanded())
1939     {
1940         m_nNode2 = rBkmk.GetOtherMarkPos().GetNodeIndex();
1941         m_nContent2 = rBkmk.GetOtherMarkPos().GetContentIndex();
1942 
1943         m_nNode2 -= rMvPos.GetIndex();
1944         if(oContentIdx && !m_nNode2)
1945             m_nContent2 -= *oContentIdx;
1946     }
1947     else
1948     {
1949         m_nNode2 = NODE_OFFSET_MAX;
1950         m_nContent2 = -1;
1951     }
1952 }
1953 
SetInDoc(SwDoc * pDoc,const SwNode & rNewPos,std::optional<sal_Int32> oContentIdx)1954 void SaveBookmark::SetInDoc(
1955     SwDoc* pDoc,
1956     const SwNode& rNewPos,
1957     std::optional<sal_Int32> oContentIdx)
1958 {
1959     SwPaM aPam(rNewPos);
1960     if(oContentIdx)
1961     {
1962         if (aPam.GetPoint()->GetNode().IsContentNode())
1963             aPam.GetPoint()->SetContent( *oContentIdx );
1964         else
1965             SAL_WARN("sw", "trying to sent content index, but point node is not a content node");
1966     }
1967 
1968     if(NODE_OFFSET_MAX != m_nNode2)
1969     {
1970         aPam.SetMark();
1971 
1972         aPam.GetMark()->Adjust(m_nNode2);
1973         if (aPam.GetMark()->GetNode().IsContentNode())
1974         {
1975             if(oContentIdx && !m_nNode2)
1976                 aPam.GetMark()->SetContent(*oContentIdx + m_nContent2);
1977             else
1978                 aPam.GetMark()->SetContent(m_nContent2);
1979         }
1980         else
1981             SAL_WARN("sw", "trying to sent content index, but mark node is not a content node");
1982     }
1983 
1984     aPam.GetPoint()->Adjust(m_nNode1);
1985 
1986     if (aPam.GetPoint()->GetNode().IsContentNode())
1987     {
1988         if(oContentIdx && !m_nNode1)
1989             aPam.GetPoint()->SetContent(*oContentIdx + m_nContent1);
1990         else
1991             aPam.GetPoint()->SetContent(m_nContent1);
1992     }
1993 
1994     if(aPam.HasMark()
1995         && !CheckNodesRange(aPam.GetPoint()->GetNode(), aPam.GetMark()->GetNode(), true))
1996         return;
1997 
1998     ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>(
1999         pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName,
2000             m_eOrigBkmType, sw::mark::InsertMode::CopyText));
2001     if(!pBookmark)
2002         return;
2003 
2004     pBookmark->SetKeyCode(m_aCode);
2005     pBookmark->SetShortName(m_aShortName);
2006     pBookmark->Hide(m_bHidden);
2007     pBookmark->SetHideCondition(m_aHideCondition);
2008 
2009     if (m_pMetadataUndo)
2010     {
2011         ::sfx2::Metadatable * const pMeta(
2012             dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
2013         assert(pMeta && "metadata undo, but not metadatable?");
2014         if (pMeta)
2015         {
2016             pMeta->RestoreMetadata(m_pMetadataUndo);
2017         }
2018     }
2019 }
2020 
2021 // DelBookmarks
2022 
DelBookmarks(SwNode & rStt,const SwNode & rEnd,std::vector<SaveBookmark> * pSaveBkmk,std::optional<sal_Int32> oStartContentIdx,std::optional<sal_Int32> oEndContentIdx,bool const isReplace)2023 void DelBookmarks(
2024     SwNode& rStt,
2025     const SwNode& rEnd,
2026     std::vector<SaveBookmark> * pSaveBkmk,
2027     std::optional<sal_Int32> oStartContentIdx,
2028     std::optional<sal_Int32> oEndContentIdx,
2029     bool const isReplace)
2030 {
2031     // illegal range ??
2032     if(rStt.GetIndex() > rEnd.GetIndex()
2033         || (&rStt == &rEnd && (!oStartContentIdx || !oEndContentIdx || *oStartContentIdx >= *oEndContentIdx)))
2034         return;
2035     SwDoc& rDoc = rStt.GetDoc();
2036 
2037     rDoc.getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk,
2038         oStartContentIdx,
2039         oEndContentIdx,
2040         isReplace);
2041 
2042     // Copy all Redlines which are in the move area into an array
2043     // which holds all position information as offset.
2044     // Assignment happens after moving.
2045     SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
2046     for(SwRangeRedline* pRedl : rTable)
2047     {
2048         // Is at position?
2049         auto [pRStt, pREnd] = pRedl->StartEnd();
2050 
2051         if( lcl_Greater( *pRStt, rStt, oStartContentIdx ) && lcl_Lower( *pRStt, rEnd, oEndContentIdx ))
2052         {
2053             pRStt->Assign( rEnd );
2054             if( oEndContentIdx )
2055                 pRStt->SetContent( *oEndContentIdx );
2056             else
2057             {
2058                 bool bStt = true;
2059                 SwContentNode* pCNd = pRStt->GetNode().GetContentNode();
2060                 if( !pCNd )
2061                     pCNd = SwNodes::GoNext(pRStt);
2062                 if (!pCNd)
2063                 {
2064                     bStt = false;
2065                     pRStt->Assign(rStt);
2066                     pCNd = SwNodes::GoPrevious( pRStt );
2067                     if( !pCNd )
2068                     {
2069                         *pRStt = *pREnd;
2070                         pCNd = pRStt->GetNode().GetContentNode();
2071                     }
2072                 }
2073                 if (pCNd && !bStt)
2074                     pRStt->AssignEndIndex( *pCNd );
2075             }
2076         }
2077         if( lcl_Greater( *pREnd, rStt, oStartContentIdx ) && lcl_Lower( *pREnd, rEnd, oEndContentIdx ))
2078         {
2079             pREnd->Assign( rStt );
2080             if (oStartContentIdx && rStt.IsContentNode())
2081                 pREnd->SetContent( *oStartContentIdx );
2082             else
2083             {
2084                 bool bStt = false;
2085                 SwContentNode* pCNd = pREnd->GetNode().GetContentNode();
2086                 if( !pCNd )
2087                     pCNd = SwNodes::GoPrevious( pREnd );
2088                 if( !pCNd )
2089                 {
2090                     bStt = true;
2091                     pREnd->Assign(rEnd);
2092                     pCNd = SwNodes::GoNext(pREnd);
2093                     if( !pCNd )
2094                     {
2095                         *pREnd = *pRStt;
2096                         pCNd = pREnd->GetNode().GetContentNode();
2097                     }
2098                 }
2099                 if (pCNd && !bStt)
2100                     pREnd->AssignEndIndex( *pCNd );
2101             }
2102             if( lcl_Greater( *pRStt, rEnd, oEndContentIdx ) )
2103                 break;
2104         }
2105     }
2106 }
2107 
2108 namespace sw {
2109 
MakeInsertText(SwTextNode & rNode,const sal_Int32 nPos,const sal_Int32 nLen)2110 InsertText MakeInsertText(SwTextNode& rNode, const sal_Int32 nPos, const sal_Int32 nLen)
2111 {
2112     SwCursor cursor(SwPosition(rNode, nPos), nullptr);
2113     bool isInsideFieldmarkCommand(false);
2114     bool isInsideFieldmarkResult(false);
2115     while (auto const*const pMark = rNode.GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(*cursor.GetPoint()))
2116     {
2117         if (sw::mark::FindFieldSep(*pMark) < *cursor.GetPoint())
2118         {
2119             isInsideFieldmarkResult = true;
2120         }
2121         else
2122         {
2123             isInsideFieldmarkCommand = true;
2124         }
2125         *cursor.GetPoint() = pMark->GetMarkStart();
2126         if (!cursor.Left(1))
2127         {
2128             break;
2129         }
2130     }
2131     return InsertText(nPos, nLen, isInsideFieldmarkCommand, isInsideFieldmarkResult);
2132 }
2133 
2134 } // namespace sw
2135 
2136 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2137