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 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
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
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
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
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
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 = rEnd.GetNodes().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 {
304 bool operator()(SwPosition const& rPos,
305 const sw::mark::IMark* pMark)
306 {
307 return rPos < pMark->GetMarkStart();
308 }
309 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 {
320 bool operator()(SwPosition const& rPos,
321 const sw::mark::IMark* pMark)
322 {
323 return pMark->GetMarkStart() > rPos;
324 }
325 };
326
327
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
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
376 bool lcl_FixCorrectedMark(
377 const bool bChangedPos,
378 const bool bChangedOPos,
379 MarkBase* io_pMark )
380 {
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
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
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
439 MarkManager::container_t::const_iterator lcl_FindMarkAtPos(
440 MarkManager::container_t& rMarks,
441 const SwPosition& rPos,
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
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
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
503{
504 const std::type_info* const pMarkTypeInfo = &typeid(rBkmk);
505 // not using dynamic_cast<> here for performance
506 if(*pMarkTypeInfo == typeid(UnoMark))
508 else if(*pMarkTypeInfo == typeid(DdeBookmark))
510 else if(*pMarkTypeInfo == typeid(Bookmark))
511 return MarkType::BOOKMARK;
512 else if(*pMarkTypeInfo == typeid(CrossRefHeadingBookmark))
514 else if(*pMarkTypeInfo == typeid(CrossRefNumItemBookmark))
516 else if(*pMarkTypeInfo == typeid(AnnotationMark))
518 else if(*pMarkTypeInfo == typeid(TextFieldmark))
520 else if(*pMarkTypeInfo == typeid(CheckboxFieldmark))
522 else if(*pMarkTypeInfo == typeid(DropDownFieldmark))
524 else if(*pMarkTypeInfo == typeid(DateFieldmark))
526 else if(*pMarkTypeInfo == typeid(NavigatorReminder))
528 else
529 {
530 assert(false && "IDocumentMarkAccess::GetType(..)"
531 " - unknown MarkType. This needs to be fixed!");
533 }
534}
535
537{
538 return "__RefHeading__";
539}
540
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
550void 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
559}
560
561namespace sw::mark
562{
563 MarkManager::MarkManager(SwDoc& rDoc)
564 : m_rDoc(rDoc)
565 , m_pLastActiveFieldmark(nullptr)
566 { }
567
569 const OUString& rName,
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()
590 // SwXTextRange can be on table node or plain start node (FLY_AT_FLY)
591 || !rPaM.GetPoint()->GetNode().IsStartNode()))
592 || (!rPaM.GetMark()->GetNode().IsTextNode()
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
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
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
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
631 // can't check for Copy - it asserts - but it's also obviously unnecessary
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 {
645 pMark = std::make_unique<TextFieldmark>(rPaM, rName);
646 break;
648 pMark = std::make_unique<CheckboxFieldmark>(rPaM, rName);
649 break;
651 pMark = std::make_unique<DropDownFieldmark>(rPaM, rName);
652 break;
654 pMark = std::make_unique<DateFieldmark>(rPaM);
655 break;
657 pMark = std::make_unique<NavigatorReminder>(rPaM);
658 break;
660 pMark = std::make_unique<Bookmark>(rPaM, vcl::KeyCode(), rName);
661 break;
663 pMark = std::make_unique<DdeBookmark>(rPaM);
664 break;
666 pMark = std::make_unique<CrossRefHeadingBookmark>(rPaM, vcl::KeyCode(), rName);
667 break;
669 pMark = std::make_unique<CrossRefNumItemBookmark>(rPaM, vcl::KeyCode(), rName);
670 break;
672 pMark = std::make_unique<UnoMark>(rPaM);
673 break;
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
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 {
697 lcl_InsertMarkSorted(m_vBookmarks, pMark.get());
698 break;
703 lcl_InsertMarkSorted(m_vFieldmarks, pMark.get());
704 break;
706 lcl_InsertMarkSorted( m_vAnnotationMarks, pMark.get() );
707 break;
711 // no special array for these
712 break;
713 }
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());
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
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,
757 pSepPos);
758 }
759 else
760 {
761 pMark = makeMark(rPaM, rName,
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
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();
791
792 sw::mark::IMark* pMark = nullptr;
793 if(rType == ODF_FORMCHECKBOX)
794 {
795 pMark = makeMark( rPaM, rName,
798 }
799 else if(rType == ODF_FORMDROPDOWN)
800 {
801 pMark = makeMark( rPaM, rName,
804 }
805 else if(rType == ODF_FORMDATE)
806 {
807 pMark = makeMark( rPaM, rName,
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);
825
826 return pFieldMark;
827 }
828
830 const SwTextNode& rTextNode,
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
842 const SwPaM& rPaM,
843 const OUString& rName )
844 {
847 }
848
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
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 }
900 }
901 }
902 return true;
903 }
904
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
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
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
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)
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 {
1052 // no delete of cross-reference bookmarks, if range is inside one paragraph
1053 bDeleteMark = &rStt != &rEnd;
1054 break;
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
1073 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
1088 {
1089 return true;
1090 }
1091 }
1092 return false;
1093 }
1094
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 {
1102 std::vector<const_iterator_t> vMarksToDelete;
1103 bool bIsSortingNeeded = false;
1104
1105 // boolean indicating, if at least one mark has been moved while collecting marks for deletion
1106 bool bMarksMoved = false;
1107 // have marks in the range been skipped instead of deleted
1108 bool bMarksSkipDeletion = false;
1109
1110 // copy all bookmarks in the move area to a vector storing all position data as offset
1111 // reassignment is performed after the move
1112 for (auto ppMark = m_vAllMarks.begin();
1113 ppMark != m_vAllMarks.end();
1114 ++ppMark)
1115 {
1116 ::sw::mark::MarkBase *const pMark = *ppMark;
1117 bool bIsPosInRange(false);
1118 bool bIsOtherPosInRange(false);
1119 bool const bDeleteMark = isDeleteMark(pMark, false, rStt, rEnd, oStartContentIdx, oEndContentIdx, bIsPosInRange, bIsOtherPosInRange);
1120
1121 if ( bIsPosInRange
1122 && ( bIsOtherPosInRange
1123 || !pMark->IsExpanded() ) )
1124 {
1125 if ( bDeleteMark )
1126 {
1127 if ( pSaveBkmk )
1128 {
1129 pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, oStartContentIdx ) );
1130 }
1131 vMarksToDelete.emplace_back(ppMark);
1132 }
1133 else
1134 {
1135 bMarksSkipDeletion = true;
1136 }
1137 }
1138 else if ( bIsPosInRange != bIsOtherPosInRange )
1139 {
1140 // the bookmark is partially in the range
1141 // move position of that is in the range out of it
1142
1143 std::optional< SwPosition > oNewPos;
1144 if ( oEndContentIdx )
1145 {
1146 oNewPos.emplace( *rEnd.GetContentNode(), *oEndContentIdx );
1147 }
1148 else
1149 {
1150 lcl_FindExpelPosition( oNewPos, rStt, rEnd, bIsPosInRange ? pMark->GetOtherMarkPos() : pMark->GetMarkPos() );
1151 }
1152
1153 bool bMoveMark = true;
1154 {
1155 switch ( IDocumentMarkAccess::GetType( *pMark ) )
1156 {
1159 // no move of cross-reference bookmarks, if move occurs inside a certain node
1160 bMoveMark = pMark->GetMarkPos().GetNode() != oNewPos->GetNode();
1161 break;
1163 // no move of annotation marks, if method is called to collect deleted marks
1164 bMoveMark = pSaveBkmk == nullptr;
1165 break;
1166 default:
1167 bMoveMark = true;
1168 break;
1169 }
1170 }
1171 if ( bMoveMark )
1172 {
1173 if ( bIsPosInRange )
1174 pMark->SetMarkPos(*oNewPos);
1175 else
1176 pMark->SetOtherMarkPos(*oNewPos);
1177 bMarksMoved = true;
1178
1179 // illegal selection? collapse the mark and restore sorting later
1180 bIsSortingNeeded |= lcl_FixCorrectedMark( bIsPosInRange, bIsOtherPosInRange, pMark );
1181 }
1182 }
1183 }
1184
1185 {
1186 // fdo#61016 delay the deletion of the fieldmark characters
1187 // to prevent that from deleting the marks on that position
1188 // which would invalidate the iterators in vMarksToDelete
1189 std::vector< std::unique_ptr<ILazyDeleter> > vDelay;
1190 vDelay.reserve(vMarksToDelete.size());
1191
1192 // If needed, sort mark containers containing subsets of the marks
1193 // in order to assure sorting. The sorting is critical for the
1194 // deletion of a mark as it is searched in these container for
1195 // deletion.
1196 if ( !vMarksToDelete.empty() && bMarksMoved )
1197 {
1199 }
1200 // we just remembered the iterators to delete, so we do not need to search
1201 // for the shared_ptr<> (the entry in m_vAllMarks) again
1202 // reverse iteration, since erasing an entry invalidates iterators
1203 // behind it (the iterators in vMarksToDelete are sorted)
1204 for ( std::vector< const_iterator_t >::reverse_iterator pppMark = vMarksToDelete.rbegin();
1205 pppMark != vMarksToDelete.rend();
1206 ++pppMark )
1207 {
1208 vDelay.push_back(deleteMark(*pppMark, pSaveBkmk != nullptr));
1209 }
1210 } // scope to kill vDelay
1211
1212 // also need to sort if both marks were moved and not-deleted because
1213 // the not-deleted marks could be in wrong order vs. the moved ones
1214 if (bIsSortingNeeded || (bMarksMoved && bMarksSkipDeletion))
1215 {
1216 sortMarks();
1217 }
1218
1219 SAL_INFO("sw.core", "deleteMarks");
1220 lcl_DebugMarks(m_vAllMarks);
1221 }
1222
1223 namespace {
1224
1225 struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
1226 {
1227 std::unique_ptr<Fieldmark> m_pFieldmark;
1229 bool const m_isMoveNodes;
1230 LazyFieldmarkDeleter(Fieldmark *const pMark, SwDoc& rDoc, bool const isMoveNodes)
1231 : m_pFieldmark(pMark), m_rDoc(rDoc), m_isMoveNodes(isMoveNodes)
1232 {
1233 assert(m_pFieldmark);
1234 }
1235 virtual ~LazyFieldmarkDeleter() override
1236 {
1237 // note: because of the call chain from SwUndoDelete, the field
1238 // command *cannot* be deleted here as it would create a separate
1239 // SwUndoDelete that's interleaved with the SwHistory of the outer
1240 // one - only delete the CH_TXT_ATR_FIELD*!
1241 if (!m_isMoveNodes)
1242 {
1243 m_pFieldmark->ReleaseDoc(m_rDoc);
1244 }
1245 }
1246 };
1247
1248 // Call DeregisterFromDoc() lazily, because it can call selection change listeners, which
1249 // may mutate the marks container
1250 struct LazyDdeBookmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
1251 {
1252 std::unique_ptr<DdeBookmark> m_pDdeBookmark;
1253 SwDoc& m_rDoc;
1254 LazyDdeBookmarkDeleter(DdeBookmark *const pDdeBookmark, SwDoc& rDoc)
1255 : m_pDdeBookmark(pDdeBookmark), m_rDoc(rDoc)
1256 {
1257 assert(pDdeBookmark);
1258 }
1259 virtual ~LazyDdeBookmarkDeleter() override
1260 {
1261 m_pDdeBookmark->DeregisterFromDoc(m_rDoc);
1262 }
1263 };
1264
1265 }
1266
1267 std::unique_ptr<IDocumentMarkAccess::ILazyDeleter>
1268 MarkManager::deleteMark(const const_iterator_t& ppMark, bool const isMoveNodes)
1269 {
1270 std::unique_ptr<ILazyDeleter> ret;
1271 if (ppMark.get() == m_vAllMarks.end())
1272 return ret;
1273 IMark* pMark = *ppMark;
1274
1275 switch(IDocumentMarkAccess::GetType(*pMark))
1276 {
1278 {
1279 auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
1280 if ( ppBookmark != m_vBookmarks.end() )
1281 {
1282 Bookmark* pBookmark = dynamic_cast<Bookmark*>(*ppBookmark);
1283
1284 if(pBookmark)
1285 pBookmark->sendLOKDeleteCallback();
1286
1287 m_vBookmarks.erase(ppBookmark);
1288 }
1289 else
1290 {
1291 assert(false &&
1292 "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
1293 }
1294 }
1295 break;
1298 {
1299 auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
1300 if ( ppBookmark != m_vBookmarks.end() )
1301 {
1302 m_vBookmarks.erase(ppBookmark);
1303 }
1304 else
1305 {
1306 assert(false &&
1307 "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
1308 }
1309 }
1310 break;
1311
1316 {
1317 auto const ppFieldmark = lcl_FindMark(m_vFieldmarks, *ppMark.get());
1318 if ( ppFieldmark != m_vFieldmarks.end() )
1319 {
1320 if(m_pLastActiveFieldmark == *ppFieldmark)
1322
1323 m_vFieldmarks.erase(ppFieldmark);
1324 ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_rDoc, isMoveNodes));
1325 }
1326 else
1327 {
1328 assert(false &&
1329 "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container.");
1330 }
1331 }
1332 break;
1333
1335 {
1336 auto const ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, *ppMark.get());
1337 assert(ppAnnotationMark != m_vAnnotationMarks.end() &&
1338 "<MarkManager::deleteMark(..)> - Annotation Mark not found in Annotation Mark container.");
1339 m_vAnnotationMarks.erase(ppAnnotationMark);
1340 }
1341 break;
1342
1346 // no special marks container
1347 break;
1348 }
1349 //Effective STL Item 27, get a non-const iterator aI at the same
1350 //position as const iterator ppMark was
1351 auto aI = m_vAllMarks.begin();
1352 std::advance(aI, std::distance<container_t::const_iterator>(aI, ppMark.get()));
1353 DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(pMark);
1354 if (pDdeBookmark)
1355 {
1356 ret.reset(new LazyDdeBookmarkDeleter(pDdeBookmark, m_rDoc));
1357 }
1358
1359 m_vAllMarks.erase(aI);
1360 // If we don't have a lazy deleter
1361 if (!ret)
1362 // delete after we remove from the list, because the destructor can
1363 // recursively call into this method.
1364 delete pMark;
1365 return ret;
1366 }
1367
1368 void MarkManager::deleteMark(const IMark* const pMark)
1369 {
1370 assert(&pMark->GetMarkPos().GetDoc() == &m_rDoc &&
1371 "<MarkManager::deleteMark(..)>"
1372 " - Mark is not in my doc.");
1373 // finds the last Mark that is starting before pMark
1374 // (pMarkLow < pMark)
1375 auto [it, endIt] = equal_range(
1376 m_vAllMarks.begin(),
1377 m_vAllMarks.end(),
1378 pMark->GetMarkStart(),
1379 CompareIMarkStartsBefore());
1380 for ( ; it != endIt; ++it)
1381 if (*it == pMark)
1382 {
1383 deleteMark(iterator(it), false);
1384 break;
1385 }
1386 }
1387
1389 {
1391 m_vFieldmarks.clear();
1392 m_vBookmarks.clear();
1393 m_vAnnotationMarks.clear();
1394 for (const auto & p : m_vAllMarks)
1395 delete p;
1396 m_vAllMarks.clear();
1397 }
1398
1400 {
1401 auto const ret = lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end());
1403 }
1404
1406 {
1407 auto const ret = lcl_FindMarkByName(rName, m_vBookmarks.begin(), m_vBookmarks.end());
1409 }
1410
1412 { return m_vAllMarks.begin(); }
1413
1415 { return m_vAllMarks.end(); }
1416
1418 { return m_vAllMarks.size(); }
1419
1421 { return m_vBookmarks.begin(); }
1422
1424 { return m_vBookmarks.end(); }
1425
1427 { return m_vBookmarks.size(); }
1428
1430 { return m_vFieldmarks.begin(); }
1431
1433 { return m_vFieldmarks.end(); }
1434
1435 sal_Int32 MarkManager::getFieldmarksCount() const { return m_vFieldmarks.size(); }
1436
1437
1438 // finds the first that is starting after
1440 {
1441 return std::upper_bound(
1442 m_vBookmarks.begin(),
1443 m_vBookmarks.end(),
1444 rPos,
1445 CompareIMarkStartsAfter());
1446 }
1447
1448 IFieldmark* MarkManager::getFieldmarkAt(const SwPosition& rPos) const
1449 {
1450 auto const pFieldmark = find_if(
1451 m_vFieldmarks.begin(),
1452 m_vFieldmarks.end(),
1453 [&rPos] (::sw::mark::MarkBase const*const pMark) {
1454 return pMark->GetMarkStart() == rPos
1455 // end position includes the CH_TXT_ATR_FIELDEND
1456 || (pMark->GetMarkEnd().GetContentIndex() == rPos.GetContentIndex() + 1
1457 && pMark->GetMarkEnd().GetNode() == rPos.GetNode());
1458 } );
1459 return (pFieldmark == m_vFieldmarks.end())
1460 ? nullptr
1461 : dynamic_cast<IFieldmark*>(*pFieldmark);
1462 }
1463
1464 IFieldmark* MarkManager::getInnerFieldmarkFor(const SwPosition& rPos) const
1465 {
1466 auto itFieldmark = find_if(
1467 m_vFieldmarks.begin(),
1468 m_vFieldmarks.end(),
1469 [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
1470 if (itFieldmark == m_vFieldmarks.end())
1471 return nullptr;
1472 auto pFieldmark(*itFieldmark);
1473
1474 // See if any fieldmarks after the first hit are closer to rPos.
1475 ++itFieldmark;
1476 for ( ; itFieldmark != m_vFieldmarks.end()
1477 && (**itFieldmark).GetMarkStart() <= rPos; ++itFieldmark)
1478 { // find the innermost fieldmark
1479 if (rPos < (**itFieldmark).GetMarkEnd()
1480 && (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart()
1481 || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd()))
1482 {
1483 pFieldmark = *itFieldmark;
1484 }
1485 }
1486 return dynamic_cast<IFieldmark*>(pFieldmark);
1487 }
1488
1490 {
1491 auto it = std::find_if(m_vBookmarks.begin(), m_vBookmarks.end(),
1492 [&rPos](const sw::mark::MarkBase* pMark)
1493 { return pMark->IsCoveringPosition(rPos); });
1494 if (it == m_vBookmarks.end())
1495 {
1496 return nullptr;
1497 }
1498 sw::mark::IMark* pBookmark = *it;
1499
1500 // See if any bookmarks after the first hit are closer to rPos.
1501 ++it;
1502
1503 for (; it != m_vBookmarks.end() && (*it)->GetMarkStart() <= rPos; ++it)
1504 {
1505 // Find the innermost bookmark.
1506 if (rPos < (*it)->GetMarkEnd()
1507 && (pBookmark->GetMarkStart() < (*it)->GetMarkStart()
1508 || (*it)->GetMarkEnd() < pBookmark->GetMarkEnd()))
1509 {
1510 pBookmark = *it;
1511 }
1512 }
1513 return pBookmark;
1514 }
1515
1517 {
1518 auto const pFieldmark = dynamic_cast<Fieldmark*>(getFieldmarkAt(rPos));
1519 assert(pFieldmark); // currently all callers require it to be there
1520
1521 deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark), false);
1522 }
1523
1524 ::sw::mark::IFieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType)
1525 {
1526 bool bActualChange = false;
1527 if(rNewType == ODF_FORMDROPDOWN)
1528 {
1529 if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark))
1530 bActualChange = true;
1531 if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
1532 return nullptr;
1533 }
1534 else if(rNewType == ODF_FORMCHECKBOX)
1535 {
1536 if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark))
1537 bActualChange = true;
1538 if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
1539 return nullptr;
1540 }
1541 else if(rNewType == ODF_FORMDATE)
1542 {
1543 if (!dynamic_cast<::sw::mark::DateFieldmark*>(pFieldmark))
1544 bActualChange = true;
1545 if (!dynamic_cast<::sw::mark::TextFieldmark*>(pFieldmark)) // only allowed converting between date field <-> text field
1546 return nullptr;
1547 }
1548
1549 if (!bActualChange)
1550 return nullptr;
1551
1552 // Store attributes needed to create the new fieldmark
1553 OUString sName = pFieldmark->GetName();
1554 SwPaM const aPaM(pFieldmark->GetMarkStart());
1555
1556 // Remove the old fieldmark and create a new one with the new type
1557 if (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX)
1558 {
1559 SwPosition aNewPos (*aPaM.GetPoint());
1560 deleteFieldmarkAt(aNewPos);
1561 return makeNoTextFieldBookmark(aPaM, sName, rNewType);
1562 }
1563 else if(rNewType == ODF_FORMDATE)
1564 {
1565 SwPosition aPos (*aPaM.GetPoint());
1566 SwPaM aNewPaM(pFieldmark->GetMarkStart(), pFieldmark->GetMarkEnd());
1567 deleteFieldmarkAt(aPos);
1568 // HACK: hard-code the separator position here at the start because
1569 // writerfilter put it in the wrong place (at the end) on attach()
1570 SwPosition const sepPos(*aNewPaM.Start());
1571 return makeFieldBookmark(aNewPaM, sName, rNewType, &sepPos);
1572 }
1573 return nullptr;
1574 }
1575
1577 {
1578 SwView* pSwView = dynamic_cast<SwView *>(rCursorShell.GetSfxViewShell());
1579 if(!pSwView)
1580 return;
1581
1582 SwEditWin& rEditWin = pSwView->GetEditWin();
1583 SwPosition aPos(*rCursorShell.GetCursor()->GetPoint());
1584 IFieldmark* pFieldBM = getInnerFieldmarkFor(aPos);
1585 FieldmarkWithDropDownButton* pNewActiveFieldmark = nullptr;
1586 if ((!pFieldBM || (pFieldBM->GetFieldname() != ODF_FORMDROPDOWN && pFieldBM->GetFieldname() != ODF_FORMDATE))
1587 && aPos.GetContentIndex() > 0 )
1588 {
1589 aPos.AdjustContent(-1);
1590 pFieldBM = getInnerFieldmarkFor(aPos);
1591 }
1592
1593 if ( pFieldBM && (pFieldBM->GetFieldname() == ODF_FORMDROPDOWN ||
1594 pFieldBM->GetFieldname() == ODF_FORMDATE))
1595 {
1596 if (m_pLastActiveFieldmark != pFieldBM)
1597 {
1598 FieldmarkWithDropDownButton& rFormField = dynamic_cast<FieldmarkWithDropDownButton&>(*pFieldBM);
1599 pNewActiveFieldmark = &rFormField;
1600 }
1601 else
1602 {
1603 pNewActiveFieldmark = m_pLastActiveFieldmark;
1604 }
1605 }
1606
1607 if(pNewActiveFieldmark != m_pLastActiveFieldmark)
1608 {
1610 m_pLastActiveFieldmark = pNewActiveFieldmark;
1611 if(pNewActiveFieldmark)
1612 pNewActiveFieldmark->ShowButton(&rEditWin);
1613 }
1614
1615 LOKUpdateActiveField(pSwView);
1616 }
1617
1619 {
1622
1623 m_pLastActiveFieldmark = nullptr;
1624 }
1625
1627 {
1629 return;
1630
1632 {
1633 if (auto pDrowDown = m_pLastActiveFieldmark->GetFieldname() == ODF_FORMDROPDOWN ?
1635 nullptr)
1636 {
1637 pDrowDown->SendLOKShowMessage(pViewShell);
1638 }
1639 }
1640 else
1641 {
1642 // Check whether we have any drop down fieldmark at all.
1643 bool bDropDownFieldExist = false;
1644 for (auto aIter = m_vFieldmarks.begin(); aIter != m_vFieldmarks.end(); ++aIter)
1645 {
1646 IFieldmark *pMark = dynamic_cast<IFieldmark*>(*aIter);
1647 if (pMark && pMark->GetFieldname() == ODF_FORMDROPDOWN)
1648 {
1649 bDropDownFieldExist = true;
1650 break;
1651 }
1652 }
1653
1654 if (bDropDownFieldExist)
1656 }
1657 }
1658
1659 IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const
1660 {
1661 IFieldmark *pMark = getFieldmarkAt(rPos);
1662 if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN)
1663 return nullptr;
1664 return pMark;
1665 }
1666
1667 std::vector<IFieldmark*> MarkManager::getNoTextFieldmarksIn(const SwPaM &rPaM) const
1668 {
1669 std::vector<IFieldmark*> aRet;
1670
1671 for (auto aI = m_vFieldmarks.begin(),
1672 aEnd = m_vFieldmarks.end(); aI != aEnd; ++aI)
1673 {
1674 ::sw::mark::IMark* pI = *aI;
1675 const SwPosition &rStart = pI->GetMarkPos();
1676 if (!rPaM.ContainsPosition(rStart))
1677 continue;
1678
1679 IFieldmark *pMark = dynamic_cast<IFieldmark*>(pI);
1680 if (!pMark || (pMark->GetFieldname() != ODF_FORMDROPDOWN
1681 && pMark->GetFieldname() != ODF_FORMCHECKBOX))
1682 {
1683 continue;
1684 }
1685
1686 aRet.push_back(pMark);
1687 }
1688
1689 return aRet;
1690 }
1691
1692 IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos, bool bLoop) const
1693 { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos, bLoop)); }
1694
1695 IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const
1696 { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos, bLoop)); }
1697
1699 {
1700 return m_vAnnotationMarks.begin();
1701 }
1702
1704 {
1705 return m_vAnnotationMarks.end();
1706 }
1707
1709 {
1710 return m_vAnnotationMarks.size();
1711 }
1712
1714 {
1715 auto const ret = lcl_FindMarkByName( rName, m_vAnnotationMarks.begin(), m_vAnnotationMarks.end() );
1717 }
1718
1720 {
1721 auto const pAnnotationMark = find_if(
1722 m_vAnnotationMarks.begin(),
1723 m_vAnnotationMarks.end(),
1724 [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
1725 if (pAnnotationMark == m_vAnnotationMarks.end())
1726 return nullptr;
1727 return *pAnnotationMark;
1728 }
1729
1730 // finds the first that is starting after
1732 {
1733 return std::upper_bound(
1734 m_vAnnotationMarks.begin(),
1735 m_vAnnotationMarks.end(),
1736 rPos,
1737 CompareIMarkStartsAfter());
1738 }
1739
1740 // create helper bookmark for annotations on tracked deletions
1742 const OUString& rName,
1744 sw::mark::InsertMode const eMode,
1745 SwPosition const*const pSepPos)
1746 {
1747 OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
1748 return makeMark( rPaM, sAnnotationBookmarkName, eType, eMode, pSepPos);
1749 }
1750
1751 // find helper bookmark of annotations on tracked deletions
1753 {
1754 OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
1755 return findBookmark(sAnnotationBookmarkName);
1756 }
1757
1758 // restore text ranges of annotations on tracked deletions
1759 // based on the helper bookmarks (which can survive I/O and hiding redlines)
1761 {
1762 for (auto iter = getBookmarksBegin();
1763 iter != getBookmarksEnd(); )
1764 {
1765 const OUString & rBookmarkName = (**iter).GetName();
1766 sal_Int32 nPos;
1767 if ( rBookmarkName.startsWith("__Annotation__") &&
1768 (nPos = rBookmarkName.indexOf(S_ANNOTATION_BOOKMARK)) > -1 )
1769 {
1770 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
1771 IDocumentMarkAccess::const_iterator_t pMark = findAnnotationMark(rBookmarkName.copy(0, nPos));
1772 if ( pMark != getAnnotationMarksEnd() )
1773 {
1774 const SwPaM aPam((**iter).GetMarkStart(), (**pMark).GetMarkEnd());
1775 repositionMark(*pMark, aPam);
1776 }
1777 if (bDelete)
1778 {
1779 deleteMark(&**iter);
1780 // this invalidates iter, have to start over...
1781 iter = getBookmarksBegin();
1782 }
1783 else
1784 ++iter;
1785 }
1786 else
1787 ++iter;
1788 }
1789 }
1790
1791 OUString MarkManager::getUniqueMarkName(const OUString& rName) const
1792 {
1793 OSL_ENSURE(rName.getLength(),
1794 "<MarkManager::getUniqueMarkName(..)> - a name should be proposed");
1795 if( m_rDoc.IsInMailMerge())
1796 {
1797 OUString newName = rName + "MailMergeMark"
1798 + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
1799 + OUString::number( m_vAllMarks.size() + 1 );
1800 return newName;
1801 }
1802
1803 if (lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
1804 {
1805 return rName;
1806 }
1807 OUString sTmp;
1808
1809 // try the name "<rName>XXX" (where XXX is a number starting from 1) unless there is
1810 // an unused name. Due to performance-reasons (especially in mailmerge-scenarios) there
1811 // is a map m_aMarkBasenameMapUniqueOffset which holds the next possible offset (XXX) for
1812 // rName (so there is no need to test for nCnt-values smaller than the offset).
1813 sal_Int32 nCnt = 1;
1814 MarkBasenameMapUniqueOffset_t::const_iterator aIter = m_aMarkBasenameMapUniqueOffset.find(rName);
1815 if(aIter != m_aMarkBasenameMapUniqueOffset.end()) nCnt = aIter->second;
1816 OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rName);
1817 while(nCnt < SAL_MAX_INT32)
1818 {
1819 sTmp = aPrefix + OUString::number(nCnt);
1820 nCnt++;
1821 if (lcl_FindMarkByName(sTmp, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
1822 {
1823 break;
1824 }
1825 }
1826 m_aMarkBasenameMapUniqueOffset[rName] = nCnt;
1827
1828 return sTmp;
1829 }
1830
1832 {
1833 const_cast< MarkManager* >(this)->sortMarks();
1834 }
1835
1837 {
1838 stable_sort(m_vBookmarks.begin(), m_vBookmarks.end(), &lcl_MarkOrderingByStart);
1839 sort(m_vFieldmarks.begin(), m_vFieldmarks.end(), &lcl_MarkOrderingByStart);
1840 sort(m_vAnnotationMarks.begin(), m_vAnnotationMarks.end(), &lcl_MarkOrderingByStart);
1841 }
1842
1844 {
1845 sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart);
1847 }
1848
1850{
1851 struct
1852 {
1853 const char* pName;
1854 const container_t* pContainer;
1855 } aContainers[] =
1856 {
1857 // UNO marks are only part of all marks.
1858 {"allmarks", &m_vAllMarks},
1859 {"bookmarks", &m_vBookmarks},
1860 {"fieldmarks", &m_vFieldmarks},
1861 {"annotationmarks", &m_vAnnotationMarks}
1862 };
1863
1864 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager"));
1865 for (const auto & rContainer : aContainers)
1866 {
1867 if (!rContainer.pContainer->empty())
1868 {
1869 (void)xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName));
1870 for (auto it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it)
1871 (*it)->dumpAsXml(pWriter);
1872 (void)xmlTextWriterEndElement(pWriter);
1873 }
1874 }
1875 (void)xmlTextWriterEndElement(pWriter);
1876}
1877
1878} // namespace ::sw::mark
1879
1880namespace
1881{
1882 bool lcl_Greater( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
1883 {
1884 return rPos.GetNode() > rNdIdx ||
1885 ( oContentIdx && rPos.GetNode() == rNdIdx && rPos.GetContentIndex() > *oContentIdx );
1886 }
1887}
1888
1889// IDocumentMarkAccess for SwDoc
1891 { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
1892
1894 { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
1895
1896SaveBookmark::SaveBookmark(
1897 const IMark& rBkmk,
1898 const SwNode& rMvPos,
1899 std::optional<sal_Int32> oContentIdx)
1900 : m_aName(rBkmk.GetName())
1901 , m_bHidden(false)
1902 , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk))
1903{
1904 const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk);
1905 if(pBookmark)
1906 {
1907 m_aShortName = pBookmark->GetShortName();
1908 m_aCode = pBookmark->GetKeyCode();
1909 m_bHidden = pBookmark->IsHidden();
1910 m_aHideCondition = pBookmark->GetHideCondition();
1911
1912 ::sfx2::Metadatable const*const pMetadatable(
1913 dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
1914 if (pMetadatable)
1915 {
1916 m_pMetadataUndo = pMetadatable->CreateUndo();
1917 }
1918 }
1919 m_nNode1 = rBkmk.GetMarkPos().GetNodeIndex();
1921
1922 m_nNode1 -= rMvPos.GetIndex();
1923 if(oContentIdx && !m_nNode1)
1924 m_nContent1 -= *oContentIdx;
1925
1926 if(rBkmk.IsExpanded())
1927 {
1930
1931 m_nNode2 -= rMvPos.GetIndex();
1932 if(oContentIdx && !m_nNode2)
1933 m_nContent2 -= *oContentIdx;
1934 }
1935 else
1936 {
1938 m_nContent2 = -1;
1939 }
1940}
1941
1943 SwDoc* pDoc,
1944 const SwNode& rNewPos,
1945 std::optional<sal_Int32> oContentIdx)
1946{
1947 SwPaM aPam(rNewPos);
1948 if(oContentIdx)
1949 {
1950 if (aPam.GetPoint()->GetNode().IsContentNode())
1951 aPam.GetPoint()->SetContent( *oContentIdx );
1952 else
1953 SAL_WARN("sw", "trying to sent content index, but point node is not a content node");
1954 }
1955
1957 {
1958 aPam.SetMark();
1959
1960 aPam.GetMark()->Adjust(m_nNode2);
1961 if (aPam.GetMark()->GetNode().IsContentNode())
1962 {
1963 if(oContentIdx && !m_nNode2)
1964 aPam.GetMark()->SetContent(*oContentIdx + m_nContent2);
1965 else
1967 }
1968 else
1969 SAL_WARN("sw", "trying to sent content index, but mark node is not a content node");
1970 }
1971
1972 aPam.GetPoint()->Adjust(m_nNode1);
1973
1974 if (aPam.GetPoint()->GetNode().IsContentNode())
1975 {
1976 if(oContentIdx && !m_nNode1)
1977 aPam.GetPoint()->SetContent(*oContentIdx + m_nContent1);
1978 else
1980 }
1981
1982 if(aPam.HasMark()
1983 && !CheckNodesRange(aPam.GetPoint()->GetNode(), aPam.GetMark()->GetNode(), true))
1984 return;
1985
1986 ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>(
1989 if(!pBookmark)
1990 return;
1991
1992 pBookmark->SetKeyCode(m_aCode);
1993 pBookmark->SetShortName(m_aShortName);
1994 pBookmark->Hide(m_bHidden);
1996
1997 if (m_pMetadataUndo)
1998 {
1999 ::sfx2::Metadatable * const pMeta(
2000 dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
2001 assert(pMeta && "metadata undo, but not metadatable?");
2002 if (pMeta)
2003 {
2004 pMeta->RestoreMetadata(m_pMetadataUndo);
2005 }
2006 }
2007}
2008
2009// DelBookmarks
2010
2012 SwNode& rStt,
2013 const SwNode& rEnd,
2014 std::vector<SaveBookmark> * pSaveBkmk,
2015 std::optional<sal_Int32> oStartContentIdx,
2016 std::optional<sal_Int32> oEndContentIdx)
2017{
2018 // illegal range ??
2019 if(rStt.GetIndex() > rEnd.GetIndex()
2020 || (&rStt == &rEnd && (!oStartContentIdx || !oEndContentIdx || *oStartContentIdx >= *oEndContentIdx)))
2021 return;
2022 SwDoc& rDoc = rStt.GetDoc();
2023
2024 rDoc.getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk,
2025 oStartContentIdx,
2026 oEndContentIdx);
2027
2028 // Copy all Redlines which are in the move area into an array
2029 // which holds all position information as offset.
2030 // Assignment happens after moving.
2032 for(SwRangeRedline* pRedl : rTable)
2033 {
2034 // Is at position?
2035 auto [pRStt, pREnd] = pRedl->StartEnd();
2036
2037 if( lcl_Greater( *pRStt, rStt, oStartContentIdx ) && lcl_Lower( *pRStt, rEnd, oEndContentIdx ))
2038 {
2039 pRStt->Assign( rEnd );
2040 if( oEndContentIdx )
2041 pRStt->SetContent( *oEndContentIdx );
2042 else
2043 {
2044 bool bStt = true;
2045 SwContentNode* pCNd = pRStt->GetNode().GetContentNode();
2046 if( !pCNd )
2047 pCNd = rDoc.GetNodes().GoNext( pRStt );
2048 if (!pCNd)
2049 {
2050 bStt = false;
2051 pRStt->Assign(rStt);
2052 pCNd = SwNodes::GoPrevious( pRStt );
2053 if( !pCNd )
2054 {
2055 *pRStt = *pREnd;
2056 pCNd = pRStt->GetNode().GetContentNode();
2057 }
2058 }
2059 if (pCNd && !bStt)
2060 pRStt->AssignEndIndex( *pCNd );
2061 }
2062 }
2063 if( lcl_Greater( *pREnd, rStt, oStartContentIdx ) && lcl_Lower( *pREnd, rEnd, oEndContentIdx ))
2064 {
2065 pREnd->Assign( rStt );
2066 if (oStartContentIdx && rStt.IsContentNode())
2067 pREnd->SetContent( *oStartContentIdx );
2068 else
2069 {
2070 bool bStt = false;
2071 SwContentNode* pCNd = pREnd->GetNode().GetContentNode();
2072 if( !pCNd )
2073 pCNd = SwNodes::GoPrevious( pREnd );
2074 if( !pCNd )
2075 {
2076 bStt = true;
2077 pREnd->Assign(rEnd);
2078 pCNd = rDoc.GetNodes().GoNext( pREnd );
2079 if( !pCNd )
2080 {
2081 *pREnd = *pRStt;
2082 pCNd = pREnd->GetNode().GetContentNode();
2083 }
2084 }
2085 if (pCNd && !bStt)
2086 pREnd->AssignEndIndex( *pCNd );
2087 }
2088 }
2089 }
2090}
2091
2092namespace sw {
2093
2094InsertText MakeInsertText(SwTextNode& rNode, const sal_Int32 nPos, const sal_Int32 nLen)
2095{
2096 SwCursor cursor(SwPosition(rNode, nPos), nullptr);
2097 bool isInsideFieldmarkCommand(false);
2098 bool isInsideFieldmarkResult(false);
2099 while (auto const*const pMark = rNode.GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(*cursor.GetPoint()))
2100 {
2101 if (sw::mark::FindFieldSep(*pMark) < *cursor.GetPoint())
2102 {
2103 isInsideFieldmarkResult = true;
2104 }
2105 else
2106 {
2107 isInsideFieldmarkCommand = true;
2108 }
2109 *cursor.GetPoint() = pMark->GetMarkStart();
2110 if (!cursor.Left(1))
2111 {
2112 break;
2113 }
2114 }
2115 return InsertText(nPos, nLen, isInsideFieldmarkCommand, isInsideFieldmarkResult);
2116}
2117
2118} // namespace sw
2119
2120/* 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 * getInnerFieldmarkFor(const SwPosition &pos) const =0
static SW_DLLPUBLIC OUString GetCrossRefHeadingBookmarkNamePrefix()
Definition: docbm.cxx:536
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:550
static SW_DLLPUBLIC bool IsLegalPaMForCrossRefHeadingBookmark(const SwPaM &rPaM)
Definition: docbm.cxx:541
static SW_DLLPUBLIC MarkType GetType(const ::sw::mark::IMark &rMark)
Returns the MarkType used to create the mark.
Definition: docbm.cxx:502
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:1256
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:197
IDocumentState const & getIDocumentState() const
Definition: doc.cxx:408
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:329
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:158
SwNodes & GetNodes()
Definition: doc.hxx:422
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:349
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1890
bool IsInMailMerge() const
Definition: doc.hxx:975
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:901
SwNodeOffset GetIndex() const
Definition: node.hxx:312
SwNodes & GetNodes()
Node is in which nodes-array/doc?
Definition: node.hxx:706
bool IsContentNode() const
Definition: node.hxx:188
SwDoc & GetDoc()
Definition: node.hxx:233
bool IsStartNode() const
Definition: node.hxx:187
bool IsTextNode() const
Definition: node.hxx:190
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:188
const SwPosition * GetMark() const
Definition: pam.hxx:255
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:643
bool ContainsPosition(const SwPosition &rPos) const
Definition: pam.hxx:307
const SwPosition * End() const
Definition: pam.hxx:263
SwDoc & GetDoc() const
Definition: pam.hxx:291
const SwPosition * GetPoint() const
Definition: pam.hxx:253
const SwPosition * Start() const
Definition: pam.hxx:258
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:251
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:291
SfxViewShell * GetSfxViewShell() const
Definition: viewsh.hxx:470
Definition: view.hxx:146
SwEditWin & GetEditWin()
Definition: view.hxx:426
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:1719
virtual ::sw::mark::IMark * getOneInnermostBookmarkFor(const SwPosition &rPos) const override
Get the innermost bookmark that contains rPos.
Definition: docbm.cxx:1489
virtual sal_Int32 getAllMarksCount() const override
returns the number of marks.
Definition: docbm.cxx:1417
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:1429
virtual bool renameMark(::sw::mark::IMark *io_pMark, const OUString &rNewName) override
Renames an existing Mark, if possible.
Definition: docbm.cxx:876
virtual void restoreAnnotationMarks(bool bDelete=true) override
Definition: docbm.cxx:1760
virtual const_iterator_t findAnnotationBookmark(const OUString &rName) const override
Definition: docbm.cxx:1752
std::vector< sw::mark::MarkBase * > container_t
virtual void ClearFieldActivation() override
Definition: docbm.cxx:1618
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:1448
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:1741
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:905
virtual void NotifyCursorUpdate(const SwCursorShell &rCursorShell) override
Definition: docbm.cxx:1576
virtual void clearAllMarks() override
Clear (deletes) all marks.
Definition: docbm.cxx:1388
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:1411
OUString getUniqueMarkName(const OUString &rName) const
Definition: docbm.cxx:1791
virtual const_iterator_t getAnnotationMarksBegin() const override
Definition: docbm.cxx:1698
virtual void assureSortedMarkContainers() const override
Definition: docbm.cxx:1831
virtual sw::mark::IFieldmark * makeNoTextFieldBookmark(const SwPaM &rPaM, const OUString &rName, const OUString &rType) override
Definition: docbm.cxx:780
void LOKUpdateActiveField(const SfxViewShell *pViewShell)
Definition: docbm.cxx:1626
sw::mark::FieldmarkWithDropDownButton * m_pLastActiveFieldmark
virtual std::vector<::sw::mark::IFieldmark * > getNoTextFieldmarksIn(const SwPaM &rPaM) const override
Definition: docbm.cxx:1667
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:1432
virtual const_iterator_t findFirstAnnotationStartsAfter(const SwPosition &rPos) const override
Finds the first mark that is starting after.
Definition: docbm.cxx:1731
virtual sw::mark::IFieldmark * getFieldmarkAfter(const SwPosition &rPos, bool bLoop) const override
Definition: docbm.cxx:1692
container_t m_vFieldmarks
virtual const_iterator_t getAnnotationMarksEnd() const override
Definition: docbm.cxx:1703
MarkBasenameMapUniqueOffset_t m_aMarkBasenameMapUniqueOffset
container_t m_vBookmarks
virtual ::sw::mark::IFieldmark * changeFormFieldmarkType(::sw::mark::IFieldmark *pFieldmark, const OUString &rNewType) override
Definition: docbm.cxx:1524
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:951
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:849
virtual sw::mark::IFieldmark * getInnerFieldmarkFor(const SwPosition &rPos) const override
Definition: docbm.cxx:1464
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:1095
virtual sw::mark::IFieldmark * makeFieldBookmark(const SwPaM &rPaM, const OUString &rName, const OUString &rType, SwPosition const *pSepPos=nullptr) override
Definition: docbm.cxx:740
virtual ::sw::mark::IMark * getMarkForTextNode(const SwTextNode &rTextNode, IDocumentMarkAccess::MarkType eMark) override
Returns a mark in the document for a paragraph.
Definition: docbm.cxx:829
virtual sal_Int32 getFieldmarksCount() const override
returns the number of IFieldmarks.
Definition: docbm.cxx:1435
void dumpAsXml(xmlTextWriterPtr pWriter) const
Definition: docbm.cxx:1849
virtual sw::mark::IMark * makeAnnotationMark(const SwPaM &rPaM, const OUString &rName) override
Definition: docbm.cxx:841
virtual const_iterator_t findFirstBookmarkStartsAfter(const SwPosition &rPos) const override
Finds the first mark that is starting after.
Definition: docbm.cxx:1439
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:568
virtual const_iterator_t findAnnotationMark(const OUString &rName) const override
Definition: docbm.cxx:1713
virtual ::sw::mark::IFieldmark * getDropDownFor(const SwPosition &rPos) const override
Definition: docbm.cxx:1659
virtual sw::mark::IFieldmark * getFieldmarkBefore(const SwPosition &rPos, bool bLoop) const override
Definition: docbm.cxx:1695
virtual void deleteFieldmarkAt(const SwPosition &rPos) override
Definition: docbm.cxx:1516
virtual const_iterator_t findBookmark(const OUString &rName) const override
Finds a bookmark by name.
Definition: docbm.cxx:1405
virtual sal_Int32 getAnnotationMarksCount() const override
Definition: docbm.cxx:1708
virtual bool isBookmarkDeleted(SwPaM const &rPaM, bool isReplace) const override
check if the selection would delete a BOOKMARK
Definition: docbm.cxx:1073
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:1423
virtual std::unique_ptr< ILazyDeleter > deleteMark(const const_iterator_t &ppMark, bool isMoveNodes) override
Deletes a mark.
Definition: docbm.cxx:1268
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:1414
virtual sal_Int32 getBookmarksCount() const override
returns the number of IBookmarks.
Definition: docbm.cxx:1426
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:1420
virtual const_iterator_t findMark(const OUString &rName) const override
Finds a mark by name.
Definition: docbm.cxx:1399
vcl::KeyCode m_aCode
Definition: mvsave.hxx:64
void SetInDoc(SwDoc *pDoc, const SwNode &, std::optional< sal_Int32 > oContentIdx=std::nullopt)
Definition: docbm.cxx:1942
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:1228
bool const m_isMoveNodes
Definition: docbm.cxx:1229
constexpr OUStringLiteral S_ANNOTATION_BOOKMARK
Definition: docbm.cxx:53
std::unique_ptr< Fieldmark > m_pFieldmark
Definition: docbm.cxx:1227
void DelBookmarks(SwNode &rStt, const SwNode &rEnd, std::vector< SaveBookmark > *pSaveBkmk, std::optional< sal_Int32 > oStartContentIdx, std::optional< sal_Int32 > oEndContentIdx)
Definition: docbm.cxx:2011
std::unique_ptr< DdeBookmark > m_pDdeBookmark
Definition: docbm.cxx:1252
float u
DocumentType eType
OUString sName
Mode eMode
void * p
sal_Int64 n
sal_uInt16 nPos
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
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:1000
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:2094
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:349
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:38
void Adjust(SwNodeOffset nDelta)
Adjust node position, and resets content position to zero.
Definition: pam.cxx:257
SwNode & GetNode() const
Definition: pam.hxx:81
void SetContent(sal_Int32 nContentIndex)
Set content index, only valid to call this if the position points to a SwContentNode subclass.
Definition: pam.cxx:267
SwNodeOffset GetNodeIndex() const
Definition: pam.hxx:78
sal_Int32 GetContentIndex() const
Definition: pam.hxx:85
void AdjustContent(sal_Int32 nDelta)
Adjust content index, only valid to call this if the position points to a SwContentNode subclass.
Definition: pam.cxx:262
SwDoc & GetDoc() const
Returns the document this position is in.
Definition: pam.cxx:218
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:168
#define SAL_MAX_INT32