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