1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
23 #include <MarkManager.hxx>
24 #include <bookmark.hxx>
25 #include <crossrefbookmark.hxx>
27 #include <annotationmark.hxx>
29 #include <IDocumentRedlineAccess.hxx>
30 #include <IDocumentState.hxx>
31 #include <IDocumentUndoRedo.hxx>
33 #include <xmloff/odffields.hxx>
38 #include <redline.hxx>
40 #include <rtl/ustring.hxx>
41 #include <sal/types.h>
42 #include <sal/log.hxx>
43 #include <UndoBookmark.hxx>
44 #include <tools/datetimeutils.hxx>
48 #include <libxml/xmlstring.h>
49 #include <libxml/xmlwriter.h>
50 #include <comphelper/lok.hxx>
51 #include <strings.hrc>
53 constexpr OUStringLiteral S_ANNOTATION_BOOKMARK
= u
"____";
55 using namespace ::sw::mark
;
57 std::vector
<::sw::mark::MarkBase
*>::const_iterator
const&
58 IDocumentMarkAccess::iterator::get() const
63 IDocumentMarkAccess::iterator::iterator(std::vector
<::sw::mark::MarkBase
*>::const_iterator
const& rIter
)
68 IDocumentMarkAccess::iterator::iterator(iterator
const& rOther
)
69 : m_pIter(rOther
.m_pIter
)
73 auto IDocumentMarkAccess::iterator::operator=(iterator
const& rOther
) -> iterator
&
75 m_pIter
= rOther
.m_pIter
;
79 IDocumentMarkAccess::iterator::iterator(iterator
&& rOther
) noexcept
80 : m_pIter(std::move(rOther
.m_pIter
))
84 auto IDocumentMarkAccess::iterator::operator=(iterator
&& rOther
) noexcept
-> iterator
&
86 m_pIter
= std::move(rOther
.m_pIter
);
90 // ARGH why does it *need* to return const& ?
91 ::sw::mark::IMark
* /*const&*/
92 IDocumentMarkAccess::iterator::operator*() const
94 return static_cast<sw::mark::IMark
*>(**m_pIter
);
97 auto IDocumentMarkAccess::iterator::operator++() -> iterator
&
102 auto IDocumentMarkAccess::iterator::operator++(int) -> iterator
109 bool IDocumentMarkAccess::iterator::operator==(iterator
const& rOther
) const
111 return *m_pIter
== *rOther
.m_pIter
;
114 bool IDocumentMarkAccess::iterator::operator!=(iterator
const& rOther
) const
116 return *m_pIter
!= *rOther
.m_pIter
;
119 IDocumentMarkAccess::iterator::iterator()
120 : m_pIter(std::in_place
)
124 auto IDocumentMarkAccess::iterator::operator--() -> iterator
&
130 auto IDocumentMarkAccess::iterator::operator--(int) -> iterator
137 auto IDocumentMarkAccess::iterator::operator+=(difference_type
const n
) -> iterator
&
143 auto IDocumentMarkAccess::iterator::operator+(difference_type
const n
) const -> iterator
145 return iterator(*m_pIter
+ n
);
148 auto IDocumentMarkAccess::iterator::operator-=(difference_type
const n
) -> iterator
&
154 auto IDocumentMarkAccess::iterator::operator-(difference_type
const n
) const -> iterator
156 return iterator(*m_pIter
- n
);
159 auto IDocumentMarkAccess::iterator::operator-(iterator
const& rOther
) const -> difference_type
161 return *m_pIter
- *rOther
.m_pIter
;
164 auto IDocumentMarkAccess::iterator::operator[](difference_type
const n
) const -> value_type
166 return static_cast<sw::mark::IMark
*>((*m_pIter
)[n
]);
169 bool IDocumentMarkAccess::iterator::operator<(iterator
const& rOther
) const
171 return *m_pIter
< *rOther
.m_pIter
;
173 bool IDocumentMarkAccess::iterator::operator>(iterator
const& rOther
) const
175 return *m_pIter
> *rOther
.m_pIter
;
177 bool IDocumentMarkAccess::iterator::operator<=(iterator
const& rOther
) const
179 return *m_pIter
<= *rOther
.m_pIter
;
181 bool IDocumentMarkAccess::iterator::operator>=(iterator
const& rOther
) const
183 return *m_pIter
>= *rOther
.m_pIter
;
189 bool lcl_GreaterThan( const SwPosition
& rPos
, const SwNode
& rNdIdx
, std::optional
<sal_Int32
> oContentIdx
)
191 return oContentIdx
.has_value()
192 ? ( rPos
.GetNode() > rNdIdx
193 || ( rPos
.GetNode() == rNdIdx
194 && rPos
.GetContentIndex() >= *oContentIdx
) )
195 : rPos
.GetNode() >= rNdIdx
;
198 bool lcl_Lower( const SwPosition
& rPos
, const SwNode
& rNdIdx
, std::optional
<sal_Int32
> oContentIdx
)
200 if (rPos
.GetNode() < rNdIdx
)
203 if (rPos
.GetNode() != rNdIdx
|| !oContentIdx
)
206 if (rPos
.GetContentIndex() < *oContentIdx
)
209 // paragraph end selected?
210 return rNdIdx
.IsTextNode() && *oContentIdx
== rNdIdx
.GetTextNode()->Len();
213 bool lcl_MarkOrderingByStart(const ::sw::mark::MarkBase
*const pFirst
,
214 const ::sw::mark::MarkBase
*const pSecond
)
216 SwPosition
const& rFirstStart(pFirst
->GetMarkStart());
217 SwPosition
const& rSecondStart(pSecond
->GetMarkStart());
218 if (rFirstStart
.GetNode() != rSecondStart
.GetNode())
220 return rFirstStart
.GetNode() < rSecondStart
.GetNode();
222 const sal_Int32 nFirstContent
= rFirstStart
.GetContentIndex();
223 const sal_Int32 nSecondContent
= rSecondStart
.GetContentIndex();
224 if (nFirstContent
!= 0 || nSecondContent
!= 0)
226 return nFirstContent
< nSecondContent
;
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;
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))
238 return false; // equal
240 return pCRFirst
!= nullptr; // cross-ref sorts *before*
243 bool lcl_MarkOrderingByEnd(const ::sw::mark::MarkBase
*const pFirst
,
244 const ::sw::mark::MarkBase
*const pSecond
)
246 return pFirst
->GetMarkEnd() < pSecond
->GetMarkEnd();
249 void lcl_InsertMarkSorted(MarkManager::container_t
& io_vMarks
,
250 ::sw::mark::MarkBase
*const pMark
)
257 &lcl_MarkOrderingByStart
),
261 void lcl_PositionFromContentNode(
262 std::optional
<SwPosition
>& rFoundPos
,
263 const SwContentNode
* const pContentNode
,
266 rFoundPos
.emplace(*pContentNode
, bAtEnd
? pContentNode
->Len() : 0);
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
,
277 const SwPosition
& rOtherPosition
)
279 const SwContentNode
* pNode
= rEnd
.GetContentNode();
280 bool bPosAtEndOfNode
= false;
281 if ( pNode
== nullptr)
283 SwNodeIndex
aEnd(rEnd
);
284 pNode
= rEnd
.GetNodes().GoNext( &aEnd
);
285 bPosAtEndOfNode
= false;
287 if ( pNode
== nullptr )
289 SwNodeIndex
aStt(rStt
);
290 pNode
= SwNodes::GoPrevious(&aStt
);
291 bPosAtEndOfNode
= true;
293 if ( pNode
!= nullptr )
295 lcl_PositionFromContentNode( rFoundPos
, pNode
, bPosAtEndOfNode
);
299 rFoundPos
= rOtherPosition
;
302 struct CompareIMarkStartsBefore
304 bool operator()(SwPosition
const& rPos
,
305 const sw::mark::IMark
* pMark
)
307 return rPos
< pMark
->GetMarkStart();
309 bool operator()(const sw::mark::IMark
* pMark
,
310 SwPosition
const& rPos
)
312 return pMark
->GetMarkStart() < rPos
;
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
320 bool operator()(SwPosition
const& rPos
,
321 const sw::mark::IMark
* pMark
)
323 return pMark
->GetMarkStart() > rPos
;
328 IMark
* lcl_getMarkAfter(const MarkManager::container_t
& rMarks
, const SwPosition
& rPos
,
331 auto const pMarkAfter
= upper_bound(
335 CompareIMarkStartsAfter());
336 if(pMarkAfter
== rMarks
.end())
338 if (bLoop
&& rMarks
.begin() != rMarks
.end())
339 return *rMarks
.begin();
346 IMark
* lcl_getMarkBefore(const MarkManager::container_t
& rMarks
, const SwPosition
& rPos
,
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(
356 CompareIMarkStartsAfter());
357 vCandidates
.reserve(pCandidatesEnd
- rMarks
.begin());
358 // only marks ending before are candidates
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())
367 if (bLoop
&& rMarks
.begin() != rMarks
.end())
368 return *(rMarks
.end() - 1);
372 // return the highest (last) candidate using mark end ordering
373 return *max_element(vCandidates
.begin(), vCandidates
.end(), &lcl_MarkOrderingByEnd
);
376 bool lcl_FixCorrectedMark(
377 const bool bChangedPos
,
378 const bool bChangedOPos
,
381 if ( IDocumentMarkAccess::GetType(*io_pMark
) == IDocumentMarkAccess::MarkType::ANNOTATIONMARK
)
383 // annotation marks are allowed to span a table cell range.
384 // but trigger sorting to be save
388 if ( ( bChangedPos
|| bChangedOPos
)
389 && io_pMark
->IsExpanded()
390 && io_pMark
->GetOtherMarkPos().GetNode().FindTableBoxStartNode() !=
391 io_pMark
->GetMarkPos().GetNode().FindTableBoxStartNode() )
395 io_pMark
->SetMarkPos( io_pMark
->GetOtherMarkPos() );
397 io_pMark
->ClearOtherMarkPos();
398 DdeBookmark
* const pDdeBkmk
= dynamic_cast< DdeBookmark
*>(io_pMark
);
399 if ( pDdeBkmk
!= nullptr
400 && pDdeBkmk
->IsServer() )
402 pDdeBkmk
->SetRefObject(nullptr);
409 bool lcl_MarkEqualByStart(const ::sw::mark::MarkBase
*const pFirst
,
410 const ::sw::mark::MarkBase
*const pSecond
)
412 return !lcl_MarkOrderingByStart(pFirst
, pSecond
) &&
413 !lcl_MarkOrderingByStart(pSecond
, pFirst
);
416 MarkManager::container_t::const_iterator
lcl_FindMark(
417 MarkManager::container_t
& rMarks
,
418 const ::sw::mark::MarkBase
*const pMarkToFind
)
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
))
428 if(*ppCurrentMark
== pMarkToFind
)
430 return MarkManager::container_t::const_iterator(std::move(ppCurrentMark
));
434 // reached a mark starting on a later start pos or the end of the
435 // vector => not found
439 MarkManager::container_t::const_iterator
lcl_FindMarkAtPos(
440 MarkManager::container_t
& rMarks
,
441 const SwPosition
& rPos
,
442 const IDocumentMarkAccess::MarkType eType
)
444 for (auto ppCurrentMark
= lower_bound(
445 rMarks
.begin(), rMarks
.end(),
447 CompareIMarkStartsBefore());
448 ppCurrentMark
!= rMarks
.end();
451 // Once we reach a mark starting after the target pos
452 // we do not need to continue
453 if((*ppCurrentMark
)->GetMarkStart() > rPos
)
455 if(IDocumentMarkAccess::GetType(**ppCurrentMark
) == eType
)
457 return MarkManager::container_t::const_iterator(std::move(ppCurrentMark
));
460 // reached a mark starting on a later start pos or the end of the
461 // vector => not found
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
)
473 [&rName
] (::sw::mark::MarkBase
const*const pMark
) { return pMark
->GetName() == rName
; } );
476 void lcl_DebugMarks(MarkManager::container_t
const& rMarks
)
478 #if OSL_DEBUG_LEVEL > 0
479 SAL_INFO("sw.core", rMarks
.size() << " Marks");
480 for (auto ppMark
= rMarks
.begin();
481 ppMark
!= rMarks
.end();
484 IMark
* pMark
= *ppMark
;
485 const SwPosition
* const pStPos
= &pMark
->GetMarkStart();
486 const SwPosition
* const pEndPos
= &pMark
->GetMarkEnd();
488 sal_Int32(pStPos
->GetNodeIndex()) << "," <<
489 pStPos
->GetContentIndex() << " " <<
490 sal_Int32(pEndPos
->GetNodeIndex()) << "," <<
491 pEndPos
->GetContentIndex() << " " <<
492 typeid(*pMark
).name() << " " <<
498 assert(std::is_sorted(rMarks
.begin(), rMarks
.end(), lcl_MarkOrderingByStart
));
502 IDocumentMarkAccess::MarkType
IDocumentMarkAccess::GetType(const IMark
& rBkmk
)
504 const std::type_info
* const pMarkTypeInfo
= &typeid(rBkmk
);
505 // not using dynamic_cast<> here for performance
506 if(*pMarkTypeInfo
== typeid(UnoMark
))
507 return MarkType::UNO_BOOKMARK
;
508 else if(*pMarkTypeInfo
== typeid(DdeBookmark
))
509 return MarkType::DDE_BOOKMARK
;
510 else if(*pMarkTypeInfo
== typeid(Bookmark
))
511 return MarkType::BOOKMARK
;
512 else if(*pMarkTypeInfo
== typeid(CrossRefHeadingBookmark
))
513 return MarkType::CROSSREF_HEADING_BOOKMARK
;
514 else if(*pMarkTypeInfo
== typeid(CrossRefNumItemBookmark
))
515 return MarkType::CROSSREF_NUMITEM_BOOKMARK
;
516 else if(*pMarkTypeInfo
== typeid(AnnotationMark
))
517 return MarkType::ANNOTATIONMARK
;
518 else if(*pMarkTypeInfo
== typeid(TextFieldmark
))
519 return MarkType::TEXT_FIELDMARK
;
520 else if(*pMarkTypeInfo
== typeid(CheckboxFieldmark
))
521 return MarkType::CHECKBOX_FIELDMARK
;
522 else if(*pMarkTypeInfo
== typeid(DropDownFieldmark
))
523 return MarkType::DROPDOWN_FIELDMARK
;
524 else if(*pMarkTypeInfo
== typeid(DateFieldmark
))
525 return MarkType::DATE_FIELDMARK
;
526 else if(*pMarkTypeInfo
== typeid(NavigatorReminder
))
527 return MarkType::NAVIGATOR_REMINDER
;
530 assert(false && "IDocumentMarkAccess::GetType(..)"
531 " - unknown MarkType. This needs to be fixed!");
532 return MarkType::UNO_BOOKMARK
;
536 OUString
IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()
538 return "__RefHeading__";
541 bool IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( const SwPaM
& rPaM
)
543 return rPaM
.Start()->GetNode().IsTextNode() &&
544 rPaM
.Start()->GetContentIndex() == 0 &&
546 ( rPaM
.GetMark()->GetNode() == rPaM
.GetPoint()->GetNode() &&
547 rPaM
.End()->GetContentIndex() == rPaM
.End()->GetNode().GetTextNode()->Len() ) );
550 void IDocumentMarkAccess::DeleteFieldmarkCommand(::sw::mark::IFieldmark
const& rMark
)
552 if (GetType(rMark
) != MarkType::TEXT_FIELDMARK
)
554 return; // TODO FORMDATE has no command?
556 SwPaM
pam(sw::mark::FindFieldSep(rMark
), rMark
.GetMarkStart());
557 pam
.GetPoint()->AdjustContent(+1); // skip CH_TXT_ATR_FIELDSTART
558 pam
.GetDoc().getIDocumentContentOperations().DeleteAndJoin(pam
);
563 MarkManager::MarkManager(SwDoc
& rDoc
)
565 , m_pLastActiveFieldmark(nullptr)
568 ::sw::mark::IMark
* MarkManager::makeMark(const SwPaM
& rPaM
,
569 const OUString
& rName
,
570 const IDocumentMarkAccess::MarkType eType
,
571 sw::mark::InsertMode
const eMode
,
572 SwPosition
const*const pSepPos
)
574 #if OSL_DEBUG_LEVEL > 0
576 const SwPosition
* const pPos1
= rPaM
.GetPoint();
577 const SwPosition
* pPos2
= pPos1
;
579 pPos2
= rPaM
.GetMark();
582 sal_Int32(pPos1
->GetNodeIndex() )<< "," <<
583 pPos1
->GetContentIndex() << " " <<
584 sal_Int32(pPos2
->GetNodeIndex()) << "," <<
585 pPos2
->GetContentIndex());
588 if ( (!rPaM
.GetPoint()->GetNode().IsTextNode()
589 && (eType
!= MarkType::UNO_BOOKMARK
590 // SwXTextRange can be on table node or plain start node (FLY_AT_FLY)
591 || !rPaM
.GetPoint()->GetNode().IsStartNode()))
592 || (!rPaM
.GetMark()->GetNode().IsTextNode()
593 && (eType
!= MarkType::UNO_BOOKMARK
594 || !rPaM
.GetMark()->GetNode().IsStartNode())))
596 SAL_WARN("sw.core", "MarkManager::makeMark(..)"
597 " - refusing to create mark on non-textnode");
600 // There should only be one CrossRefBookmark per Textnode per Type
601 if ((eType
== MarkType::CROSSREF_NUMITEM_BOOKMARK
|| eType
== MarkType::CROSSREF_HEADING_BOOKMARK
)
602 && (lcl_FindMarkAtPos(m_vBookmarks
, *rPaM
.Start(), eType
) != m_vBookmarks
.end()))
603 { // this can happen via UNO API
604 SAL_WARN("sw.core", "MarkManager::makeMark(..)"
605 " - refusing to create duplicate CrossRefBookmark");
609 if ((eType
== MarkType::CHECKBOX_FIELDMARK
|| eType
== MarkType::DROPDOWN_FIELDMARK
)
610 && (eMode
== InsertMode::New
611 ? *rPaM
.GetPoint() != *rPaM
.GetMark()
612 // CopyText: pam covers CH_TXT_ATR_FORMELEMENT
613 : (rPaM
.GetPoint()->GetNode() != rPaM
.GetMark()->GetNode()
614 || rPaM
.Start()->GetContentIndex() + 1 != rPaM
.End()->GetContentIndex())))
616 SAL_WARN("sw.core", "MarkManager::makeMark(..)"
617 " - invalid range on point fieldmark");
621 if ((eType
== MarkType::TEXT_FIELDMARK
|| eType
== MarkType::DATE_FIELDMARK
)
622 && (rPaM
.GetPoint()->GetNode().StartOfSectionNode() != rPaM
.GetMark()->GetNode().StartOfSectionNode()
623 || (pSepPos
&& rPaM
.GetPoint()->GetNode().StartOfSectionNode() != pSepPos
->GetNode().StartOfSectionNode())))
625 SAL_WARN("sw.core", "MarkManager::makeMark(..)"
626 " - invalid range on fieldmark, different nodes array sections");
630 if ((eType
== MarkType::TEXT_FIELDMARK
|| eType
== MarkType::DATE_FIELDMARK
)
631 // can't check for Copy - it asserts - but it's also obviously unnecessary
632 && eMode
== InsertMode::New
633 && sw::mark::IsFieldmarkOverlap(rPaM
))
635 SAL_WARN("sw.core", "MarkManager::makeMark(..)"
636 " - invalid range on fieldmark, overlaps existing fieldmark or meta-field");
641 std::unique_ptr
<::sw::mark::MarkBase
> pMark
;
644 case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
:
645 pMark
= std::make_unique
<TextFieldmark
>(rPaM
, rName
);
647 case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
:
648 pMark
= std::make_unique
<CheckboxFieldmark
>(rPaM
, rName
);
650 case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
:
651 pMark
= std::make_unique
<DropDownFieldmark
>(rPaM
, rName
);
653 case IDocumentMarkAccess::MarkType::DATE_FIELDMARK
:
654 pMark
= std::make_unique
<DateFieldmark
>(rPaM
);
656 case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER
:
657 pMark
= std::make_unique
<NavigatorReminder
>(rPaM
);
659 case IDocumentMarkAccess::MarkType::BOOKMARK
:
660 pMark
= std::make_unique
<Bookmark
>(rPaM
, vcl::KeyCode(), rName
);
662 case IDocumentMarkAccess::MarkType::DDE_BOOKMARK
:
663 pMark
= std::make_unique
<DdeBookmark
>(rPaM
);
665 case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
:
666 pMark
= std::make_unique
<CrossRefHeadingBookmark
>(rPaM
, vcl::KeyCode(), rName
);
668 case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
:
669 pMark
= std::make_unique
<CrossRefNumItemBookmark
>(rPaM
, vcl::KeyCode(), rName
);
671 case IDocumentMarkAccess::MarkType::UNO_BOOKMARK
:
672 pMark
= std::make_unique
<UnoMark
>(rPaM
);
674 case IDocumentMarkAccess::MarkType::ANNOTATIONMARK
:
675 pMark
= std::make_unique
<AnnotationMark
>( rPaM
, rName
);
678 assert(pMark
&& "MarkManager::makeMark(..) - Mark was not created.");
680 if(pMark
->GetMarkPos() != pMark
->GetMarkStart())
683 // for performance reasons, we trust UnoMarks to have a (generated) unique name
684 if ( eType
!= IDocumentMarkAccess::MarkType::UNO_BOOKMARK
)
685 pMark
->SetName( getUniqueMarkName( pMark
->GetName() ) );
687 // insert any dummy chars before inserting into sorted vectors
688 pMark
->InitDoc(m_rDoc
, eMode
, pSepPos
);
691 lcl_InsertMarkSorted(m_vAllMarks
, pMark
.get());
694 case IDocumentMarkAccess::MarkType::BOOKMARK
:
695 case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
:
696 case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
:
697 lcl_InsertMarkSorted(m_vBookmarks
, pMark
.get());
699 case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
:
700 case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
:
701 case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
:
702 case IDocumentMarkAccess::MarkType::DATE_FIELDMARK
:
703 lcl_InsertMarkSorted(m_vFieldmarks
, pMark
.get());
705 case IDocumentMarkAccess::MarkType::ANNOTATIONMARK
:
706 lcl_InsertMarkSorted( m_vAnnotationMarks
, pMark
.get() );
708 case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER
:
709 case IDocumentMarkAccess::MarkType::DDE_BOOKMARK
:
710 case IDocumentMarkAccess::MarkType::UNO_BOOKMARK
:
711 // no special array for these
714 if (eMode
== InsertMode::New
715 && (eType
== IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
716 || eType
== IDocumentMarkAccess::MarkType::DATE_FIELDMARK
))
718 // due to sw::InsertText notifications everything is visible now - tell
719 // layout to hide as appropriate
720 // note: we don't know how many layouts there are and which
721 // parts they hide, so just notify the entire fieldmark, it
722 // should give the right result if not in the most efficient way
723 // note2: can't be done in InitDoc() because it requires the mark
724 // to be inserted in the vectors.
725 SwPaM
const tmp(pMark
->GetMarkPos(), pMark
->GetOtherMarkPos());
726 sw::UpdateFramesForAddDeleteRedline(m_rDoc
, tmp
);
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
);
737 return pMark
.release();
740 ::sw::mark::IFieldmark
* MarkManager::makeFieldBookmark(
742 const OUString
& rName
,
743 const OUString
& rType
,
744 SwPosition
const*const pSepPos
)
747 // Disable undo, because we handle it using SwUndoInsTextFieldmark
748 bool bUndoIsEnabled
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
749 m_rDoc
.GetIDocumentUndoRedo().DoUndo(false);
751 sw::mark::IMark
* pMark
= nullptr;
752 if(rType
== ODF_FORMDATE
)
754 pMark
= makeMark(rPaM
, rName
,
755 IDocumentMarkAccess::MarkType::DATE_FIELDMARK
,
756 sw::mark::InsertMode::New
,
761 pMark
= makeMark(rPaM
, rName
,
762 IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
,
763 sw::mark::InsertMode::New
,
766 sw::mark::IFieldmark
* pFieldMark
= dynamic_cast<sw::mark::IFieldmark
*>( pMark
);
768 pFieldMark
->SetFieldname( rType
);
772 m_rDoc
.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled
);
774 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoInsTextFieldmark
>(*pFieldMark
));
780 ::sw::mark::IFieldmark
* MarkManager::makeNoTextFieldBookmark(
782 const OUString
& rName
,
783 const OUString
& rType
)
785 // Disable undo, because we handle it using SwUndoInsNoTextFieldmark
786 bool bUndoIsEnabled
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
787 m_rDoc
.GetIDocumentUndoRedo().DoUndo(false);
789 bool bEnableSetModified
= m_rDoc
.getIDocumentState().IsEnableSetModified();
790 m_rDoc
.getIDocumentState().SetEnableSetModified(false);
792 sw::mark::IMark
* pMark
= nullptr;
793 if(rType
== ODF_FORMCHECKBOX
)
795 pMark
= makeMark( rPaM
, rName
,
796 IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
,
797 sw::mark::InsertMode::New
);
799 else if(rType
== ODF_FORMDROPDOWN
)
801 pMark
= makeMark( rPaM
, rName
,
802 IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
,
803 sw::mark::InsertMode::New
);
805 else if(rType
== ODF_FORMDATE
)
807 pMark
= makeMark( rPaM
, rName
,
808 IDocumentMarkAccess::MarkType::DATE_FIELDMARK
,
809 sw::mark::InsertMode::New
);
812 sw::mark::IFieldmark
* pFieldMark
= dynamic_cast<sw::mark::IFieldmark
*>( pMark
);
814 pFieldMark
->SetFieldname( rType
);
818 m_rDoc
.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled
);
820 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoInsNoTextFieldmark
>(*pFieldMark
));
823 m_rDoc
.getIDocumentState().SetEnableSetModified(bEnableSetModified
);
824 m_rDoc
.getIDocumentState().SetModified();
829 ::sw::mark::IMark
* MarkManager::getMarkForTextNode(
830 const SwTextNode
& rTextNode
,
831 const IDocumentMarkAccess::MarkType eType
)
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
);
841 sw::mark::IMark
* MarkManager::makeAnnotationMark(
843 const OUString
& rName
)
845 return makeMark(rPaM
, rName
, IDocumentMarkAccess::MarkType::ANNOTATIONMARK
,
846 sw::mark::InsertMode::New
);
849 void MarkManager::repositionMark(
850 ::sw::mark::IMark
* const io_pMark
,
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
);
860 pMarkBase
->InvalidateFrames();
862 pMarkBase
->SetMarkPos(*(rPaM
.GetPoint()));
864 pMarkBase
->SetOtherMarkPos(*(rPaM
.GetMark()));
866 pMarkBase
->ClearOtherMarkPos();
868 if(pMarkBase
->GetMarkPos() != pMarkBase
->GetMarkStart())
871 pMarkBase
->InvalidateFrames();
876 bool MarkManager::renameMark(
877 ::sw::mark::IMark
* io_pMark
,
878 const OUString
& rNewName
)
880 assert(&io_pMark
->GetMarkPos().GetDoc() == &m_rDoc
&&
881 "<MarkManager::renameMark(..)>"
882 " - Mark is not in my doc.");
883 if ( io_pMark
->GetName() == rNewName
)
885 if (lcl_FindMarkByName(rNewName
, m_vAllMarks
.begin(), m_vAllMarks
.end()) != m_vAllMarks
.end())
887 if (::sw::mark::MarkBase
* pMarkBase
= dynamic_cast< ::sw::mark::MarkBase
* >(io_pMark
))
889 const OUString
sOldName(pMarkBase
->GetName());
890 pMarkBase
->SetName(rNewName
);
892 if (dynamic_cast< ::sw::mark::Bookmark
* >(io_pMark
))
894 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
896 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
897 std::make_unique
<SwUndoRenameBookmark
>(sOldName
, rNewName
, m_rDoc
));
899 m_rDoc
.getIDocumentState().SetModified();
905 void MarkManager::correctMarksAbsolute(
906 const SwNode
& rOldNode
,
907 const SwPosition
& rNewPos
,
908 const sal_Int32 nOffset
)
910 const SwNode
* const pOldNode
= &rOldNode
;
911 SwPosition
aNewPos(rNewPos
);
912 aNewPos
.AdjustContent(nOffset
);
913 bool isSortingNeeded
= false;
915 for (auto ppMark
= m_vAllMarks
.begin();
916 ppMark
!= m_vAllMarks
.end();
919 ::sw::mark::MarkBase
*const pMark
= *ppMark
;
920 // correction of non-existent non-MarkBase instances cannot be done
923 bool bChangedPos
= false;
924 if(&pMark
->GetMarkPos().GetNode() == pOldNode
)
926 pMark
->SetMarkPos(aNewPos
);
928 isSortingNeeded
= true;
930 bool bChangedOPos
= false;
931 if (pMark
->IsExpanded() &&
932 &pMark
->GetOtherMarkPos().GetNode() == pOldNode
)
934 // shift the OtherMark to aNewPos
935 pMark
->SetOtherMarkPos(aNewPos
);
937 isSortingNeeded
= true;
939 // illegal selection? collapse the mark and restore sorting later
940 isSortingNeeded
|= lcl_FixCorrectedMark(bChangedPos
, bChangedOPos
, pMark
);
943 // restore sorting if needed
947 SAL_INFO("sw.core", "correctMarksAbsolute");
948 lcl_DebugMarks(m_vAllMarks
);
951 void MarkManager::correctMarksRelative(const SwNode
& rOldNode
, const SwPosition
& rNewPos
, const sal_Int32 nOffset
)
953 const SwNode
* const pOldNode
= &rOldNode
;
954 SwPosition
aNewPos(rNewPos
);
955 aNewPos
.AdjustContent(nOffset
);
956 bool isSortingNeeded
= false;
958 for (auto ppMark
= m_vAllMarks
.begin();
959 ppMark
!= m_vAllMarks
.end();
963 bool bChangedPos
= false, bChangedOPos
= false;
964 ::sw::mark::MarkBase
* const pMark
= *ppMark
;
965 // correction of non-existent non-MarkBase instances cannot be done
967 if(&pMark
->GetMarkPos().GetNode() == pOldNode
)
969 SwPosition
aNewPosRel(aNewPos
);
970 if (dynamic_cast< ::sw::mark::CrossRefBookmark
*>(pMark
))
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...
976 aNewPosRel
.AdjustContent(pMark
->GetMarkPos().GetContentIndex());
977 pMark
->SetMarkPos(aNewPosRel
);
980 if(pMark
->IsExpanded() &&
981 &pMark
->GetOtherMarkPos().GetNode() == pOldNode
)
983 SwPosition
aNewPosRel(aNewPos
);
984 aNewPosRel
.AdjustContent(pMark
->GetOtherMarkPos().GetContentIndex());
985 pMark
->SetOtherMarkPos(aNewPosRel
);
988 // illegal selection? collapse the mark and restore sorting later
989 isSortingNeeded
|= lcl_FixCorrectedMark(bChangedPos
, bChangedOPos
, pMark
);
992 // restore sorting if needed
996 SAL_INFO("sw.core", "correctMarksRelative");
997 lcl_DebugMarks(m_vAllMarks
);
1000 static bool isDeleteMark(
1001 ::sw::mark::MarkBase
const*const pMark
,
1002 bool const isReplace
,
1005 std::optional
<sal_Int32
> oStartContentIdx
,
1006 std::optional
<sal_Int32
> oEndContentIdx
,
1007 bool & rbIsPosInRange
,
1008 bool & rbIsOtherPosInRange
)
1011 // navigator marks should not be moved
1012 // TODO: Check if this might make them invalid
1013 if (IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER
)
1019 rbIsPosInRange
= lcl_GreaterThan(pMark
->GetMarkPos(), rStt
, oStartContentIdx
)
1020 && lcl_Lower(pMark
->GetMarkPos(), rEnd
, oEndContentIdx
);
1021 rbIsOtherPosInRange
= pMark
->IsExpanded()
1022 && lcl_GreaterThan(pMark
->GetOtherMarkPos(), rStt
, oStartContentIdx
)
1023 && lcl_Lower(pMark
->GetOtherMarkPos(), rEnd
, oEndContentIdx
);
1024 // special case: completely in range, touching the end?
1025 if ( oEndContentIdx
.has_value()
1026 && !(isReplace
&& IDocumentMarkAccess::GetType(*pMark
)
1027 == IDocumentMarkAccess::MarkType::BOOKMARK
)
1028 && ( ( rbIsOtherPosInRange
1029 && pMark
->GetMarkPos().GetNode() == rEnd
1030 && pMark
->GetMarkPos().GetContentIndex() == *oEndContentIdx
)
1032 && pMark
->IsExpanded()
1033 && pMark
->GetOtherMarkPos().GetNode() == rEnd
1034 && pMark
->GetOtherMarkPos().GetContentIndex() == *oEndContentIdx
) ) )
1036 rbIsPosInRange
= true;
1037 rbIsOtherPosInRange
= true;
1041 && (rbIsOtherPosInRange
1042 || !pMark
->IsExpanded()))
1044 // completely in range
1046 bool bDeleteMark
= true;
1048 switch ( IDocumentMarkAccess::GetType( *pMark
) )
1050 case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
:
1051 case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
:
1052 // no delete of cross-reference bookmarks, if range is inside one paragraph
1053 bDeleteMark
= &rStt
!= &rEnd
;
1055 case IDocumentMarkAccess::MarkType::UNO_BOOKMARK
:
1056 // no delete of UNO mark, if it is not expanded and only touches the start of the range
1057 bDeleteMark
= rbIsOtherPosInRange
1058 || pMark
->IsExpanded()
1059 || !oStartContentIdx
.has_value()
1060 || pMark
->GetMarkPos().GetNode() != rStt
1061 || pMark
->GetMarkPos().GetContentIndex() != *oStartContentIdx
;
1073 bool MarkManager::isBookmarkDeleted(SwPaM
const& rPaM
, bool const isReplace
) const
1075 SwPosition
const& rStart(*rPaM
.Start());
1076 SwPosition
const& rEnd(*rPaM
.End());
1077 for (auto ppMark
= m_vBookmarks
.begin();
1078 ppMark
!= m_vBookmarks
.end();
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
);
1087 && IDocumentMarkAccess::GetType(**ppMark
) == MarkType::BOOKMARK
)
1095 void MarkManager::deleteMarks(
1098 std::vector
<SaveBookmark
>* pSaveBkmk
,
1099 std::optional
<sal_Int32
> oStartContentIdx
,
1100 std::optional
<sal_Int32
> oEndContentIdx
)
1102 std::vector
<const_iterator_t
> vMarksToDelete
;
1103 bool bIsSortingNeeded
= false;
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;
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();
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
);
1122 && ( bIsOtherPosInRange
1123 || !pMark
->IsExpanded() ) )
1129 pSaveBkmk
->push_back( SaveBookmark( *pMark
, rStt
, oStartContentIdx
) );
1131 vMarksToDelete
.emplace_back(ppMark
);
1135 bMarksSkipDeletion
= true;
1138 else if ( bIsPosInRange
!= bIsOtherPosInRange
)
1140 // the bookmark is partially in the range
1141 // move position of that is in the range out of it
1143 std::optional
< SwPosition
> oNewPos
;
1144 if ( oEndContentIdx
)
1146 oNewPos
.emplace( *rEnd
.GetContentNode(), *oEndContentIdx
);
1150 lcl_FindExpelPosition( oNewPos
, rStt
, rEnd
, bIsPosInRange
? pMark
->GetOtherMarkPos() : pMark
->GetMarkPos() );
1153 bool bMoveMark
= true;
1155 switch ( IDocumentMarkAccess::GetType( *pMark
) )
1157 case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
:
1158 case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
:
1159 // no move of cross-reference bookmarks, if move occurs inside a certain node
1160 bMoveMark
= pMark
->GetMarkPos().GetNode() != oNewPos
->GetNode();
1162 case IDocumentMarkAccess::MarkType::ANNOTATIONMARK
:
1163 // no move of annotation marks, if method is called to collect deleted marks
1164 bMoveMark
= pSaveBkmk
== nullptr;
1173 if ( bIsPosInRange
)
1174 pMark
->SetMarkPos(*oNewPos
);
1176 pMark
->SetOtherMarkPos(*oNewPos
);
1179 // illegal selection? collapse the mark and restore sorting later
1180 bIsSortingNeeded
|= lcl_FixCorrectedMark( bIsPosInRange
, bIsOtherPosInRange
, pMark
);
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());
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
1196 if ( !vMarksToDelete
.empty() && bMarksMoved
)
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();
1208 vDelay
.push_back(deleteMark(*pppMark
, pSaveBkmk
!= nullptr));
1210 } // scope to kill vDelay
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
))
1219 SAL_INFO("sw.core", "deleteMarks");
1220 lcl_DebugMarks(m_vAllMarks
);
1225 struct LazyFieldmarkDeleter
: public IDocumentMarkAccess::ILazyDeleter
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
)
1233 assert(m_pFieldmark
);
1235 virtual ~LazyFieldmarkDeleter() override
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*!
1243 m_pFieldmark
->ReleaseDoc(m_rDoc
);
1248 // Call DeregisterFromDoc() lazily, because it can call selection change listeners, which
1249 // may mutate the marks container
1250 struct LazyDdeBookmarkDeleter
: public IDocumentMarkAccess::ILazyDeleter
1252 std::unique_ptr
<DdeBookmark
> m_pDdeBookmark
;
1254 LazyDdeBookmarkDeleter(DdeBookmark
*const pDdeBookmark
, SwDoc
& rDoc
)
1255 : m_pDdeBookmark(pDdeBookmark
), m_rDoc(rDoc
)
1257 assert(pDdeBookmark
);
1259 virtual ~LazyDdeBookmarkDeleter() override
1261 m_pDdeBookmark
->DeregisterFromDoc(m_rDoc
);
1267 std::unique_ptr
<IDocumentMarkAccess::ILazyDeleter
>
1268 MarkManager::deleteMark(const const_iterator_t
& ppMark
, bool const isMoveNodes
)
1270 std::unique_ptr
<ILazyDeleter
> ret
;
1271 if (ppMark
.get() == m_vAllMarks
.end())
1273 IMark
* pMark
= *ppMark
;
1275 switch(IDocumentMarkAccess::GetType(*pMark
))
1277 case IDocumentMarkAccess::MarkType::BOOKMARK
:
1279 auto const ppBookmark
= lcl_FindMark(m_vBookmarks
, *ppMark
.get());
1280 if ( ppBookmark
!= m_vBookmarks
.end() )
1282 Bookmark
* pBookmark
= dynamic_cast<Bookmark
*>(*ppBookmark
);
1285 pBookmark
->sendLOKDeleteCallback();
1287 m_vBookmarks
.erase(ppBookmark
);
1292 "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
1296 case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
:
1297 case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
:
1299 auto const ppBookmark
= lcl_FindMark(m_vBookmarks
, *ppMark
.get());
1300 if ( ppBookmark
!= m_vBookmarks
.end() )
1302 m_vBookmarks
.erase(ppBookmark
);
1307 "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
1312 case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
:
1313 case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
:
1314 case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
:
1315 case IDocumentMarkAccess::MarkType::DATE_FIELDMARK
:
1317 auto const ppFieldmark
= lcl_FindMark(m_vFieldmarks
, *ppMark
.get());
1318 if ( ppFieldmark
!= m_vFieldmarks
.end() )
1320 if(m_pLastActiveFieldmark
== *ppFieldmark
)
1321 ClearFieldActivation();
1323 m_vFieldmarks
.erase(ppFieldmark
);
1324 ret
.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark
*>(pMark
), m_rDoc
, isMoveNodes
));
1329 "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container.");
1334 case IDocumentMarkAccess::MarkType::ANNOTATIONMARK
:
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
);
1343 case IDocumentMarkAccess::MarkType::DDE_BOOKMARK
:
1344 case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER
:
1345 case IDocumentMarkAccess::MarkType::UNO_BOOKMARK
:
1346 // no special marks container
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
);
1356 ret
.reset(new LazyDdeBookmarkDeleter(pDdeBookmark
, m_rDoc
));
1359 m_vAllMarks
.erase(aI
);
1360 // If we don't have a lazy deleter
1362 // delete after we remove from the list, because the destructor can
1363 // recursively call into this method.
1368 void MarkManager::deleteMark(const IMark
* const pMark
)
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(),
1378 pMark
->GetMarkStart(),
1379 CompareIMarkStartsBefore());
1380 for ( ; it
!= endIt
; ++it
)
1383 deleteMark(iterator(it
), false);
1388 void MarkManager::clearAllMarks()
1390 ClearFieldActivation();
1391 m_vFieldmarks
.clear();
1392 m_vBookmarks
.clear();
1393 m_vAnnotationMarks
.clear();
1394 for (const auto & p
: m_vAllMarks
)
1396 m_vAllMarks
.clear();
1399 IDocumentMarkAccess::const_iterator_t
MarkManager::findMark(const OUString
& rName
) const
1401 auto const ret
= lcl_FindMarkByName(rName
, m_vAllMarks
.begin(), m_vAllMarks
.end());
1402 return IDocumentMarkAccess::iterator(ret
);
1405 IDocumentMarkAccess::const_iterator_t
MarkManager::findBookmark(const OUString
& rName
) const
1407 auto const ret
= lcl_FindMarkByName(rName
, m_vBookmarks
.begin(), m_vBookmarks
.end());
1408 return IDocumentMarkAccess::iterator(ret
);
1411 IDocumentMarkAccess::const_iterator_t
MarkManager::getAllMarksBegin() const
1412 { return m_vAllMarks
.begin(); }
1414 IDocumentMarkAccess::const_iterator_t
MarkManager::getAllMarksEnd() const
1415 { return m_vAllMarks
.end(); }
1417 sal_Int32
MarkManager::getAllMarksCount() const
1418 { return m_vAllMarks
.size(); }
1420 IDocumentMarkAccess::const_iterator_t
MarkManager::getBookmarksBegin() const
1421 { return m_vBookmarks
.begin(); }
1423 IDocumentMarkAccess::const_iterator_t
MarkManager::getBookmarksEnd() const
1424 { return m_vBookmarks
.end(); }
1426 sal_Int32
MarkManager::getBookmarksCount() const
1427 { return m_vBookmarks
.size(); }
1429 IDocumentMarkAccess::const_iterator_t
MarkManager::getFieldmarksBegin() const
1430 { return m_vFieldmarks
.begin(); }
1432 IDocumentMarkAccess::const_iterator_t
MarkManager::getFieldmarksEnd() const
1433 { return m_vFieldmarks
.end(); }
1435 sal_Int32
MarkManager::getFieldmarksCount() const { return m_vFieldmarks
.size(); }
1438 // finds the first that is starting after
1439 IDocumentMarkAccess::const_iterator_t
MarkManager::findFirstBookmarkStartsAfter(const SwPosition
& rPos
) const
1441 return std::upper_bound(
1442 m_vBookmarks
.begin(),
1445 CompareIMarkStartsAfter());
1448 IFieldmark
* MarkManager::getFieldmarkAt(const SwPosition
& rPos
) const
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());
1459 return (pFieldmark
== m_vFieldmarks
.end())
1461 : dynamic_cast<IFieldmark
*>(*pFieldmark
);
1464 IFieldmark
* MarkManager::getInnerFieldmarkFor(const SwPosition
& rPos
) const
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())
1472 auto pFieldmark(*itFieldmark
);
1474 // See if any fieldmarks after the first hit are closer to rPos.
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()))
1483 pFieldmark
= *itFieldmark
;
1486 return dynamic_cast<IFieldmark
*>(pFieldmark
);
1489 IMark
* MarkManager::getOneInnermostBookmarkFor(const SwPosition
& rPos
) const
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())
1498 sw::mark::IMark
* pBookmark
= *it
;
1500 // See if any bookmarks after the first hit are closer to rPos.
1503 for (; it
!= m_vBookmarks
.end() && (*it
)->GetMarkStart() <= rPos
; ++it
)
1505 // Find the innermost bookmark.
1506 if (rPos
< (*it
)->GetMarkEnd()
1507 && (pBookmark
->GetMarkStart() < (*it
)->GetMarkStart()
1508 || (*it
)->GetMarkEnd() < pBookmark
->GetMarkEnd()))
1516 void MarkManager::deleteFieldmarkAt(const SwPosition
& rPos
)
1518 auto const pFieldmark
= dynamic_cast<Fieldmark
*>(getFieldmarkAt(rPos
));
1519 assert(pFieldmark
); // currently all callers require it to be there
1521 deleteMark(lcl_FindMark(m_vAllMarks
, pFieldmark
), false);
1524 ::sw::mark::IFieldmark
* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark
* pFieldmark
, const OUString
& rNewType
)
1526 bool bActualChange
= false;
1527 if(rNewType
== ODF_FORMDROPDOWN
)
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
1534 else if(rNewType
== ODF_FORMCHECKBOX
)
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
1541 else if(rNewType
== ODF_FORMDATE
)
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
1552 // Store attributes needed to create the new fieldmark
1553 OUString sName
= pFieldmark
->GetName();
1554 SwPaM
const aPaM(pFieldmark
->GetMarkStart());
1556 // Remove the old fieldmark and create a new one with the new type
1557 if (rNewType
== ODF_FORMDROPDOWN
|| rNewType
== ODF_FORMCHECKBOX
)
1559 SwPosition
aNewPos (*aPaM
.GetPoint());
1560 deleteFieldmarkAt(aNewPos
);
1561 return makeNoTextFieldBookmark(aPaM
, sName
, rNewType
);
1563 else if(rNewType
== ODF_FORMDATE
)
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
);
1576 void MarkManager::NotifyCursorUpdate(const SwCursorShell
& rCursorShell
)
1578 SwView
* pSwView
= dynamic_cast<SwView
*>(rCursorShell
.GetSfxViewShell());
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 )
1589 aPos
.AdjustContent(-1);
1590 pFieldBM
= getInnerFieldmarkFor(aPos
);
1593 if ( pFieldBM
&& (pFieldBM
->GetFieldname() == ODF_FORMDROPDOWN
||
1594 pFieldBM
->GetFieldname() == ODF_FORMDATE
))
1596 if (m_pLastActiveFieldmark
!= pFieldBM
)
1598 FieldmarkWithDropDownButton
& rFormField
= dynamic_cast<FieldmarkWithDropDownButton
&>(*pFieldBM
);
1599 pNewActiveFieldmark
= &rFormField
;
1603 pNewActiveFieldmark
= m_pLastActiveFieldmark
;
1607 if(pNewActiveFieldmark
!= m_pLastActiveFieldmark
)
1609 ClearFieldActivation();
1610 m_pLastActiveFieldmark
= pNewActiveFieldmark
;
1611 if(pNewActiveFieldmark
)
1612 pNewActiveFieldmark
->ShowButton(&rEditWin
);
1615 LOKUpdateActiveField(pSwView
);
1618 void MarkManager::ClearFieldActivation()
1620 if(m_pLastActiveFieldmark
)
1621 m_pLastActiveFieldmark
->RemoveButton();
1623 m_pLastActiveFieldmark
= nullptr;
1626 void MarkManager::LOKUpdateActiveField(const SfxViewShell
* pViewShell
)
1628 if (!comphelper::LibreOfficeKit::isActive())
1631 if (m_pLastActiveFieldmark
)
1633 if (auto pDrowDown
= m_pLastActiveFieldmark
->GetFieldname() == ODF_FORMDROPDOWN
?
1634 dynamic_cast<::sw::mark::DropDownFieldmark
*>(m_pLastActiveFieldmark
) :
1637 pDrowDown
->SendLOKShowMessage(pViewShell
);
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
)
1646 IFieldmark
*pMark
= dynamic_cast<IFieldmark
*>(*aIter
);
1647 if (pMark
&& pMark
->GetFieldname() == ODF_FORMDROPDOWN
)
1649 bDropDownFieldExist
= true;
1654 if (bDropDownFieldExist
)
1655 ::sw::mark::DropDownFieldmark::SendLOKHideMessage(pViewShell
);
1659 IFieldmark
* MarkManager::getDropDownFor(const SwPosition
& rPos
) const
1661 IFieldmark
*pMark
= getFieldmarkAt(rPos
);
1662 if (!pMark
|| pMark
->GetFieldname() != ODF_FORMDROPDOWN
)
1667 std::vector
<IFieldmark
*> MarkManager::getNoTextFieldmarksIn(const SwPaM
&rPaM
) const
1669 std::vector
<IFieldmark
*> aRet
;
1671 for (auto aI
= m_vFieldmarks
.begin(),
1672 aEnd
= m_vFieldmarks
.end(); aI
!= aEnd
; ++aI
)
1674 ::sw::mark::IMark
* pI
= *aI
;
1675 const SwPosition
&rStart
= pI
->GetMarkPos();
1676 if (!rPaM
.ContainsPosition(rStart
))
1679 IFieldmark
*pMark
= dynamic_cast<IFieldmark
*>(pI
);
1680 if (!pMark
|| (pMark
->GetFieldname() != ODF_FORMDROPDOWN
1681 && pMark
->GetFieldname() != ODF_FORMCHECKBOX
))
1686 aRet
.push_back(pMark
);
1692 IFieldmark
* MarkManager::getFieldmarkAfter(const SwPosition
& rPos
, bool bLoop
) const
1693 { return dynamic_cast<IFieldmark
*>(lcl_getMarkAfter(m_vFieldmarks
, rPos
, bLoop
)); }
1695 IFieldmark
* MarkManager::getFieldmarkBefore(const SwPosition
& rPos
, bool bLoop
) const
1696 { return dynamic_cast<IFieldmark
*>(lcl_getMarkBefore(m_vFieldmarks
, rPos
, bLoop
)); }
1698 IDocumentMarkAccess::const_iterator_t
MarkManager::getAnnotationMarksBegin() const
1700 return m_vAnnotationMarks
.begin();
1703 IDocumentMarkAccess::const_iterator_t
MarkManager::getAnnotationMarksEnd() const
1705 return m_vAnnotationMarks
.end();
1708 sal_Int32
MarkManager::getAnnotationMarksCount() const
1710 return m_vAnnotationMarks
.size();
1713 IDocumentMarkAccess::const_iterator_t
MarkManager::findAnnotationMark( const OUString
& rName
) const
1715 auto const ret
= lcl_FindMarkByName( rName
, m_vAnnotationMarks
.begin(), m_vAnnotationMarks
.end() );
1716 return IDocumentMarkAccess::iterator(ret
);
1719 IMark
* MarkManager::getAnnotationMarkFor(const SwPosition
& rPos
) const
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())
1727 return *pAnnotationMark
;
1730 // finds the first that is starting after
1731 IDocumentMarkAccess::const_iterator_t
MarkManager::findFirstAnnotationStartsAfter(const SwPosition
& rPos
) const
1733 return std::upper_bound(
1734 m_vAnnotationMarks
.begin(),
1735 m_vAnnotationMarks
.end(),
1737 CompareIMarkStartsAfter());
1740 // create helper bookmark for annotations on tracked deletions
1741 ::sw::mark::IMark
* MarkManager::makeAnnotationBookmark(const SwPaM
& rPaM
,
1742 const OUString
& rName
,
1743 const IDocumentMarkAccess::MarkType eType
,
1744 sw::mark::InsertMode
const eMode
,
1745 SwPosition
const*const pSepPos
)
1747 OUString
sAnnotationBookmarkName(rName
+ S_ANNOTATION_BOOKMARK
);
1748 return makeMark( rPaM
, sAnnotationBookmarkName
, eType
, eMode
, pSepPos
);
1751 // find helper bookmark of annotations on tracked deletions
1752 IDocumentMarkAccess::const_iterator_t
MarkManager::findAnnotationBookmark(const OUString
& rName
) const
1754 OUString
sAnnotationBookmarkName(rName
+ S_ANNOTATION_BOOKMARK
);
1755 return findBookmark(sAnnotationBookmarkName
);
1758 // restore text ranges of annotations on tracked deletions
1759 // based on the helper bookmarks (which can survive I/O and hiding redlines)
1760 void MarkManager::restoreAnnotationMarks(bool bDelete
)
1762 for (auto iter
= getBookmarksBegin();
1763 iter
!= getBookmarksEnd(); )
1765 const OUString
& rBookmarkName
= (**iter
).GetName();
1767 if ( rBookmarkName
.startsWith("__Annotation__") &&
1768 (nPos
= rBookmarkName
.indexOf(S_ANNOTATION_BOOKMARK
)) > -1 )
1770 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
1771 IDocumentMarkAccess::const_iterator_t pMark
= findAnnotationMark(rBookmarkName
.copy(0, nPos
));
1772 if ( pMark
!= getAnnotationMarksEnd() )
1774 const SwPaM
aPam((**iter
).GetMarkStart(), (**pMark
).GetMarkEnd());
1775 repositionMark(*pMark
, aPam
);
1779 deleteMark(&**iter
);
1780 // this invalidates iter, have to start over...
1781 iter
= getBookmarksBegin();
1791 OUString
MarkManager::getUniqueMarkName(const OUString
& rName
) const
1793 OSL_ENSURE(rName
.getLength(),
1794 "<MarkManager::getUniqueMarkName(..)> - a name should be proposed");
1795 if( m_rDoc
.IsInMailMerge())
1797 OUString newName
= rName
+ "MailMergeMark"
1798 + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM
)), RTL_TEXTENCODING_ASCII_US
)
1799 + OUString::number( m_vAllMarks
.size() + 1 );
1803 if (lcl_FindMarkByName(rName
, m_vAllMarks
.begin(), m_vAllMarks
.end()) == m_vAllMarks
.end())
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).
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
)
1819 sTmp
= aPrefix
+ OUString::number(nCnt
);
1821 if (lcl_FindMarkByName(sTmp
, m_vAllMarks
.begin(), m_vAllMarks
.end()) == m_vAllMarks
.end())
1826 m_aMarkBasenameMapUniqueOffset
[rName
] = nCnt
;
1831 void MarkManager::assureSortedMarkContainers() const
1833 const_cast< MarkManager
* >(this)->sortMarks();
1836 void MarkManager::sortSubsetMarks()
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
);
1843 void MarkManager::sortMarks()
1845 sort(m_vAllMarks
.begin(), m_vAllMarks
.end(), &lcl_MarkOrderingByStart
);
1849 void MarkManager::dumpAsXml(xmlTextWriterPtr pWriter
) const
1854 const container_t
* pContainer
;
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
}
1864 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("MarkManager"));
1865 for (const auto & rContainer
: aContainers
)
1867 if (!rContainer
.pContainer
->empty())
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
);
1875 (void)xmlTextWriterEndElement(pWriter
);
1878 } // namespace ::sw::mark
1882 bool lcl_Greater( const SwPosition
& rPos
, const SwNode
& rNdIdx
, std::optional
<sal_Int32
> oContentIdx
)
1884 return rPos
.GetNode() > rNdIdx
||
1885 ( oContentIdx
&& rPos
.GetNode() == rNdIdx
&& rPos
.GetContentIndex() > *oContentIdx
);
1889 // IDocumentMarkAccess for SwDoc
1890 IDocumentMarkAccess
* SwDoc::getIDocumentMarkAccess()
1891 { return static_cast< IDocumentMarkAccess
* >(mpMarkManager
.get()); }
1893 const IDocumentMarkAccess
* SwDoc::getIDocumentMarkAccess() const
1894 { return static_cast< IDocumentMarkAccess
* >(mpMarkManager
.get()); }
1896 SaveBookmark::SaveBookmark(
1898 const SwNode
& rMvPos
,
1899 std::optional
<sal_Int32
> oContentIdx
)
1900 : m_aName(rBkmk
.GetName())
1902 , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk
))
1904 const IBookmark
* const pBookmark
= dynamic_cast< const IBookmark
* >(&rBkmk
);
1907 m_aShortName
= pBookmark
->GetShortName();
1908 m_aCode
= pBookmark
->GetKeyCode();
1909 m_bHidden
= pBookmark
->IsHidden();
1910 m_aHideCondition
= pBookmark
->GetHideCondition();
1912 ::sfx2::Metadatable
const*const pMetadatable(
1913 dynamic_cast< ::sfx2::Metadatable
const* >(pBookmark
));
1916 m_pMetadataUndo
= pMetadatable
->CreateUndo();
1919 m_nNode1
= rBkmk
.GetMarkPos().GetNodeIndex();
1920 m_nContent1
= rBkmk
.GetMarkPos().GetContentIndex();
1922 m_nNode1
-= rMvPos
.GetIndex();
1923 if(oContentIdx
&& !m_nNode1
)
1924 m_nContent1
-= *oContentIdx
;
1926 if(rBkmk
.IsExpanded())
1928 m_nNode2
= rBkmk
.GetOtherMarkPos().GetNodeIndex();
1929 m_nContent2
= rBkmk
.GetOtherMarkPos().GetContentIndex();
1931 m_nNode2
-= rMvPos
.GetIndex();
1932 if(oContentIdx
&& !m_nNode2
)
1933 m_nContent2
-= *oContentIdx
;
1937 m_nNode2
= NODE_OFFSET_MAX
;
1942 void SaveBookmark::SetInDoc(
1944 const SwNode
& rNewPos
,
1945 std::optional
<sal_Int32
> oContentIdx
)
1947 SwPaM
aPam(rNewPos
);
1950 if (aPam
.GetPoint()->GetNode().IsContentNode())
1951 aPam
.GetPoint()->SetContent( *oContentIdx
);
1953 SAL_WARN("sw", "trying to sent content index, but point node is not a content node");
1956 if(NODE_OFFSET_MAX
!= m_nNode2
)
1960 aPam
.GetMark()->Adjust(m_nNode2
);
1961 if (aPam
.GetMark()->GetNode().IsContentNode())
1963 if(oContentIdx
&& !m_nNode2
)
1964 aPam
.GetMark()->SetContent(*oContentIdx
+ m_nContent2
);
1966 aPam
.GetMark()->SetContent(m_nContent2
);
1969 SAL_WARN("sw", "trying to sent content index, but mark node is not a content node");
1972 aPam
.GetPoint()->Adjust(m_nNode1
);
1974 if (aPam
.GetPoint()->GetNode().IsContentNode())
1976 if(oContentIdx
&& !m_nNode1
)
1977 aPam
.GetPoint()->SetContent(*oContentIdx
+ m_nContent1
);
1979 aPam
.GetPoint()->SetContent(m_nContent1
);
1983 && !CheckNodesRange(aPam
.GetPoint()->GetNode(), aPam
.GetMark()->GetNode(), true))
1986 ::sw::mark::IBookmark
* const pBookmark
= dynamic_cast<::sw::mark::IBookmark
*>(
1987 pDoc
->getIDocumentMarkAccess()->makeMark(aPam
, m_aName
,
1988 m_eOrigBkmType
, sw::mark::InsertMode::CopyText
));
1992 pBookmark
->SetKeyCode(m_aCode
);
1993 pBookmark
->SetShortName(m_aShortName
);
1994 pBookmark
->Hide(m_bHidden
);
1995 pBookmark
->SetHideCondition(m_aHideCondition
);
1997 if (m_pMetadataUndo
)
1999 ::sfx2::Metadatable
* const pMeta(
2000 dynamic_cast< ::sfx2::Metadatable
* >(pBookmark
));
2001 assert(pMeta
&& "metadata undo, but not metadatable?");
2004 pMeta
->RestoreMetadata(m_pMetadataUndo
);
2014 std::vector
<SaveBookmark
> * pSaveBkmk
,
2015 std::optional
<sal_Int32
> oStartContentIdx
,
2016 std::optional
<sal_Int32
> oEndContentIdx
)
2019 if(rStt
.GetIndex() > rEnd
.GetIndex()
2020 || (&rStt
== &rEnd
&& (!oStartContentIdx
|| !oEndContentIdx
|| *oStartContentIdx
>= *oEndContentIdx
)))
2022 SwDoc
& rDoc
= rStt
.GetDoc();
2024 rDoc
.getIDocumentMarkAccess()->deleteMarks(rStt
, rEnd
, pSaveBkmk
,
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.
2031 SwRedlineTable
& rTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
2032 for(SwRangeRedline
* pRedl
: rTable
)
2035 auto [pRStt
, pREnd
] = pRedl
->StartEnd();
2037 if( lcl_Greater( *pRStt
, rStt
, oStartContentIdx
) && lcl_Lower( *pRStt
, rEnd
, oEndContentIdx
))
2039 pRStt
->Assign( rEnd
);
2040 if( oEndContentIdx
)
2041 pRStt
->SetContent( *oEndContentIdx
);
2045 SwContentNode
* pCNd
= pRStt
->GetNode().GetContentNode();
2047 pCNd
= rDoc
.GetNodes().GoNext( pRStt
);
2051 pRStt
->Assign(rStt
);
2052 pCNd
= SwNodes::GoPrevious( pRStt
);
2056 pCNd
= pRStt
->GetNode().GetContentNode();
2060 pRStt
->AssignEndIndex( *pCNd
);
2063 if( lcl_Greater( *pREnd
, rStt
, oStartContentIdx
) && lcl_Lower( *pREnd
, rEnd
, oEndContentIdx
))
2065 pREnd
->Assign( rStt
);
2066 if (oStartContentIdx
&& rStt
.IsContentNode())
2067 pREnd
->SetContent( *oStartContentIdx
);
2071 SwContentNode
* pCNd
= pREnd
->GetNode().GetContentNode();
2073 pCNd
= SwNodes::GoPrevious( pREnd
);
2077 pREnd
->Assign(rEnd
);
2078 pCNd
= rDoc
.GetNodes().GoNext( pREnd
);
2082 pCNd
= pREnd
->GetNode().GetContentNode();
2086 pREnd
->AssignEndIndex( *pCNd
);
2094 InsertText
MakeInsertText(SwTextNode
& rNode
, const sal_Int32 nPos
, const sal_Int32 nLen
)
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()))
2101 if (sw::mark::FindFieldSep(*pMark
) < *cursor
.GetPoint())
2103 isInsideFieldmarkResult
= true;
2107 isInsideFieldmarkCommand
= true;
2109 *cursor
.GetPoint() = pMark
->GetMarkStart();
2110 if (!cursor
.Left(1))
2115 return InsertText(nPos
, nLen
, isInsideFieldmarkCommand
, isInsideFieldmarkResult
);
2120 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */