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