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