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