sc: factor out some more code
[LibreOffice.git] / sw / source / core / doc / DocumentRedlineManager.cxx
blob653ff6175bf60085dc87830a1d494b0ba09bd5de
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
19 #include <DocumentRedlineManager.hxx>
20 #include <frmfmt.hxx>
21 #include <rootfrm.hxx>
22 #include <txtfrm.hxx>
23 #include <txtfld.hxx>
24 #include <doc.hxx>
25 #include <docsh.hxx>
26 #include <wrtsh.hxx>
27 #include <fmtfld.hxx>
28 #include <frmtool.hxx>
29 #include <IDocumentUndoRedo.hxx>
30 #include <IDocumentFieldsAccess.hxx>
31 #include <IDocumentLayoutAccess.hxx>
32 #include <IDocumentState.hxx>
33 #include <redline.hxx>
34 #include <UndoRedline.hxx>
35 #include <docary.hxx>
36 #include <ndtxt.hxx>
37 #include <unocrsr.hxx>
38 #include <ftnidx.hxx>
39 #include <authfld.hxx>
40 #include <strings.hrc>
41 #include <swmodule.hxx>
42 #include <osl/diagnose.h>
43 #include <editeng/prntitem.hxx>
44 #include <comphelper/lok.hxx>
45 #include <svl/itemiter.hxx>
47 using namespace com::sun::star;
49 #ifdef DBG_UTIL
51 #define ERROR_PREFIX "redline table corrupted: "
53 namespace
55 // helper function for lcl_CheckRedline
56 // 1. make sure that pPos->nContent points into pPos->nNode
57 // 2. check that position is valid and doesn't point after text
58 void lcl_CheckPosition( const SwPosition* pPos )
60 assert(dynamic_cast<SwContentIndexReg*>(&pPos->GetNode())
61 == pPos->GetContentNode());
63 SwTextNode* pTextNode = pPos->GetNode().GetTextNode();
64 if( pTextNode == nullptr )
66 assert(pPos->GetContentIndex() == 0);
68 else
70 assert(pPos->GetContentIndex() >= 0 && pPos->GetContentIndex() <= pTextNode->Len());
74 void lcl_CheckPam( const SwPaM* pPam )
76 assert(pPam);
77 lcl_CheckPosition( pPam->GetPoint() );
78 lcl_CheckPosition( pPam->GetMark() );
81 // check validity of the redline table. Checks redline bounds, and make
82 // sure the redlines are sorted and non-overlapping.
83 void lcl_CheckRedline( const IDocumentRedlineAccess& redlineAccess )
85 const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
87 // verify valid redline positions
88 for(SwRangeRedline* i : rTable)
89 lcl_CheckPam( i );
91 for(SwRangeRedline* j : rTable)
93 // check for empty redlines
94 // note: these can destroy sorting in SwTextNode::Update()
95 // if there's another one without mark on the same pos.
96 OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
97 ( j->GetContentIdx() != nullptr ),
98 ERROR_PREFIX "empty redline" );
101 // verify proper redline sorting
102 for( size_t n = 1; n < rTable.size(); ++n )
104 const SwRangeRedline* pPrev = rTable[ n-1 ];
105 const SwRangeRedline* pCurrent = rTable[ n ];
107 // check redline sorting
108 SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
109 ERROR_PREFIX "not sorted correctly" );
111 // check for overlapping redlines
112 SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
113 ERROR_PREFIX "overlapping redlines" );
116 assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
120 #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
122 #else
124 #define CHECK_REDLINE( pDoc )
126 #endif
128 namespace sw {
130 static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
132 auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
133 SwFieldIds::TableOfAuthorities, OUString(), false)));
134 if (pAuthType) // created on demand...
136 pAuthType->DelSequenceArray();
138 rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
139 rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
140 rIDFA.UpdateExpFields(nullptr, false);
141 rIDFA.UpdateRefFields();
144 void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
146 if (rDoc.IsClipBoard())
148 return;
150 // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
151 // the AppendFootnote/RemoveFootnote will do it by itself!
152 rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
153 SwPosition currentStart(*rPam.Start());
154 SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
155 while (!pStartNode)
157 // note: branch only taken for redlines, not fieldmarks
158 SwStartNode *const pTableOrSectionNode(
159 currentStart.GetNode().IsTableNode()
160 ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
161 : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
162 if ( !pTableOrSectionNode )
164 SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
165 return;
167 for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
169 pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
171 for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
173 if (pLayout->HasMergedParas())
175 if (pTableOrSectionNode->IsTableNode())
177 static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
179 else
181 static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
185 currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
186 pStartNode = currentStart.GetNode().GetTextNode();
188 if (currentStart < *rPam.End())
190 SwTextNode * pNode(pStartNode);
193 // deleted text node: remove it from "hidden" list
194 // to update numbering in Show Changes mode
195 SwPosition aPos( *pNode, pNode->Len() );
196 if ( pNode->GetNumRule() && aPos < *rPam.End() )
197 pNode->RemoveFromListRLHidden();
199 std::vector<SwTextFrame*> frames;
200 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
201 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
203 if (pFrame->getRootFrame()->HasMergedParas())
205 frames.push_back(pFrame);
207 // set anchored objects as deleted
208 pFrame->SetDrawObjsAsDeleted(true);
210 if (frames.empty())
212 auto const layouts(rDoc.GetAllLayouts());
213 assert(std::none_of(layouts.begin(), layouts.end(),
214 [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
215 (void) layouts;
216 break;
218 auto eMode(sw::FrameMode::Existing);
219 SwTextNode * pLast(pNode);
220 for (SwTextFrame * pFrame : frames)
222 SwTextNode & rFirstNode(pFrame->GetMergedPara()
223 ? *pFrame->GetMergedPara()->pFirstNode
224 : *pNode);
225 assert(pNode == pStartNode
226 ? rFirstNode.GetIndex() <= pNode->GetIndex()
227 : &rFirstNode == pNode);
228 // clear old one first to avoid DelFrames confusing updates & asserts...
229 pFrame->SetMergedPara(nullptr);
230 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
231 *pFrame, rFirstNode, eMode));
232 eMode = sw::FrameMode::New; // Existing is not idempotent!
233 // the first node of the new redline is not necessarily the first
234 // node of the merged frame, there could be another redline nearby
235 sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
236 // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
237 if (pFrame->GetMergedPara())
239 pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
242 SwNodeIndex tmp(*pLast);
243 // skip over hidden sections!
244 pNode = static_cast<SwTextNode*>(SwNodes::GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
246 while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
248 // fields last - SwGetRefField::UpdateField requires up-to-date frames
249 UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
251 // update SwPostItMgr / notes in the margin
252 rDoc.GetDocShell()->Broadcast(
253 SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
256 void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
258 // tdf#147006 fieldmark command may be empty => do not call AppendAllObjs()
259 if (rDoc.IsClipBoard() || *rPam.GetPoint() == *rPam.GetMark())
261 return;
263 bool isAppendObjsCalled(false);
264 rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
265 SwPosition currentStart(*rPam.Start());
266 SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
267 while (!pStartNode)
269 // note: branch only taken for redlines, not fieldmarks
270 SwStartNode *const pTableOrSectionNode(
271 currentStart.GetNode().IsTableNode()
272 ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
273 : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
274 assert(pTableOrSectionNode); // known pathology
275 for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
277 pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
279 if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
281 // note: this will also create frames for all currently hidden flys
282 // because it calls AppendAllObjs
283 ::MakeFrames(&rDoc, currentStart.GetNode(), *pTableOrSectionNode->EndOfSectionNode());
284 isAppendObjsCalled = true;
286 currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
287 pStartNode = currentStart.GetNode().GetTextNode();
289 if (currentStart < *rPam.End())
291 SwTextNode * pNode(pStartNode);
294 // undeleted text node: add it to the "hidden" list
295 // to update numbering in Show Changes mode
296 SwPosition aPos( *pNode, pNode->Len() );
297 if ( pNode->GetNumRule() && aPos < *rPam.End() )
298 pNode->AddToListRLHidden();
300 std::vector<SwTextFrame*> frames;
301 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
302 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
304 if (pFrame->getRootFrame()->HasMergedParas())
306 frames.push_back(pFrame);
308 // set anchored objects as not deleted
309 pFrame->SetDrawObjsAsDeleted(false);
311 if (frames.empty())
313 // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
314 if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
316 auto const layouts(rDoc.GetAllLayouts());
317 assert(std::none_of(layouts.begin(), layouts.end(),
318 [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
319 (void) layouts;
321 isAppendObjsCalled = true; // skip that!
322 break;
325 // no nodes can be unmerged by this - skip MakeFrames() etc.
326 if (rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode())
328 break; // continue with AppendAllObjs()
331 // first, call CheckParaRedlineMerge on the first paragraph,
332 // to init flag on new merge range (if any) + 1st node post the merge
333 auto eMode(sw::FrameMode::Existing);
334 SwTextNode * pLast(pNode);
335 for (SwTextFrame * pFrame : frames)
337 if (auto const pMergedPara = pFrame->GetMergedPara())
339 pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
340 assert(pNode == pStartNode
341 ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
342 : pMergedPara->pFirstNode == pNode);
343 // clear old one first to avoid DelFrames confusing updates & asserts...
344 SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
345 pFrame->SetMergedPara(nullptr);
346 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
347 *pFrame, rFirstNode, eMode));
348 eMode = sw::FrameMode::New; // Existing is not idempotent!
349 // update pNode so MakeFrames starts on 2nd node
350 pNode = &rFirstNode;
353 if (pLast != pNode)
355 // now start node until end of merge + 1 has proper flags; MakeFrames
356 // should pick up from the next node in need of frames by checking flags
357 SwNodeIndex const start(*pNode, +1);
358 SwNodeIndex const end(*pLast, +1); // end is exclusive
359 // note: this will also create frames for all currently hidden flys
360 // both on first and non-first nodes because it calls AppendAllObjs
361 ::MakeFrames(&rDoc, start.GetNode(), end.GetNode());
362 isAppendObjsCalled = true;
363 // re-use this to move flys that are now on the wrong frame, with end
364 // of redline as "second" node; the nodes between start and end should
365 // be complete with MakeFrames already
366 sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
368 SwNodeIndex tmp(*pLast);
369 // skip over hidden sections!
370 pNode = static_cast<SwTextNode*>(SwNodes::GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
372 while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
375 if (!isAppendObjsCalled)
376 { // recreate flys in the one node the hard way...
377 for (auto const& pLayout : rDoc.GetAllLayouts())
379 if (pLayout->HasMergedParas())
381 AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
382 break;
386 // fields last - SwGetRefField::UpdateField requires up-to-date frames
387 UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
389 const SwTextNode *pTextNode = rPam.GetPointNode().GetTextNode();
390 SwTextAttr* pTextAttr = pTextNode ? pTextNode->GetFieldTextAttrAt(rPam.GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default) : nullptr;
391 SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pTextAttr));
392 if (pTextField && comphelper::LibreOfficeKit::isActive() )
393 rDoc.GetDocShell()->Broadcast(
394 SwFormatFieldHint(&pTextField->GetFormatField(), SwFormatFieldHintWhich::INSERTED));
395 else
396 rDoc.GetDocShell()->Broadcast(
397 SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
400 } // namespace sw
402 namespace
404 bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
406 const SwContentNode* pCNd;
407 if( 0 != rPos2.GetContentIndex() )
408 return false;
409 if( rPos2.GetNodeIndex() - 1 != rPos1.GetNodeIndex() )
410 return false;
411 pCNd = rPos1.GetNode().GetContentNode();
412 return pCNd && rPos1.GetContentIndex() == pCNd->Len();
415 // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
416 std::unique_ptr<SwRedlineExtraData_FormatColl> lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
418 SwTextNode* pToNode = rTo.GetNode().GetTextNode();
419 SwTextNode* pFromNode = rFrom.GetNode().GetTextNode();
420 if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
422 const SwPaM aPam(*pToNode);
423 SwDoc& rDoc = aPam.GetDoc();
424 // using Undo, copy paragraph style
425 SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
426 SwTextFormatColl* pToColl = pToNode->GetTextColl();
427 if (bCopy && pFromColl != pToColl)
428 rDoc.SetTextFormatColl(aPam, pFromColl);
430 // using Undo, remove direct paragraph formatting of the "To" paragraph,
431 // and apply here direct paragraph formatting of the "From" paragraph
432 SfxItemSet aTmp(SfxItemSet::makeFixedSfxItemSet<
433 RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
434 RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
435 RES_CNTNT, RES_FRMATR_END - 1>(rDoc.GetAttrPool()));
436 SfxItemSet aTmp2(aTmp);
438 pToNode->GetParaAttr(aTmp, 0, 0);
439 pFromNode->GetParaAttr(aTmp2, 0, 0);
441 bool bSameSet = aTmp == aTmp2;
443 if (!bSameSet)
445 for (SfxItemIter aIter(aTmp); !aIter.IsAtEnd(); aIter.NextItem())
447 const sal_uInt16 nWhich(aIter.GetCurWhich());
448 if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
449 SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
450 aTmp2.Put( aTmp.GetPool()->GetUserOrPoolDefaultItem(nWhich) );
454 if (bCopy && !bSameSet)
455 rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
456 else if (!bCopy && (!bSameSet || pFromColl != pToColl))
457 return std::make_unique<SwRedlineExtraData_FormatColl>( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
459 return nullptr;
462 // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
463 void lcl_DeleteTrackedTableRow ( const SwPosition* pPos )
465 const SwTableBox* pBox = pPos->GetNode().GetTableBox();
466 if ( !pBox )
467 return;
469 // tracked column deletion
471 const SvxPrintItem *pHasBoxTextChangesOnlyProp =
472 pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
473 // empty table cell with property "HasTextChangesOnly" = false
474 if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
476 SwCursor aCursor( *pPos, nullptr );
477 if ( pBox->IsEmpty() )
479 // tdf#155747 remove table cursor
480 pPos->GetDoc().GetDocShell()->GetWrtShell()->EnterStdMode();
481 // TODO check the other cells of the column
482 // before removing the column
483 pPos->GetDoc().DeleteCol( aCursor );
484 return;
486 else
488 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
489 pPos->GetDoc().SetBoxAttr( aCursor, aHasTextChangesOnly );
493 // tracked row deletion
495 const SwTableLine* pLine = pBox->GetUpper();
496 const SvxPrintItem *pHasTextChangesOnlyProp =
497 pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
498 // empty table row with property "HasTextChangesOnly" = false
499 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
501 if ( pLine->IsEmpty() )
503 SwCursor aCursor( *pPos, nullptr );
504 pPos->GetDoc().DeleteRow( aCursor );
506 else
508 // update property "HasTextChangesOnly"
509 SwRedlineTable::size_type nPos = 0;
510 (void)pLine->UpdateTextChangesOnly(nPos);
515 // at rejection of a deletion in a table, remove the tracking of the table row
516 // (also at accepting the last redline insertion of a tracked table row insertion)
517 void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos, bool bRejectDeletion )
519 const SwTableBox* pBox = pPos->GetNode().GetTableBox();
520 if ( !pBox )
521 return;
523 // tracked column deletion
525 const SvxPrintItem *pHasBoxTextChangesOnlyProp =
526 pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
527 // table cell property "HasTextChangesOnly" is set and its value is false
528 if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
530 SvxPrintItem aUnsetTracking(RES_PRINT, true);
531 SwCursor aCursor( *pPos, nullptr );
532 pPos->GetDoc().SetBoxAttr( aCursor, aUnsetTracking );
535 // tracked row deletion
537 const SwTableLine* pLine = pBox->GetUpper();
538 const SvxPrintItem *pHasTextChangesOnlyProp =
539 pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
540 // table row property "HasTextChangesOnly" is set and its value is false
541 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
543 bool bNoMoreInsertion = false;
544 if ( !bRejectDeletion )
546 SwRedlineTable::size_type nPos = 0;
547 SwRedlineTable::size_type nInsert = pLine->UpdateTextChangesOnly(nPos, /*bUpdateProperty=*/false);
549 if ( SwRedlineTable::npos == nInsert )
550 bNoMoreInsertion = true;
552 if ( bRejectDeletion || bNoMoreInsertion )
554 SvxPrintItem aUnsetTracking(RES_PRINT, true);
555 SwCursor aCursor( *pPos, nullptr );
556 pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
561 bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
562 bool bCallDelete,
563 const SwPosition* pSttRng = nullptr,
564 const SwPosition* pEndRng = nullptr )
566 bool bRet = true;
567 SwRangeRedline* pRedl = rArr[ rPos ];
568 SwPosition *pRStt = nullptr, *pREnd = nullptr;
569 SwComparePosition eCmp = SwComparePosition::Outside;
570 if( pSttRng && pEndRng )
572 pRStt = pRedl->Start();
573 pREnd = pRedl->End();
574 eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
577 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
579 switch( pRedl->GetType() )
581 case RedlineType::Insert:
582 case RedlineType::Format:
584 bool bCheck = false, bReplace = false;
585 switch( eCmp )
587 case SwComparePosition::Inside:
588 if( *pSttRng == *pRStt )
589 pRedl->SetStart( *pEndRng, pRStt );
590 else
592 if( *pEndRng != *pREnd )
594 // split up
595 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
596 pNew->SetStart( *pEndRng );
597 rArr.Insert( pNew ); ++rPos;
599 pRedl->SetEnd( *pSttRng, pREnd );
600 bCheck = true;
602 break;
604 case SwComparePosition::OverlapBefore:
605 pRedl->SetStart( *pEndRng, pRStt );
606 bReplace = true;
607 break;
609 case SwComparePosition::OverlapBehind:
610 pRedl->SetEnd( *pSttRng, pREnd );
611 bCheck = true;
612 break;
614 case SwComparePosition::Outside:
615 case SwComparePosition::Equal:
617 bool bInsert = RedlineType::Insert == pRedl->GetType();
618 SwPosition aPos(pRedl->Start()->GetNode());
619 rArr.DeleteAndDestroy( rPos-- );
621 // remove tracking of the table row, if needed
622 if ( bInsert )
623 lcl_RemoveTrackingOfTableRow( &aPos, /*bRejectDelete=*/false );
625 break;
627 default:
628 bRet = false;
631 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
633 // re-insert
634 rArr.Remove( pRedl );
635 rArr.Insert( pRedl );
638 break;
639 case RedlineType::Delete:
641 SwDoc& rDoc = pRedl->GetDoc();
642 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
643 bool bDelRedl = false;
644 switch( eCmp )
646 case SwComparePosition::Inside:
647 if( bCallDelete )
649 pDelStt = pSttRng;
650 pDelEnd = pEndRng;
652 break;
654 case SwComparePosition::OverlapBefore:
655 if( bCallDelete )
657 pDelStt = pRStt;
658 pDelEnd = pEndRng;
660 break;
661 case SwComparePosition::OverlapBehind:
662 if( bCallDelete )
664 pDelStt = pREnd;
665 pDelEnd = pSttRng;
667 break;
669 case SwComparePosition::Outside:
670 case SwComparePosition::Equal:
672 rArr.Remove( rPos-- );
673 bDelRedl = true;
674 if( bCallDelete )
676 pDelStt = pRedl->Start();
677 pDelEnd = pRedl->End();
680 break;
681 default:
682 bRet = false;
685 if( pDelStt && pDelEnd )
687 SwPaM aPam( *pDelStt, *pDelEnd );
688 SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode();
689 SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
690 pRStt = pRedl->Start();
691 pREnd = pRedl->End();
693 // keep style of the empty paragraph after deletion of wholly paragraphs
694 if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->GetContentIndex() == 0 )
695 lcl_CopyStyle(*pREnd, *pRStt);
697 if( bDelRedl )
698 delete pRedl;
700 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
701 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
703 if( pCSttNd && pCEndNd )
705 rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
706 lcl_DeleteTrackedTableRow( aPam.End() );
708 else if (pCSttNd && !pCEndNd)
710 aPam.GetBound().nContent.Assign( nullptr, 0 );
711 aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
712 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
714 else
716 rDoc.getIDocumentContentOperations().DeleteRange(aPam);
718 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
720 else if( bDelRedl )
721 delete pRedl;
723 break;
725 case RedlineType::FmtColl:
726 case RedlineType::ParagraphFormat:
727 rArr.DeleteAndDestroy( rPos-- );
728 break;
730 default:
731 bRet = false;
733 return bRet;
736 bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
737 bool bCallDelete,
738 const SwPosition* pSttRng = nullptr,
739 const SwPosition* pEndRng = nullptr )
741 bool bRet = true;
742 SwRangeRedline* pRedl = rArr[ rPos ];
743 SwDoc& rDoc = pRedl->GetDoc();
744 SwPosition *pRStt = nullptr, *pREnd = nullptr;
745 SwComparePosition eCmp = SwComparePosition::Outside;
746 if( pSttRng && pEndRng )
748 pRStt = pRedl->Start();
749 pREnd = pRedl->End();
750 eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
753 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
755 switch( pRedl->GetType() )
757 case RedlineType::Insert:
759 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
760 bool bDelRedl = false;
761 switch( eCmp )
763 case SwComparePosition::Inside:
764 if( bCallDelete )
766 pDelStt = pSttRng;
767 pDelEnd = pEndRng;
769 break;
771 case SwComparePosition::OverlapBefore:
772 if( bCallDelete )
774 pDelStt = pRStt;
775 pDelEnd = pEndRng;
777 break;
778 case SwComparePosition::OverlapBehind:
779 if( bCallDelete )
781 pDelStt = pREnd;
782 pDelEnd = pSttRng;
784 break;
785 case SwComparePosition::Outside:
786 case SwComparePosition::Equal:
788 // delete the range again
789 rArr.Remove( rPos-- );
790 bDelRedl = true;
791 if( bCallDelete )
793 pDelStt = pRedl->Start();
794 pDelEnd = pRedl->End();
797 break;
799 default:
800 bRet = false;
802 if( pDelStt && pDelEnd )
804 SwPaM aPam( *pDelStt, *pDelEnd );
806 SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode();
807 SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
809 if( bDelRedl )
810 delete pRedl;
812 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
813 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
815 if( pCSttNd && pCEndNd )
817 rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
818 lcl_DeleteTrackedTableRow( aPam.End() );
820 else if (pCSttNd && !pCEndNd)
822 aPam.GetBound().nContent.Assign( nullptr, 0 );
823 aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
824 if (aPam.End()->GetNode().IsStartNode())
825 { // end node will be deleted too! see nNodeDiff+1
826 aPam.End()->Adjust(SwNodeOffset(-1));
828 assert(!aPam.End()->GetNode().IsStartNode());
829 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
831 else
833 rDoc.getIDocumentContentOperations().DeleteRange(aPam);
835 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
837 else if( bDelRedl )
838 delete pRedl;
840 break;
841 case RedlineType::Delete:
843 SwRangeRedline* pNew = nullptr;
844 bool bCheck = false, bReplace = false;
845 SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
846 pEndRng ? *pEndRng : *pRedl->End());
848 if( pRedl->GetExtraData() )
849 pRedl->GetExtraData()->Reject( *pRedl );
851 // remove tracking of the table row, if needed
852 lcl_RemoveTrackingOfTableRow( updatePaM.End(), /*bRejectDelete=*/true );
854 switch( eCmp )
856 case SwComparePosition::Inside:
858 if( 1 < pRedl->GetStackCount() )
860 pNew = new SwRangeRedline( *pRedl );
861 pNew->PopData();
863 if( *pSttRng == *pRStt )
865 pRedl->SetStart( *pEndRng, pRStt );
866 bReplace = true;
867 if( pNew )
868 pNew->SetEnd( *pEndRng );
870 else
872 if( *pEndRng != *pREnd )
874 // split up
875 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
876 pCpy->SetStart( *pEndRng );
877 rArr.Insert( pCpy ); ++rPos;
878 if( pNew )
879 pNew->SetEnd( *pEndRng );
882 pRedl->SetEnd( *pSttRng, pREnd );
883 bCheck = true;
884 if( pNew )
885 pNew->SetStart( *pSttRng );
888 break;
890 case SwComparePosition::OverlapBefore:
891 if( 1 < pRedl->GetStackCount() )
893 pNew = new SwRangeRedline( *pRedl );
894 pNew->PopData();
896 pRedl->SetStart( *pEndRng, pRStt );
897 bReplace = true;
898 if( pNew )
899 pNew->SetEnd( *pEndRng );
900 break;
902 case SwComparePosition::OverlapBehind:
903 if( 1 < pRedl->GetStackCount() )
905 pNew = new SwRangeRedline( *pRedl );
906 pNew->PopData();
908 pRedl->SetEnd( *pSttRng, pREnd );
909 bCheck = true;
910 if( pNew )
911 pNew->SetStart( *pSttRng );
912 break;
914 case SwComparePosition::Outside:
915 case SwComparePosition::Equal:
916 if( !pRedl->PopData() )
917 // deleting the RedlineObject is enough
918 rArr.DeleteAndDestroy( rPos-- );
919 break;
921 default:
922 bRet = false;
925 if( pNew )
927 rArr.Insert( pNew ); ++rPos;
930 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
932 // re-insert
933 rArr.Remove( pRedl );
934 rArr.Insert( pRedl );
937 sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
939 break;
941 case RedlineType::Format:
942 case RedlineType::FmtColl:
943 case RedlineType::ParagraphFormat:
945 // tdf#52391 instead of hidden acception at the requested
946 // rejection, remove direct text formatting to get the potential
947 // original state of the text (FIXME if the original text
948 // has already contained direct text formatting: unfortunately
949 // ODF 1.2 doesn't support rejection of format-only changes)
950 if ( pRedl->GetType() == RedlineType::Format )
952 SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
953 rDoc.ResetAttrs(aPam);
955 else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
957 // handle paragraph formatting changes
958 // (range is only a full paragraph or a part of it)
959 const SwPosition* pStart = pRedl->Start();
960 SwTextNode* pTNd = pStart->GetNode().GetTextNode();
961 if( pTNd )
963 // expand range to the whole paragraph
964 // and reset only the paragraph attributes
965 SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
966 o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
968 static constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
969 { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
970 { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
973 for (const auto& [nBegin, nEnd] : aResetableSetRange)
975 for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
976 aResetAttrsArray.insert( i );
979 rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
981 // remove numbering
982 if ( pTNd->GetNumRule() )
983 rDoc.DelNumRules(aPam);
987 if( pRedl->GetExtraData() )
988 pRedl->GetExtraData()->Reject( *pRedl );
990 rArr.DeleteAndDestroy( rPos-- );
992 break;
994 default:
995 bRet = false;
997 return bRet;
1000 bool lcl_AcceptInnerInsertRedline(SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
1001 int nDepth)
1003 SwRangeRedline* pRedl = rArr[rPos];
1004 SwDoc& rDoc = pRedl->GetDoc();
1005 SwPaM const updatePaM(*pRedl->Start(), *pRedl->End());
1007 pRedl->PopAllDataAfter(nDepth);
1008 sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
1009 return true;
1012 typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
1013 bool bCallDelete,
1014 const SwPosition* pSttRng,
1015 const SwPosition* pEndRng);
1018 int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
1019 SwRedlineTable& rArr, bool bCallDelete,
1020 const SwPaM& rPam)
1022 SwRedlineTable::size_type n = 0;
1023 int nCount = 0;
1025 const SwPosition* pStart = rPam.Start(),
1026 * pEnd = rPam.End();
1027 const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStart, n );
1028 if( pFnd && // Is new a part of it?
1029 ( *pFnd->Start() != *pStart || *pFnd->End() > *pEnd ))
1031 // Only revoke the partial selection
1032 if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStart, pEnd ))
1033 nCount++;
1034 ++n;
1037 // tdf#119824 first we will accept only overlapping paragraph format changes
1038 // in the first loop to avoid potential content changes during Redo
1039 bool bHasParagraphFormatChange = false;
1040 for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
1042 for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
1044 SwRangeRedline* pTmp = rArr[ o ];
1045 if( pTmp->HasMark() && pTmp->IsVisible() )
1047 if( *pTmp->End() <= *pEnd )
1049 if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
1050 (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
1052 bHasParagraphFormatChange = true;
1053 nCount++;
1056 else
1058 if( *pTmp->Start() < *pEnd )
1060 // Only revoke the partial selection
1061 if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
1062 (*fn_AcceptReject)( rArr, o, bCallDelete, pStart, pEnd ))
1064 bHasParagraphFormatChange = true;
1065 nCount++;
1068 break;
1073 return nCount;
1076 void lcl_AdjustRedlineRange( SwPaM& rPam )
1078 // The Selection is only in the ContentSection. If there are Redlines
1079 // to Non-ContentNodes before or after that, then the Selections
1080 // expand to them.
1081 auto [pStart, pEnd] = rPam.StartEnd(); // SwPosition*
1082 SwDoc& rDoc = rPam.GetDoc();
1083 if( !pStart->GetContentIndex() &&
1084 !rDoc.GetNodes()[ pStart->GetNodeIndex() - 1 ]->IsContentNode() )
1086 const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, nullptr );
1087 if( pRedl )
1089 const SwPosition* pRStt = pRedl->Start();
1090 if( !pRStt->GetContentIndex() && pRStt->GetNodeIndex() ==
1091 pStart->GetNodeIndex() - 1 )
1092 *pStart = *pRStt;
1095 if( pEnd->GetNode().IsContentNode() &&
1096 !rDoc.GetNodes()[ pEnd->GetNodeIndex() + 1 ]->IsContentNode() &&
1097 pEnd->GetContentIndex() == pEnd->GetNode().GetContentNode()->Len() )
1099 const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
1100 if( pRedl )
1102 const SwPosition* pREnd = pRedl->End();
1103 if( !pREnd->GetContentIndex() && pREnd->GetNodeIndex() ==
1104 pEnd->GetNodeIndex() + 1 )
1105 *pEnd = *pREnd;
1110 /// in case some text is deleted, ensure that the not-yet-inserted
1111 /// SwRangeRedline has its positions corrected not to point to deleted node
1112 class TemporaryRedlineUpdater
1114 private:
1115 SwRangeRedline & m_rRedline;
1116 std::shared_ptr<SwUnoCursor> m_pCursor;
1117 public:
1118 TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
1119 : m_rRedline(rRedline)
1120 , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
1122 if (m_rRedline.HasMark())
1124 m_pCursor->SetMark();
1125 *m_pCursor->GetMark() = *m_rRedline.GetMark();
1126 m_rRedline.GetMark()->Assign(rDoc.GetNodes().GetEndOfContent());
1128 m_rRedline.GetPoint()->Assign(rDoc.GetNodes().GetEndOfContent());
1130 ~TemporaryRedlineUpdater()
1132 static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
1137 namespace sw
1140 DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
1141 : m_rDoc(i_rSwdoc)
1142 , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
1143 , mbIsRedlineMove(false)
1144 , mnAutoFormatRedlnCommentNo(0)
1148 RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
1150 return meRedlineFlags;
1153 void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
1155 if( meRedlineFlags == eMode )
1156 return;
1158 if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
1159 || !(RedlineFlags::ShowMask & eMode) )
1161 bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
1162 m_rDoc.SetInXMLImport( false );
1163 // and then hide/display everything
1164 void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
1165 // uninitialized ptr is possible
1167 RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
1168 if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1169 pFnc = &SwRangeRedline::Show;
1170 else if (eShowMode == RedlineFlags::ShowInsert)
1171 pFnc = &SwRangeRedline::Hide;
1172 else if (eShowMode == RedlineFlags::ShowDelete)
1173 pFnc = &SwRangeRedline::ShowOriginal;
1174 else
1176 pFnc = &SwRangeRedline::Hide;
1177 eMode |= RedlineFlags::ShowInsert;
1180 CheckAnchoredFlyConsistency(m_rDoc);
1181 CHECK_REDLINE( *this )
1183 o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
1184 if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1186 // sw_redlinehide: the problem here is that MoveFromSection
1187 // creates the frames wrongly (non-merged), because its own
1188 // SwRangeRedline has wrong positions until after the nodes
1189 // are all moved, so fix things up by force by re-creating
1190 // all merged frames from scratch.
1191 o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
1192 for (SwRootFrame *const pLayout : layouts)
1194 if (pLayout->IsHideRedlines())
1196 pLayout->SetHideRedlines(false);
1197 hiddenLayouts.insert(pLayout);
1202 for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
1203 for (size_t i = 0; i < maRedlineTable.size(); )
1205 SwRangeRedline *const pRedline = maRedlineTable[i];
1206 (pRedline->*pFnc)(nLoop, i, false);
1207 // a previous redline may have been deleted
1208 if (i < maRedlineTable.size() && maRedlineTable[i] == pRedline)
1209 ++i;
1212 //SwRangeRedline::MoveFromSection routinely changes
1213 //the keys that mpRedlineTable is sorted by
1214 maRedlineTable.Resort();
1216 CheckAnchoredFlyConsistency(m_rDoc);
1217 CHECK_REDLINE( *this )
1219 for (SwRootFrame *const pLayout : hiddenLayouts)
1221 pLayout->SetHideRedlines(true);
1224 m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
1226 meRedlineFlags = eMode;
1227 m_rDoc.getIDocumentState().SetModified();
1229 // #TODO - add 'SwExtraRedlineTable' also ?
1232 bool DocumentRedlineManager::IsRedlineOn() const
1234 return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
1237 bool DocumentRedlineManager::IsIgnoreRedline() const
1239 return bool(RedlineFlags::Ignore & meRedlineFlags);
1242 void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
1244 meRedlineFlags = eMode;
1247 const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
1249 return maRedlineTable;
1252 SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
1254 return maRedlineTable;
1257 const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
1259 return maExtraRedlineTable;
1262 SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
1264 return maExtraRedlineTable;
1267 bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
1269 if (&rNode.GetNodes() != &m_rDoc.GetNodes())
1270 return false;
1272 SwPosition aPos(rNode);
1273 SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
1274 SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
1275 SwPosition(rEndOfRedlines));
1277 return aPam.ContainsPosition(aPos);
1280 bool DocumentRedlineManager::IsRedlineMove() const
1282 return mbIsRedlineMove;
1285 void DocumentRedlineManager::SetRedlineMove(bool bFlag)
1287 mbIsRedlineMove = bFlag;
1291 Text means Text not "polluted" by Redlines.
1293 Behaviour of Insert-Redline:
1294 - in the Text - insert Redline Object
1295 - in InsertRedline (own) - ignore, existing is extended
1296 - in InsertRedline (others) - split up InsertRedline and
1297 insert Redline Object
1298 - in DeleteRedline - split up DeleteRedline or
1299 move at the end/beginning
1301 Behaviour of Delete-Redline:
1302 - in the Text - insert Redline Object
1303 - in DeleteRedline (own/others) - ignore
1304 - in InsertRedline (own) - ignore, but delete character
1305 - in InsertRedline (others) - split up InsertRedline and
1306 insert Redline Object
1307 - Text and own Insert overlap - delete Text in the own Insert,
1308 extend in the other Text
1309 (up to the Insert!)
1310 - Text and other Insert overlap - insert Redline Object, the
1311 other Insert is overlapped by
1312 the Delete
1314 IDocumentRedlineAccess::AppendResult
1315 DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete,
1316 sal_uInt32 nMoveIDToDelete)
1318 CHECK_REDLINE( *this )
1320 if (!IsRedlineOn() || IsShowOriginal(meRedlineFlags))
1322 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
1324 RedlineFlags eOld = meRedlineFlags;
1325 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
1326 // The ShowMode needs to be retained!
1327 meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
1328 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1329 meRedlineFlags = eOld;
1331 delete pNewRedl;
1332 pNewRedl = nullptr;
1333 CHECK_REDLINE( *this )
1334 return AppendResult::IGNORED;
1337 // Collect MoveID's of the redlines we delete.
1338 // If there is only 1, then we should use its ID. (continuing the move)
1339 std::set<sal_uInt32> deletedMoveIDs;
1341 bool bMerged = false;
1343 pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
1345 if( m_rDoc.IsAutoFormatRedline() )
1347 pNewRedl->SetAutoFormat();
1348 if( moAutoFormatRedlnComment && !moAutoFormatRedlnComment->isEmpty() )
1350 pNewRedl->SetComment( *moAutoFormatRedlnComment );
1351 pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
1355 auto [pStart, pEnd] = pNewRedl->StartEnd(); // SwPosition*
1357 SwTextNode* pTextNode = pStart->GetNode().GetTextNode();
1358 if( pTextNode == nullptr )
1360 if( pStart->GetContentIndex() > 0 )
1362 OSL_ENSURE( false, "Redline start: non-text-node with content" );
1363 pStart->SetContent( 0 );
1366 else
1368 if( pStart->GetContentIndex() > pTextNode->Len() )
1370 OSL_ENSURE( false, "Redline start: index after text" );
1371 pStart->SetContent( pTextNode->Len() );
1374 pTextNode = pEnd->GetNode().GetTextNode();
1375 if( pTextNode == nullptr )
1377 if( pEnd->GetContentIndex() > 0 )
1379 OSL_ENSURE( false, "Redline end: non-text-node with content" );
1380 pEnd->SetContent(0);
1383 else
1385 if( pEnd->GetContentIndex() > pTextNode->Len() )
1387 OSL_ENSURE( false, "Redline end: index after text" );
1388 pEnd->SetContent( pTextNode->Len() );
1392 if( ( *pStart == *pEnd ) &&
1393 ( pNewRedl->GetContentIdx() == nullptr ) )
1394 { // Do not insert empty redlines
1395 delete pNewRedl;
1396 return AppendResult::IGNORED;
1398 bool bCompress = false;
1399 SwRedlineTable::size_type n = 0;
1400 // look up the first Redline for the starting position
1401 if( !GetRedline( *pStart, &n ) && n > 0 )
1402 --n;
1403 const SwRedlineTable::size_type nStartPos = n;
1404 bool bDec = false;
1406 for( ; pNewRedl && n < maRedlineTable.size(); bDec ? n : ++n )
1408 bDec = false;
1410 SwRangeRedline* pRedl = maRedlineTable[ n ];
1411 auto [pRStt, pREnd] = pRedl->StartEnd();
1413 // #i8518# remove empty redlines while we're at it
1414 if( ( *pRStt == *pREnd ) &&
1415 ( pRedl->GetContentIdx() == nullptr ) )
1417 maRedlineTable.DeleteAndDestroy(n);
1418 continue;
1421 SwComparePosition eCmpPos = ComparePosition( *pStart, *pEnd, *pRStt, *pREnd );
1423 if ( SwComparePosition::Before == eCmpPos && !IsPrevPos( *pEnd, *pRStt ))
1424 break;
1426 switch( pNewRedl->GetType() )
1428 case RedlineType::Insert:
1429 switch( pRedl->GetType() )
1431 case RedlineType::Insert:
1432 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1433 // don't join inserted characters with moved text
1434 !pRedl->IsMoved() )
1436 bool bDelete = false;
1437 bool bMaybeNotify = false;
1439 // Merge if applicable?
1440 if( (( SwComparePosition::Behind == eCmpPos &&
1441 IsPrevPos( *pREnd, *pStart ) ) ||
1442 ( SwComparePosition::CollideStart == eCmpPos ) ||
1443 ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
1444 pRedl->CanCombine( *pNewRedl ) &&
1445 ( n+1 >= maRedlineTable.size() ||
1446 ( *maRedlineTable[ n+1 ]->Start() >= *pEnd &&
1447 *maRedlineTable[ n+1 ]->Start() != *pREnd ) ) )
1449 pRedl->SetEnd( *pEnd, pREnd );
1450 if( !pRedl->HasValidRange() )
1452 // re-insert
1453 maRedlineTable.Remove( n );
1454 maRedlineTable.Insert( pRedl );
1457 bMerged = true;
1458 bDelete = true;
1460 else if( (( SwComparePosition::Before == eCmpPos &&
1461 IsPrevPos( *pEnd, *pRStt ) ) ||
1462 ( SwComparePosition::CollideEnd == eCmpPos ) ||
1463 ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
1464 pRedl->CanCombine( *pNewRedl ) &&
1465 ( !n ||
1466 *maRedlineTable[ n-1 ]->End() != *pRStt ))
1468 pRedl->SetStart( *pStart, pRStt );
1469 // re-insert
1470 maRedlineTable.Remove( n );
1471 maRedlineTable.Insert( pRedl );
1473 bMerged = true;
1474 bDelete = true;
1476 else if ( SwComparePosition::Outside == eCmpPos )
1478 // own insert-over-insert redlines:
1479 // just scrap the inside ones
1480 maRedlineTable.DeleteAndDestroy( n );
1481 bDec = true;
1483 else if( SwComparePosition::OverlapBehind == eCmpPos )
1485 *pStart = *pREnd;
1486 if( ( *pStart == *pEnd ) &&
1487 ( pNewRedl->GetContentIdx() == nullptr ) )
1488 bDelete = bMaybeNotify = true;
1490 else if( SwComparePosition::OverlapBefore == eCmpPos )
1492 *pEnd = *pRStt;
1493 if( ( *pStart == *pEnd ) &&
1494 ( pNewRedl->GetContentIdx() == nullptr ) )
1495 bDelete = bMaybeNotify = true;
1497 else if( SwComparePosition::Inside == eCmpPos )
1499 bDelete = bMaybeNotify = true;
1500 bMerged = true;
1502 else if( SwComparePosition::Equal == eCmpPos )
1503 bDelete = bMaybeNotify = true;
1505 if( bDelete )
1507 delete pNewRedl;
1508 pNewRedl = nullptr;
1509 bCompress = true;
1511 if (bMaybeNotify)
1512 MaybeNotifyRedlineModification(*pRedl, m_rDoc);
1514 // set IsMoved checking nearby redlines
1515 if (n < maRedlineTable.size()) // in case above 're-insert' failed
1516 maRedlineTable.isMoved(n);
1519 else if( SwComparePosition::Inside == eCmpPos )
1521 // split up
1522 if( *pEnd != *pREnd )
1524 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1525 pCpy->SetStart( *pEnd );
1526 maRedlineTable.Insert( pCpy );
1528 pRedl->SetEnd( *pStart, pREnd );
1529 if( ( *pStart == *pRStt ) &&
1530 ( pRedl->GetContentIdx() == nullptr ) )
1532 maRedlineTable.DeleteAndDestroy( n );
1533 bDec = true;
1535 else if( !pRedl->HasValidRange() )
1537 // re-insert
1538 maRedlineTable.Remove( n );
1539 maRedlineTable.Insert( pRedl );
1542 else if ( SwComparePosition::Outside == eCmpPos )
1544 // handle overlapping redlines in broken documents
1546 // split up the new redline, since it covers the
1547 // existing redline. Insert the first part, and
1548 // progress with the remainder as usual
1549 SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1550 pSplit->SetEnd( *pRStt );
1551 pNewRedl->SetStart( *pREnd );
1552 maRedlineTable.Insert( pSplit );
1553 if( *pStart == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1555 delete pNewRedl;
1556 pNewRedl = nullptr;
1557 bCompress = true;
1560 else if ( SwComparePosition::OverlapBehind == eCmpPos )
1562 // handle overlapping redlines in broken documents
1563 pNewRedl->SetStart( *pREnd );
1565 else if ( SwComparePosition::OverlapBefore == eCmpPos )
1567 // handle overlapping redlines in broken documents
1568 *pEnd = *pRStt;
1569 if( ( *pStart == *pEnd ) &&
1570 ( pNewRedl->GetContentIdx() == nullptr ) )
1572 delete pNewRedl;
1573 pNewRedl = nullptr;
1574 bCompress = true;
1576 MaybeNotifyRedlineModification(*pRedl, m_rDoc);
1579 break;
1580 case RedlineType::Delete:
1581 if( SwComparePosition::Inside == eCmpPos )
1583 // split up
1584 if( *pEnd != *pREnd )
1586 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1587 pCpy->SetStart( *pEnd );
1588 maRedlineTable.Insert( pCpy );
1590 pRedl->SetEnd( *pStart, pREnd );
1591 if( ( *pStart == *pRStt ) &&
1592 ( pRedl->GetContentIdx() == nullptr ) )
1594 maRedlineTable.DeleteAndDestroy( n );
1595 bDec = true;
1597 else if( !pRedl->HasValidRange() )
1599 // re-insert
1600 maRedlineTable.Remove( n );
1601 maRedlineTable.Insert( pRedl, n );
1604 else if ( SwComparePosition::Outside == eCmpPos )
1606 // handle overlapping redlines in broken documents
1608 // split up the new redline, since it covers the
1609 // existing redline. Insert the first part, and
1610 // progress with the remainder as usual
1611 SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1612 pSplit->SetEnd( *pRStt );
1613 pNewRedl->SetStart( *pREnd );
1614 maRedlineTable.Insert( pSplit );
1615 if( *pStart == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1617 delete pNewRedl;
1618 pNewRedl = nullptr;
1619 bCompress = true;
1622 else if ( SwComparePosition::Equal == eCmpPos )
1624 // handle identical redlines in broken documents
1625 // delete old (delete) redline
1626 maRedlineTable.DeleteAndDestroy( n );
1627 bDec = true;
1629 else if ( SwComparePosition::OverlapBehind == eCmpPos )
1630 { // Another workaround for broken redlines
1631 pNewRedl->SetStart( *pREnd );
1633 break;
1634 case RedlineType::Format:
1635 switch( eCmpPos )
1637 case SwComparePosition::OverlapBefore:
1638 pRedl->SetStart( *pEnd, pRStt );
1639 // re-insert
1640 maRedlineTable.Remove( n );
1641 maRedlineTable.Insert( pRedl, n );
1642 bDec = true;
1643 break;
1645 case SwComparePosition::OverlapBehind:
1646 pRedl->SetEnd( *pStart, pREnd );
1647 if( *pStart == *pRStt && pRedl->GetContentIdx() == nullptr )
1649 maRedlineTable.DeleteAndDestroy( n );
1650 bDec = true;
1652 break;
1654 case SwComparePosition::Equal:
1655 case SwComparePosition::Outside:
1656 // Overlaps the current one completely or has the
1657 // same dimension, delete the old one
1658 maRedlineTable.DeleteAndDestroy( n );
1659 bDec = true;
1660 break;
1662 case SwComparePosition::Inside:
1663 // Overlaps the current one completely,
1664 // split or shorten the new one
1665 if( *pEnd != *pREnd )
1667 if( *pEnd != *pRStt )
1669 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
1670 pNew->SetStart( *pEnd );
1671 pRedl->SetEnd( *pStart, pREnd );
1672 if( *pStart == *pRStt && pRedl->GetContentIdx() == nullptr )
1673 maRedlineTable.DeleteAndDestroy( n );
1674 AppendRedline( pNew, bCallDelete );
1675 n = 0; // re-initialize
1676 bDec = true;
1679 else
1680 pRedl->SetEnd( *pStart, pREnd );
1681 break;
1682 default:
1683 break;
1685 break;
1686 default:
1687 break;
1689 break;
1691 case RedlineType::Delete:
1692 switch( pRedl->GetType() )
1694 case RedlineType::Delete:
1695 switch( eCmpPos )
1697 case SwComparePosition::Outside:
1699 // Overlaps the current one completely,
1700 // split the new one
1701 if (*pEnd == *pREnd)
1703 pNewRedl->SetEnd(*pRStt, pEnd);
1705 else if (*pStart == *pRStt)
1707 pNewRedl->SetStart(*pREnd, pStart);
1709 else
1711 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
1712 pNew->SetStart( *pREnd );
1713 pNewRedl->SetEnd( *pRStt, pEnd );
1714 AppendRedline( pNew, bCallDelete );
1715 n = 0; // re-initialize
1716 bDec = true;
1719 break;
1721 case SwComparePosition::Inside:
1722 case SwComparePosition::Equal:
1723 delete pNewRedl;
1724 pNewRedl = nullptr;
1725 bCompress = true;
1727 MaybeNotifyRedlineModification(*pRedl, m_rDoc);
1728 break;
1730 case SwComparePosition::OverlapBefore:
1731 case SwComparePosition::OverlapBehind:
1732 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1733 pRedl->CanCombine( *pNewRedl ))
1735 // If that's the case we can merge it, meaning
1736 // the new one covers this well
1737 if( SwComparePosition::OverlapBehind == eCmpPos )
1738 pNewRedl->SetStart( *pRStt, pStart );
1739 else
1740 pNewRedl->SetEnd( *pREnd, pEnd );
1741 maRedlineTable.DeleteAndDestroy( n );
1742 bDec = true;
1744 else if( SwComparePosition::OverlapBehind == eCmpPos )
1745 pNewRedl->SetStart( *pREnd, pStart );
1746 else
1747 pNewRedl->SetEnd( *pRStt, pEnd );
1748 break;
1750 case SwComparePosition::CollideEnd:
1751 if (pRStt->GetContentIndex() != 0
1752 && pRStt->GetNode() != pREnd->GetNode())
1753 { // tdf#147466 HACK: don't combine in this case to avoid the tdf#119571 code from *undeleting* section nodes
1754 break;
1756 [[fallthrough]];
1757 case SwComparePosition::CollideStart:
1758 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1759 pRedl->CanCombine( *pNewRedl ) )
1761 if( IsHideChanges( meRedlineFlags ))
1763 // Before we can merge, we make it visible!
1764 // We insert temporarily so that pNew is
1765 // also dealt with when moving the indices.
1766 maRedlineTable.Insert(pNewRedl);
1767 pRedl->Show(0, maRedlineTable.GetPos(pRedl));
1768 maRedlineTable.Remove( pNewRedl );
1769 pRStt = pRedl->Start();
1770 pREnd = pRedl->End();
1773 // If that's the case we can merge it, meaning
1774 // the new one covers this well
1775 if( SwComparePosition::CollideStart == eCmpPos )
1776 pNewRedl->SetStart( *pRStt, pStart );
1777 else
1778 pNewRedl->SetEnd( *pREnd, pEnd );
1780 // delete current (below), and restart process with
1781 // previous
1782 SwRedlineTable::size_type nToBeDeleted = n;
1783 bDec = true;
1785 if( *(pNewRedl->Start()) <= *pREnd )
1787 // Whoooah, we just extended the new 'redline'
1788 // beyond previous redlines, so better start
1789 // again. Of course this is not supposed to
1790 // happen, and in an ideal world it doesn't,
1791 // but unfortunately this code is buggy and
1792 // totally rotten so it does happen and we
1793 // better fix it.
1794 n = 0;
1797 maRedlineTable.DeleteAndDestroy( nToBeDeleted );
1799 break;
1800 default:
1801 break;
1803 break;
1805 case RedlineType::Insert:
1807 // b62341295: Do not throw away redlines
1808 // even if they are not allowed to be combined
1809 RedlineFlags eOld = meRedlineFlags;
1810 if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
1811 pRedl->IsOwnRedline( *pNewRedl ) &&
1812 // tdf#116084 tdf#121176 don't combine anonymized deletion
1813 // and anonymized insertion, i.e. with the same dummy timestamp
1814 !pRedl->GetRedlineData(0).IsAnonymized() )
1816 // Collect MoveID's of the redlines we delete.
1817 if (nMoveIDToDelete > 1 && maRedlineTable[n]->GetMoved() > 0
1818 && (eCmpPos == SwComparePosition::Equal
1819 || eCmpPos == SwComparePosition::Inside
1820 || eCmpPos == SwComparePosition::Outside
1821 || eCmpPos == SwComparePosition::OverlapBefore
1822 || eCmpPos == SwComparePosition::OverlapBehind))
1824 deletedMoveIDs.insert(maRedlineTable[n]->GetMoved());
1827 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
1828 // The ShowMode needs to be retained!
1829 meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
1830 switch( eCmpPos )
1832 case SwComparePosition::Equal:
1833 bCompress = true;
1834 maRedlineTable.DeleteAndDestroy( n );
1835 bDec = true;
1836 [[fallthrough]];
1838 case SwComparePosition::Inside:
1839 if( bCallDelete )
1841 // DeleteAndJoin does not yield the
1842 // desired result if there is no paragraph to
1843 // join with, i.e. at the end of the document.
1844 // For this case, we completely delete the
1845 // paragraphs (if, of course, we also start on
1846 // a paragraph boundary).
1847 if( (pStart->GetContentIndex() == 0) &&
1848 pEnd->GetNode().IsEndNode() )
1850 pEnd->Adjust(SwNodeOffset(-1));
1851 m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
1853 else
1854 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1856 bCompress = true;
1858 delete pNewRedl;
1859 pNewRedl = nullptr;
1861 // No need to call MaybeNotifyRedlineModification, because a notification
1862 // was already sent in DocumentRedlineManager::DeleteRedline
1863 break;
1865 case SwComparePosition::Outside:
1867 maRedlineTable.Remove( n );
1868 bDec = true;
1869 if( bCallDelete )
1871 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1872 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
1873 n = 0; // re-initialize
1875 delete pRedl;
1877 break;
1879 case SwComparePosition::OverlapBefore:
1881 SwPaM aPam( *pRStt, *pEnd );
1883 if( *pEnd == *pREnd )
1884 maRedlineTable.DeleteAndDestroy( n );
1885 else
1887 pRedl->SetStart( *pEnd, pRStt );
1888 // re-insert
1889 maRedlineTable.Remove( n );
1890 maRedlineTable.Insert( pRedl, n );
1893 if( bCallDelete )
1895 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1896 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1897 n = 0; // re-initialize
1899 bDec = true;
1901 break;
1903 case SwComparePosition::OverlapBehind:
1905 SwPaM aPam( *pStart, *pREnd );
1907 if( *pStart == *pRStt )
1909 maRedlineTable.DeleteAndDestroy( n );
1910 bDec = true;
1912 else
1913 pRedl->SetEnd( *pStart, pREnd );
1915 if( bCallDelete )
1917 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1918 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1919 n = 0; // re-initialize
1920 bDec = true;
1923 break;
1924 default:
1925 break;
1928 meRedlineFlags = eOld;
1930 else
1932 // it may be necessary to split the existing redline in
1933 // two. In this case, pRedl will be changed to cover
1934 // only part of its former range, and pNew will cover
1935 // the remainder.
1936 SwRangeRedline* pNew = nullptr;
1938 switch( eCmpPos )
1940 case SwComparePosition::Equal:
1942 pRedl->PushData( *pNewRedl );
1943 delete pNewRedl;
1944 pNewRedl = nullptr;
1945 if( IsHideChanges( meRedlineFlags ))
1947 pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
1949 bCompress = true;
1951 // set IsMoved checking nearby redlines
1952 SwRedlineTable::size_type nRIdx = maRedlineTable.GetPos(pRedl);
1953 if (nRIdx < maRedlineTable.size()) // in case above 're-insert' failed
1954 maRedlineTable.isMoved(nRIdx);
1957 break;
1959 case SwComparePosition::Inside:
1961 if( *pRStt == *pStart )
1963 // #i97421#
1964 // redline w/out extent loops
1965 if (*pStart != *pEnd)
1967 pNewRedl->PushData( *pRedl, false );
1968 pRedl->SetStart( *pEnd, pRStt );
1969 // re-insert
1970 maRedlineTable.Remove( n );
1971 maRedlineTable.Insert( pRedl, n );
1972 bDec = true;
1975 else
1977 pNewRedl->PushData( *pRedl, false );
1978 if( *pREnd != *pEnd )
1980 pNew = new SwRangeRedline( *pRedl );
1981 pNew->SetStart( *pEnd );
1983 pRedl->SetEnd( *pStart, pREnd );
1984 if( !pRedl->HasValidRange() )
1986 // re-insert
1987 maRedlineTable.Remove( n );
1988 maRedlineTable.Insert( pRedl, n );
1992 break;
1994 case SwComparePosition::Outside:
1996 pRedl->PushData( *pNewRedl );
1997 if( *pEnd == *pREnd )
1999 pNewRedl->SetEnd( *pRStt, pEnd );
2001 else if (*pStart == *pRStt)
2003 pNewRedl->SetStart(*pREnd, pStart);
2005 else
2007 pNew = new SwRangeRedline( *pNewRedl );
2008 pNew->SetEnd( *pRStt );
2009 pNewRedl->SetStart( *pREnd, pStart );
2011 bCompress = true;
2013 break;
2015 case SwComparePosition::OverlapBefore:
2017 if( *pEnd == *pREnd )
2019 pRedl->PushData( *pNewRedl );
2020 pNewRedl->SetEnd( *pRStt, pEnd );
2021 if( IsHideChanges( meRedlineFlags ))
2023 maRedlineTable.Insert(pNewRedl);
2024 pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
2025 maRedlineTable.Remove( pNewRedl );
2028 else
2030 pNew = new SwRangeRedline( *pRedl );
2031 pNew->PushData( *pNewRedl );
2032 pNew->SetEnd( *pEnd );
2033 pNewRedl->SetEnd( *pRStt, pEnd );
2034 pRedl->SetStart( *pNew->End(), pRStt ) ;
2035 // re-insert
2036 maRedlineTable.Remove( n );
2037 maRedlineTable.Insert( pRedl );
2038 bDec = true;
2041 break;
2043 case SwComparePosition::OverlapBehind:
2045 if( *pStart == *pRStt )
2047 pRedl->PushData( *pNewRedl );
2048 pNewRedl->SetStart( *pREnd, pStart );
2049 if( IsHideChanges( meRedlineFlags ))
2051 maRedlineTable.Insert( pNewRedl );
2052 pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
2053 maRedlineTable.Remove( pNewRedl );
2056 else
2058 pNew = new SwRangeRedline( *pRedl );
2059 pNew->PushData( *pNewRedl );
2060 pNew->SetStart( *pStart );
2061 pNewRedl->SetStart( *pREnd, pStart );
2062 pRedl->SetEnd( *pNew->Start(), pREnd );
2063 if( !pRedl->HasValidRange() )
2065 // re-insert
2066 maRedlineTable.Remove( n );
2067 maRedlineTable.Insert( pRedl );
2071 break;
2072 default:
2073 break;
2076 // insert the pNew part (if it exists)
2077 if( pNew )
2079 maRedlineTable.Insert( pNew );
2081 // pNew must be deleted if Insert() wasn't
2082 // successful. But that can't happen, since pNew is
2083 // part of the original pRedl redline.
2084 // OSL_ENSURE( bRet, "Can't insert existing redline?" );
2086 // restart (now with pRedl being split up)
2087 n = 0;
2088 bDec = true;
2092 break;
2094 case RedlineType::Format:
2095 switch( eCmpPos )
2097 case SwComparePosition::OverlapBefore:
2098 pRedl->SetStart( *pEnd, pRStt );
2099 // re-insert
2100 maRedlineTable.Remove( n );
2101 maRedlineTable.Insert( pRedl, n );
2102 bDec = true;
2103 break;
2105 case SwComparePosition::OverlapBehind:
2106 pRedl->SetEnd( *pStart, pREnd );
2107 break;
2109 case SwComparePosition::Equal:
2110 case SwComparePosition::Outside:
2111 // Overlaps the current one completely or has the
2112 // same dimension, delete the old one
2113 maRedlineTable.DeleteAndDestroy( n );
2114 bDec = true;
2115 break;
2117 case SwComparePosition::Inside:
2118 // Overlaps the current one completely,
2119 // split or shorten the new one
2120 if( *pEnd != *pREnd )
2122 if( *pEnd != *pRStt )
2124 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
2125 pNew->SetStart( *pEnd );
2126 pRedl->SetEnd( *pStart, pREnd );
2127 if( ( *pStart == *pRStt ) &&
2128 ( pRedl->GetContentIdx() == nullptr ) )
2129 maRedlineTable.DeleteAndDestroy( n );
2130 AppendRedline( pNew, bCallDelete );
2131 n = 0; // re-initialize
2132 bDec = true;
2135 else
2136 pRedl->SetEnd( *pStart, pREnd );
2137 break;
2138 default:
2139 break;
2141 break;
2142 default:
2143 break;
2145 break;
2147 case RedlineType::Format:
2148 switch( pRedl->GetType() )
2150 case RedlineType::Insert:
2151 case RedlineType::Delete:
2152 switch( eCmpPos )
2154 case SwComparePosition::OverlapBefore:
2155 pNewRedl->SetEnd( *pRStt, pEnd );
2156 break;
2158 case SwComparePosition::OverlapBehind:
2159 pNewRedl->SetStart( *pREnd, pStart );
2160 break;
2162 case SwComparePosition::Equal:
2163 case SwComparePosition::Inside:
2164 delete pNewRedl;
2165 pNewRedl = nullptr;
2167 MaybeNotifyRedlineModification(*pRedl, m_rDoc);
2168 break;
2170 case SwComparePosition::Outside:
2171 // Overlaps the current one completely,
2172 // split or shorten the new one
2173 if (*pEnd == *pREnd)
2175 pNewRedl->SetEnd(*pRStt, pEnd);
2177 else if (*pStart == *pRStt)
2179 pNewRedl->SetStart(*pREnd, pStart);
2181 else
2183 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
2184 pNew->SetStart( *pREnd );
2185 pNewRedl->SetEnd( *pRStt, pEnd );
2186 AppendRedline( pNew, bCallDelete );
2187 n = 0; // re-initialize
2188 bDec = true;
2190 break;
2191 default:
2192 break;
2194 break;
2195 case RedlineType::Format:
2196 switch( eCmpPos )
2198 case SwComparePosition::Outside:
2199 case SwComparePosition::Equal:
2201 // Overlaps the current one completely or has the
2202 // same dimension, delete the old one
2203 maRedlineTable.DeleteAndDestroy( n );
2204 bDec = true;
2206 break;
2208 case SwComparePosition::Inside:
2209 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2210 pRedl->CanCombine( *pNewRedl ))
2212 // own one can be ignored completely
2213 delete pNewRedl;
2214 pNewRedl = nullptr;
2216 MaybeNotifyRedlineModification(*pRedl, m_rDoc);
2218 else if( *pREnd == *pEnd )
2219 // or else only shorten the current one
2220 pRedl->SetEnd( *pStart, pREnd );
2221 else if( *pRStt == *pStart )
2223 // or else only shorten the current one
2224 pRedl->SetStart( *pEnd, pRStt );
2225 // re-insert
2226 maRedlineTable.Remove( n );
2227 maRedlineTable.Insert( pRedl, n );
2228 bDec = true;
2230 else
2232 // If it lies completely within the current one
2233 // we need to split it
2234 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
2235 pNew->SetStart( *pEnd );
2236 pRedl->SetEnd( *pStart, pREnd );
2237 AppendRedline( pNew, bCallDelete );
2238 n = 0; // re-initialize
2239 bDec = true;
2241 break;
2243 case SwComparePosition::OverlapBefore:
2244 case SwComparePosition::OverlapBehind:
2245 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2246 pRedl->CanCombine( *pNewRedl ))
2248 // If that's the case we can merge it, meaning
2249 // the new one covers this well
2250 if( SwComparePosition::OverlapBehind == eCmpPos )
2251 pNewRedl->SetStart( *pRStt, pStart );
2252 else
2253 pNewRedl->SetEnd( *pREnd, pEnd );
2254 maRedlineTable.DeleteAndDestroy( n );
2255 bDec = false;
2257 else if( SwComparePosition::OverlapBehind == eCmpPos )
2258 pNewRedl->SetStart( *pREnd, pStart );
2259 else
2260 pNewRedl->SetEnd( *pRStt, pEnd );
2261 break;
2263 case SwComparePosition::CollideEnd:
2264 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2265 pRedl->CanCombine( *pNewRedl ) &&
2266 (n == 0 || *maRedlineTable[ n-1 ]->End() < *pStart))
2268 // If that's the case we can merge it, meaning
2269 // the new one covers this well
2270 pNewRedl->SetEnd( *pREnd, pEnd );
2271 maRedlineTable.DeleteAndDestroy( n );
2272 bDec = true;
2274 break;
2275 case SwComparePosition::CollideStart:
2276 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2277 pRedl->CanCombine( *pNewRedl ) &&
2278 (n+1 >= maRedlineTable.size() ||
2279 (*maRedlineTable[ n+1 ]->Start() >= *pEnd &&
2280 *maRedlineTable[ n+1 ]->Start() != *pREnd)))
2282 // If that's the case we can merge it, meaning
2283 // the new one covers this well
2284 pNewRedl->SetStart( *pRStt, pStart );
2285 maRedlineTable.DeleteAndDestroy( n );
2286 bDec = true;
2288 break;
2289 default:
2290 break;
2292 break;
2293 default:
2294 break;
2296 break;
2298 case RedlineType::FmtColl:
2299 // How should we behave here?
2300 // insert as is
2301 break;
2302 default:
2303 break;
2307 if( pNewRedl )
2309 if( ( *pStart == *pEnd ) &&
2310 ( pNewRedl->GetContentIdx() == nullptr ) )
2311 { // Do not insert empty redlines
2312 delete pNewRedl;
2313 pNewRedl = nullptr;
2315 else
2317 if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2319 if ( pStart->GetContentIndex() != 0 )
2321 // tdf#119571 update the style of the joined paragraph
2322 // after a partially deleted paragraph to show its correct style
2323 // in "Show changes" mode, too. All removed paragraphs
2324 // get the style of the first (partially deleted) paragraph
2325 // to avoid text insertion with bad style in the deleted
2326 // area later (except paragraphs of the removed tables).
2328 SwContentNode* pDelNd = pStart->GetNode().GetContentNode();
2329 // start copying the style of the first paragraph from the end of the range
2330 SwContentNode* pTextNd = pEnd->GetNode().GetContentNode();
2331 SwNodeIndex aIdx( pEnd->GetNode() );
2332 bool bFirst = true;
2334 while (pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
2336 if( pTextNd->IsTextNode() )
2338 SwPosition aPos(aIdx);
2340 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2342 bCompress = true;
2344 // split redline to store ExtraData per paragraphs
2345 SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
2346 pPar->SetStart( aPos );
2347 pNewRedl->SetEnd( aPos );
2349 // get extradata for reset formatting of the modified paragraph
2350 std::unique_ptr<SwRedlineExtraData_FormatColl> pExtraData = lcl_CopyStyle(aPos, *pStart, false);
2351 if (pExtraData)
2353 if (!bFirst)
2354 pExtraData->SetFormatAll(false);
2355 pPar->SetExtraData( pExtraData.get() );
2358 // skip empty redlines without ExtraData
2359 // FIXME: maybe checking pExtraData is redundant here
2360 if ( pExtraData || *pPar->Start() != *pPar->End() )
2361 maRedlineTable.Insert( pPar );
2362 else
2363 delete pPar;
2366 // modify paragraph formatting
2367 lcl_CopyStyle(*pStart, aPos);
2370 if (bFirst)
2371 bFirst = false;
2373 // Jump to the previous paragraph and if needed, skip paragraphs of
2374 // the removed table(s) in the range to avoid leaving empty tables
2375 // because of the non-continuous redline range over the table.
2376 // FIXME: this is not enough for tables with inner redlines, where
2377 // tracked deletion of the text containing such a table leaves an
2378 // empty table at the place of the table (a problem inherited from OOo).
2379 pTextNd = nullptr;
2380 while( --aIdx > *pDelNd && !aIdx.GetNode().IsContentNode() )
2382 // possible table end
2383 if( aIdx.GetNode().IsEndNode() && aIdx.GetNode().FindTableNode() )
2385 SwNodeIndex aIdx2 = aIdx;
2386 // search table start and skip table paragraphs
2387 while ( pDelNd->GetIndex() < aIdx2.GetIndex() )
2389 SwTableNode* pTable = aIdx2.GetNode().GetTableNode();
2390 if( pTable &&
2391 pTable->EndOfSectionNode()->GetIndex() == aIdx.GetIndex() )
2393 aIdx = aIdx2;
2394 break;
2396 --aIdx2;
2401 if (aIdx.GetNode().IsContentNode())
2402 pTextNd = aIdx.GetNode().GetContentNode();
2406 // delete tables of the deletion explicitly, to avoid
2407 // remaining empty tables after accepting the rejection
2408 // and visible empty tables in Hide Changes mode
2409 // (this was the case, if tables have already contained
2410 // other tracked changes)
2411 // FIXME: because of recursive nature of AppendRedline,
2412 // this doesn't work for selections with multiple tables
2413 if ( m_rDoc.GetIDocumentUndoRedo().DoesUndo() )
2415 SwNodeIndex aSttIdx( pStart->GetNode() );
2416 SwNodeIndex aEndIdx( pEnd->GetNode() );
2417 while ( aSttIdx < aEndIdx )
2419 if ( aSttIdx.GetNode().IsTableNode() )
2421 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
2422 SwCursor aCursor( SwPosition(aSttIdx), nullptr );
2423 m_rDoc.SetRowNotTracked( aCursor, aHasTextChangesOnly, /*bAll=*/true );
2425 ++aSttIdx;
2429 bool const ret = maRedlineTable.Insert( pNewRedl );
2430 assert(ret || !pNewRedl);
2431 if (ret && !pNewRedl)
2433 bMerged = true; // treat InsertWithValidRanges as "merge"
2438 // If we deleted moved redlines, and there was only 1 MoveID, then we should use that
2439 // We overwrite those that was given right now, so it cannot be deeper under other redline
2440 if (nMoveIDToDelete > 1 && deletedMoveIDs.size() == 1)
2442 sal_uInt32 nNewMoveID = *(deletedMoveIDs.begin());
2443 if (nNewMoveID > 1) // MoveID==1 is for old, unrecognised moves, leave them alone
2445 for (n = 0; n < maRedlineTable.size(); ++n)
2447 if (maRedlineTable[n]->GetMoved() == nMoveIDToDelete)
2449 maRedlineTable[n]->SetMoved(nNewMoveID);
2455 if( bCompress )
2456 CompressRedlines(nStartPos);
2458 CHECK_REDLINE( *this )
2460 return (nullptr != pNewRedl)
2461 ? AppendResult::APPENDED
2462 : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
2465 bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
2467 // #TODO - equivalent for 'SwTableRowRedline'
2469 CHECK_REDLINE( this )
2472 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2474 // #TODO - equivalent for 'SwTableRowRedline'
2476 pNewRedl->InvalidateRange();
2479 // Make equivalent of 'AppendRedline' checks inside here too
2481 maExtraRedlineTable.Insert( pNewRedl );
2483 else
2485 // TO DO - equivalent for 'SwTableRowRedline'
2487 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2489 RedlineFlags eOld = meRedlineFlags;
2490 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2491 // The ShowMode needs to be retained!
2492 meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2493 DeleteAndJoin( *pNewRedl );
2494 meRedlineFlags = eOld;
2496 delete pNewRedl, pNewRedl = 0;
2499 // #TODO - equivalent for 'SwTableRowRedline'
2501 CHECK_REDLINE( this )
2504 return nullptr != pNewRedl;
2507 bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
2509 // #TODO - equivalent for 'SwTableCellRedline'
2511 CHECK_REDLINE( this )
2514 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2516 // #TODO - equivalent for 'SwTableCellRedline'
2518 pNewRedl->InvalidateRange();
2521 // Make equivalent of 'AppendRedline' checks inside here too
2523 maExtraRedlineTable.Insert( pNewRedl );
2525 else
2527 // TO DO - equivalent for 'SwTableCellRedline'
2529 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2531 RedlineFlags eOld = meRedlineFlags;
2532 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2533 // The ShowMode needs to be retained!
2534 meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2535 DeleteAndJoin( *pNewRedl );
2536 meRedlineFlags = eOld;
2538 delete pNewRedl, pNewRedl = 0;
2541 // #TODO - equivalent for 'SwTableCellRedline'
2543 CHECK_REDLINE( this )
2546 return nullptr != pNewRedl;
2549 void DocumentRedlineManager::CompressRedlines(size_t nStartIndex)
2551 CHECK_REDLINE( *this )
2553 void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
2554 RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
2555 if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
2556 pFnc = &SwRangeRedline::Show;
2557 else if (eShow == RedlineFlags::ShowInsert)
2558 pFnc = &SwRangeRedline::Hide;
2560 // Try to merge identical ones
2561 if (nStartIndex == 0)
2562 nStartIndex = 1;
2563 for( SwRedlineTable::size_type n = nStartIndex; n < maRedlineTable.size(); ++n )
2565 SwRangeRedline* pPrev = maRedlineTable[ n-1 ],
2566 * pCur = maRedlineTable[ n ];
2567 auto [pPrevStt,pPrevEnd] = pPrev->StartEnd();
2568 auto [pCurStt, pCurEnd] = pCur->StartEnd();
2570 if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
2571 pPrevStt->GetNode().StartOfSectionNode() ==
2572 pCurEnd->GetNode().StartOfSectionNode() &&
2573 !pCurEnd->GetNode().StartOfSectionNode()->IsTableNode() )
2575 // we then can merge them
2576 SwRedlineTable::size_type nPrevIndex = n-1;
2577 pPrev->Show(0, nPrevIndex);
2578 pCur->Show(0, n);
2580 pPrev->SetEnd( *pCur->End() );
2581 maRedlineTable.DeleteAndDestroy( n );
2582 --n;
2583 if( pFnc )
2584 (pPrev->*pFnc)(0, nPrevIndex, false);
2587 CHECK_REDLINE( *this )
2589 // #TODO - add 'SwExtraRedlineTable' also ?
2592 bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
2594 if (maRedlineTable.empty())
2595 return false;
2596 auto [pStart, pEnd] = rRange.StartEnd(); // SwPosition*
2597 // tdf#144208 this happens a lot during load of some DOCX files.
2598 if (*pEnd > maRedlineTable.GetMaxEndPos())
2599 return false;
2600 bool bChg = false;
2601 SwRedlineTable::size_type n = 0;
2602 //FIXME overlapping problem GetRedline( *pStart, &n );
2603 while (n < maRedlineTable.size())
2605 SwRangeRedline * pRedline = maRedlineTable[ n ];
2606 auto [pRedlineStart, pRedlineEnd] = pRedline->StartEnd();
2607 if (*pRedlineStart <= *pStart && *pEnd <= *pRedlineEnd)
2609 bChg = true;
2610 int nn = 0;
2611 if (*pStart == *pRedlineStart)
2612 nn += 1;
2613 if (*pEnd == *pRedlineEnd)
2614 nn += 2;
2616 SwRangeRedline* pNew = nullptr;
2617 switch( nn )
2619 case 0:
2620 pNew = new SwRangeRedline( *pRedline );
2621 pRedline->SetEnd( *pStart, pRedlineEnd );
2622 pNew->SetStart( *pEnd );
2623 break;
2625 case 1:
2626 *pRedlineStart = *pEnd;
2627 break;
2629 case 2:
2630 *pRedlineEnd = *pStart;
2631 break;
2633 case 3:
2634 pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2635 maRedlineTable.DeleteAndDestroy( n );
2636 // loop again with the same n to iterate to the next entry
2637 pRedline = nullptr;
2638 break;
2641 if (pRedline)
2643 if (!pRedline->HasValidRange())
2645 // re-insert
2646 maRedlineTable.Remove( n );
2647 maRedlineTable.Insert( pRedline, n );
2650 if (pNew)
2651 maRedlineTable.Insert(pNew, n);
2654 else if (*pEnd < *pRedlineStart)
2655 break;
2656 if (pRedline)
2657 ++n;
2659 return bChg;
2661 // #TODO - add 'SwExtraRedlineTable' also ?
2664 bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
2665 RedlineType nDelType )
2667 if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
2668 return false;
2670 bool bChg = false;
2672 if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2674 std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
2675 if( pUndo->GetRedlSaveCount() )
2677 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2681 auto [pStart, pEnd] = rRange.StartEnd(); // SwPosition*
2682 SwRedlineTable::size_type n = 0;
2683 GetRedline( *pStart, &n );
2684 while (n < maRedlineTable.size())
2686 SwRangeRedline* pRedl = maRedlineTable[ n ];
2687 if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
2689 ++n;
2690 continue;
2693 auto [pRStt, pREnd] = pRedl->StartEnd(); // SwPosition*
2694 switch( ComparePosition( *pStart, *pEnd, *pRStt, *pREnd ) )
2696 case SwComparePosition::Equal:
2697 case SwComparePosition::Outside:
2698 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2699 maRedlineTable.DeleteAndDestroy( n );
2700 bChg = true;
2701 break;
2703 case SwComparePosition::OverlapBefore:
2704 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2705 pRedl->SetStart( *pEnd, pRStt );
2706 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2707 // re-insert
2708 maRedlineTable.Remove( n );
2709 maRedlineTable.Insert( pRedl );
2710 break;
2712 case SwComparePosition::OverlapBehind:
2713 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2714 pRedl->SetEnd( *pStart, pREnd );
2715 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2716 if( !pRedl->HasValidRange() )
2718 // re-insert
2719 maRedlineTable.Remove( n );
2720 maRedlineTable.Insert( pRedl );
2722 else
2723 ++n;
2724 break;
2726 case SwComparePosition::Inside:
2728 // this one needs to be split
2729 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2730 if( *pRStt == *pStart )
2732 pRedl->SetStart( *pEnd, pRStt );
2733 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2734 // re-insert
2735 maRedlineTable.Remove( n );
2736 maRedlineTable.Insert( pRedl );
2738 else
2740 SwRangeRedline* pCpy;
2741 if( *pREnd != *pEnd )
2743 pCpy = new SwRangeRedline( *pRedl );
2744 pCpy->SetStart( *pEnd );
2745 pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
2747 else
2748 pCpy = nullptr;
2749 pRedl->SetEnd( *pStart, pREnd );
2750 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2751 if( !pRedl->HasValidRange() )
2753 // re-insert
2754 maRedlineTable.Remove( n );
2755 maRedlineTable.Insert( pRedl );
2757 else
2758 ++n;
2759 if( pCpy )
2760 maRedlineTable.Insert( pCpy );
2763 break;
2765 case SwComparePosition::CollideEnd:
2766 // remove (not hidden) empty redlines created for fixing tdf#119571
2767 // (Note: hidden redlines are all empty, i.e. start and end are equal.)
2768 if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
2770 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2771 maRedlineTable.DeleteAndDestroy( n );
2772 bChg = true;
2773 break;
2775 [[fallthrough]];
2777 case SwComparePosition::Before:
2778 n = maRedlineTable.size() + 1;
2779 break;
2780 default:
2781 ++n;
2782 break;
2786 if( bChg )
2787 m_rDoc.getIDocumentState().SetModified();
2789 return bChg;
2791 // #TODO - add 'SwExtraRedlineTable' also ?
2794 bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
2795 RedlineType nDelType )
2797 SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
2798 return DeleteRedline(aTemp, bSaveInUndo, nDelType);
2801 SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
2803 const SwNodeOffset nNdIdx = rNd.GetIndex();
2804 // if the table only contains good (i.e. non-overlapping) data, we can do a binary search
2805 if (!maRedlineTable.HasOverlappingElements())
2807 // binary search to the first redline with end >= the needle
2808 auto it = std::lower_bound(maRedlineTable.begin(), maRedlineTable.end(), rNd,
2809 [&nNdIdx](const SwRangeRedline* lhs, const SwNode& /*rhs*/)
2811 return lhs->End()->GetNodeIndex() < nNdIdx;
2813 for( ; it != maRedlineTable.end(); ++it)
2815 const SwRangeRedline* pTmp = *it;
2816 auto [pStart, pEnd] = pTmp->StartEnd(); // SwPosition*
2817 SwNodeOffset nStart = pStart->GetNodeIndex(),
2818 nEnd = pEnd->GetNodeIndex();
2820 if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
2821 nStart <= nNdIdx && nNdIdx <= nEnd )
2822 return std::distance(maRedlineTable.begin(), it);
2824 if( nStart > nNdIdx )
2825 break;
2828 else
2830 for( auto it = maRedlineTable.begin(), itEnd = maRedlineTable.end(); it != itEnd; ++it )
2832 const SwRangeRedline* pTmp = *it;
2833 SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(),
2834 nMk = pTmp->GetMark()->GetNodeIndex();
2835 if( nPt < nMk )
2836 std::swap( nMk, nPt );
2838 if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
2839 nMk <= nNdIdx && nNdIdx <= nPt )
2840 return std::distance(maRedlineTable.begin(), it);
2842 if( nMk > nNdIdx )
2843 break;
2846 return SwRedlineTable::npos;
2848 // #TODO - add 'SwExtraRedlineTable' also ?
2851 SwRedlineTable::size_type
2852 DocumentRedlineManager::GetRedlineEndPos(SwRedlineTable::size_type nStartPos, const SwNode& rNd,
2853 RedlineType nType) const
2855 //if the start is already invalid
2856 if (nStartPos >= maRedlineTable.size())
2857 return nStartPos;
2859 const SwNodeOffset nNdIdx = rNd.GetIndex();
2860 SwRedlineTable::size_type nEndPos = nStartPos;
2861 SwRedlineTable::size_type nEndPosTry = nEndPos + 1;
2863 while (nEndPosTry < maRedlineTable.size()
2864 && maRedlineTable[nEndPosTry]->Start()->GetNodeIndex() <= nNdIdx)
2866 if (RedlineType::Any == nType || nType == maRedlineTable[nEndPosTry]->GetType())
2868 nEndPos = nEndPosTry;
2870 nEndPosTry++;
2872 return nEndPos;
2875 void DocumentRedlineManager::UpdateRedlineContentNode(SwRedlineTable::size_type nStartPos,
2876 SwRedlineTable::size_type nEndPos) const
2878 for (SwRedlineTable::size_type n = nStartPos; n <= nEndPos; ++n)
2880 //just in case we got wrong input
2881 if (n >= maRedlineTable.size())
2882 return;
2884 SwPosition* pStart = maRedlineTable[n]->Start();
2885 SwPosition* pEnd = maRedlineTable[n]->End();
2886 SwContentNode* pCont = pStart->GetNode().GetContentNode();
2887 if (pCont)
2889 pStart->nContent.Assign(pCont, pStart->nContent.GetIndex());
2891 pCont = pEnd->GetNode().GetContentNode();
2892 if (pCont)
2894 pEnd->nContent.Assign(pCont, pEnd->nContent.GetIndex());
2899 bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const
2901 SwPosition currentStart(*rPam.Start());
2902 SwPosition currentEnd(*rPam.End());
2903 const SwNode& rEndNode(currentEnd.GetNode());
2905 for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->GetNode(), nType );
2906 n < maRedlineTable.size(); ++n )
2908 const SwRangeRedline* pTmp = maRedlineTable[ n ];
2910 if ( pTmp->Start()->GetNode() > rEndNode )
2911 break;
2913 if( RedlineType::Any != nType && nType != pTmp->GetType() )
2914 continue;
2916 // redline over the range
2917 if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
2918 // starting or ending within the range
2919 ( !bStartOrEndInRange ||
2920 ( currentStart < *pTmp->Start() || *pTmp->End() < currentEnd ) ) )
2922 return true;
2925 return false;
2928 const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
2929 SwRedlineTable::size_type* pFndPos ) const
2931 SwRedlineTable::size_type nO = maRedlineTable.size(), nM, nU = 0;
2932 if( nO > 0 )
2934 nO--;
2935 while( nU <= nO )
2937 nM = nU + ( nO - nU ) / 2;
2938 const SwRangeRedline* pRedl = maRedlineTable[ nM ];
2939 auto [pStart, pEnd] = pRedl->StartEnd();
2940 if( pEnd == pStart
2941 ? *pStart == rPos
2942 : ( *pStart <= rPos && rPos < *pEnd ) )
2944 while( nM && rPos == *maRedlineTable[ nM - 1 ]->End() &&
2945 rPos == *maRedlineTable[ nM - 1 ]->Start() )
2947 --nM;
2948 pRedl = maRedlineTable[ nM ];
2950 // if there are format and insert changes in the same position
2951 // show insert change first.
2952 // since the redlines are sorted by position, only check the redline
2953 // before and after the current redline
2954 if( RedlineType::Format == pRedl->GetType() )
2956 if( nM && rPos >= *maRedlineTable[ nM - 1 ]->Start() &&
2957 rPos <= *maRedlineTable[ nM - 1 ]->End() &&
2958 ( RedlineType::Insert == maRedlineTable[ nM - 1 ]->GetType() ) )
2960 --nM;
2961 pRedl = maRedlineTable[ nM ];
2963 else if( ( nM + 1 ) <= nO && rPos >= *maRedlineTable[ nM + 1 ]->Start() &&
2964 rPos <= *maRedlineTable[ nM + 1 ]->End() &&
2965 ( RedlineType::Insert == maRedlineTable[ nM + 1 ]->GetType() ) )
2967 ++nM;
2968 pRedl = maRedlineTable[ nM ];
2972 if( pFndPos )
2973 *pFndPos = nM;
2974 return pRedl;
2976 else if( *pEnd <= rPos )
2977 nU = nM + 1;
2978 else if( nM == 0 )
2980 if( pFndPos )
2981 *pFndPos = nU;
2982 return nullptr;
2984 else
2985 nO = nM - 1;
2988 if( pFndPos )
2989 *pFndPos = nU;
2990 return nullptr;
2992 // #TODO - add 'SwExtraRedlineTable' also ?
2995 bool DocumentRedlineManager::AcceptRedlineRange(SwRedlineTable::size_type nPosOrigin,
2996 SwRedlineTable::size_type& nPosStart,
2997 SwRedlineTable::size_type& nPosEnd,
2998 bool bCallDelete)
3000 bool bRet = false;
3002 SwRangeRedline* pTmp = maRedlineTable[nPosOrigin];
3003 SwRedlineTable::size_type nRdlIdx = nPosEnd + 1;
3004 SwRedlineData aOrigData = pTmp->GetRedlineData(0);
3006 SwNodeOffset nPamStartNI = maRedlineTable[nPosStart]->Start()->GetNodeIndex();
3007 sal_Int32 nPamStartCI = maRedlineTable[nPosStart]->Start()->GetContentIndex();
3008 SwNodeOffset nPamEndtNI = maRedlineTable[nPosEnd]->End()->GetNodeIndex();
3009 sal_Int32 nPamEndCI = maRedlineTable[nPosEnd]->End()->GetContentIndex();
3012 nRdlIdx--;
3013 pTmp = maRedlineTable[nRdlIdx];
3014 if (pTmp->Start()->GetNodeIndex() < nPamStartNI
3015 || (pTmp->Start()->GetNodeIndex() == nPamStartNI
3016 && pTmp->Start()->GetContentIndex() < nPamStartCI))
3017 break;
3019 if (pTmp->End()->GetNodeIndex() > nPamEndtNI
3020 || (pTmp->End()->GetNodeIndex() == nPamEndtNI
3021 && pTmp->End()->GetContentIndex() > nPamEndCI))
3024 else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData))
3026 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3028 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3029 std::make_unique<SwUndoAcceptRedline>(*pTmp));
3031 nPamEndtNI = pTmp->Start()->GetNodeIndex();
3032 nPamEndCI = pTmp->Start()->GetContentIndex();
3033 bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
3034 nRdlIdx++; //we will decrease it in the loop anyway.
3036 else if (aOrigData.GetType() == RedlineType::Insert
3037 && pTmp->GetType() == RedlineType::Delete && pTmp->GetStackCount() > 1
3038 && pTmp->GetType(1) == RedlineType::Insert
3039 && pTmp->GetRedlineData(1).CanCombineForAcceptReject(aOrigData))
3041 // The Insert redline we want to accept has a deletion redline too
3042 // we should leave the deletion redline, and only accept the inner insert.
3043 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3045 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3046 std::make_unique<SwUndoAcceptRedline>(*pTmp, 1));
3048 nPamEndtNI = pTmp->Start()->GetNodeIndex();
3049 nPamEndCI = pTmp->Start()->GetContentIndex();
3050 bRet |= lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1);
3051 nRdlIdx++; //we will decrease it in the loop anyway.
3053 } while (nRdlIdx > 0);
3054 return bRet;
3057 bool DocumentRedlineManager::AcceptMovedRedlines(sal_uInt32 nMovedID, bool bCallDelete)
3059 assert(nMovedID > 1); // 0, and 1 is reserved
3060 bool bRet = false;
3061 SwRedlineTable::size_type nRdlIdx = maRedlineTable.size();
3063 while (nRdlIdx > 0)
3065 nRdlIdx--;
3066 SwRangeRedline* pTmp = maRedlineTable[nRdlIdx];
3067 if (pTmp->GetMoved(0) == nMovedID
3068 || (pTmp->GetStackCount() > 1 && pTmp->GetMoved(1) == nMovedID))
3070 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3072 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3073 std::make_unique<SwUndoAcceptRedline>(*pTmp));
3076 if (pTmp->GetMoved(0) == nMovedID)
3077 bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
3078 else
3079 bRet |= lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1);
3081 nRdlIdx++; //we will decrease it in the loop anyway.
3084 return bRet;
3087 bool DocumentRedlineManager::AcceptRedline(SwRedlineTable::size_type nPos, bool bCallDelete,
3088 bool bRange)
3090 bool bRet = false;
3092 // Switch to visible in any case
3093 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
3094 (RedlineFlags::ShowMask & meRedlineFlags) )
3095 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
3097 SwRangeRedline* pTmp = maRedlineTable[ nPos ];
3098 bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized();
3100 pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
3101 pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
3102 if( pTmp->HasMark() && pTmp->IsVisible() )
3104 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3106 SwRewriter aRewriter;
3108 aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
3109 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
3112 int nLoopCnt = 2;
3113 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
3115 if (bRange && !nSeqNo && !bAnonym
3116 && !pTmp->Start()->GetNode().StartOfSectionNode()->IsTableNode())
3118 sal_uInt32 nMovedID = pTmp->GetMoved(0);
3119 if (nMovedID > 1)
3121 // Accept all redlineData with this unique move id
3122 bRet |= AcceptMovedRedlines(nMovedID, bCallDelete);
3124 else
3126 SwRedlineTable::size_type nPosStart = nPos;
3127 SwRedlineTable::size_type nPosEnd = nPos;
3129 maRedlineTable.getConnectedArea(nPos, nPosStart, nPosEnd, true);
3131 // Accept redlines between pPamStart-pPamEnd.
3132 // but only those that can be combined with the selected.
3133 bRet |= AcceptRedlineRange(nPos, nPosStart, nPosEnd, bCallDelete);
3136 else do {
3138 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3140 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3141 std::make_unique<SwUndoAcceptRedline>(*pTmp) );
3144 bRet |= lcl_AcceptRedline( maRedlineTable, nPos, bCallDelete );
3146 if( nSeqNo )
3148 if( SwRedlineTable::npos == nPos )
3149 nPos = 0;
3150 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
3151 ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
3152 : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
3153 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
3154 SwRedlineTable::npos != ( nFndPos =
3155 maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
3157 nPos = nFndPos;
3158 pTmp = maRedlineTable[ nPos ];
3160 else
3161 nLoopCnt = 0;
3163 else
3164 nLoopCnt = 0;
3166 } while (nLoopCnt);
3168 if( bRet )
3170 CompressRedlines();
3171 m_rDoc.getIDocumentState().SetModified();
3174 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3176 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
3179 return bRet;
3181 // #TODO - add 'SwExtraRedlineTable' also ?
3184 bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete, sal_Int8 nDepth )
3186 // Switch to visible in any case
3187 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
3188 (RedlineFlags::ShowMask & meRedlineFlags) )
3189 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
3191 // The Selection is only in the ContentSection. If there are Redlines
3192 // to Non-ContentNodes before or after that, then the Selections
3193 // expand to them.
3194 std::shared_ptr<SwUnoCursor> const pPam(m_rDoc.CreateUnoCursor(*rPam.GetPoint(), false));
3195 if (rPam.HasMark())
3197 pPam->SetMark();
3198 *pPam->GetMark() = *rPam.GetMark();
3200 lcl_AdjustRedlineRange(*pPam);
3202 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3204 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
3205 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3206 std::make_unique<SwUndoAcceptRedline>(*pPam, nDepth));
3209 int nRet = 0;
3210 if (nDepth == 0)
3212 nRet = lcl_AcceptRejectRedl(lcl_AcceptRedline, maRedlineTable, bCallDelete, *pPam);
3214 else
3216 // For now it is called only if it is an Insert redline in a delete redline.
3217 SwRedlineTable::size_type nRdlIdx = 0;
3218 maRedlineTable.FindAtPosition(*rPam.Start(), nRdlIdx);
3219 if (lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1))
3220 nRet = 1;
3222 if( nRet > 0 )
3224 CompressRedlines();
3225 m_rDoc.getIDocumentState().SetModified();
3227 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3229 OUString aTmpStr;
3232 SwRewriter aRewriter;
3233 aRewriter.AddRule(UndoArg1, OUString::number(nRet));
3234 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
3237 SwRewriter aRewriter;
3238 aRewriter.AddRule(UndoArg1, aTmpStr);
3240 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
3242 return nRet != 0;
3244 // #TODO - add 'SwExtraRedlineTable' also ?
3247 void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
3249 auto [pStart, pEnd] = rPam.StartEnd(); // SwPosition*
3251 const SwNodeOffset nSttIdx = pStart->GetNodeIndex();
3252 const SwNodeOffset nEndIdx = pEnd->GetNodeIndex();
3254 for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
3256 const SwRangeRedline* pTmp = maRedlineTable[ n ];
3257 SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(),
3258 nMk = pTmp->GetMark()->GetNodeIndex();
3259 if( nPt < nMk )
3260 std::swap( nMk, nPt );
3262 if( RedlineType::ParagraphFormat == pTmp->GetType() &&
3263 ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
3264 AcceptRedline( n, false );
3266 if( nMk > nEndIdx )
3267 break;
3271 bool DocumentRedlineManager::RejectRedlineRange(SwRedlineTable::size_type nPosOrigin,
3272 SwRedlineTable::size_type& nPosStart,
3273 SwRedlineTable::size_type& nPosEnd,
3274 bool bCallDelete)
3276 bool bRet = false;
3278 SwRangeRedline* pTmp = maRedlineTable[nPosOrigin];
3279 SwRedlineTable::size_type nRdlIdx = nPosEnd + 1;
3280 SwRedlineData aOrigData = pTmp->GetRedlineData(0);
3282 SwNodeOffset nPamStartNI = maRedlineTable[nPosStart]->Start()->GetNodeIndex();
3283 sal_Int32 nPamStartCI = maRedlineTable[nPosStart]->Start()->GetContentIndex();
3284 SwNodeOffset nPamEndtNI = maRedlineTable[nPosEnd]->End()->GetNodeIndex();
3285 sal_Int32 nPamEndCI = maRedlineTable[nPosEnd]->End()->GetContentIndex();
3288 nRdlIdx--;
3289 pTmp = maRedlineTable[nRdlIdx];
3290 if (pTmp->Start()->GetNodeIndex() < nPamStartNI
3291 || (pTmp->Start()->GetNodeIndex() == nPamStartNI
3292 && pTmp->Start()->GetContentIndex() < nPamStartCI))
3293 break;
3295 if (pTmp->End()->GetNodeIndex() > nPamEndtNI
3296 || (pTmp->End()->GetNodeIndex() == nPamEndtNI
3297 && pTmp->End()->GetContentIndex() > nPamEndCI))
3300 else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData))
3302 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3304 std::unique_ptr<SwUndoRejectRedline> pUndoRdl
3305 = std::make_unique<SwUndoRejectRedline>(*pTmp);
3306 #if OSL_DEBUG_LEVEL > 0
3307 pUndoRdl->SetRedlineCountDontCheck(true);
3308 #endif
3309 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl));
3311 nPamEndtNI = pTmp->Start()->GetNodeIndex();
3312 nPamEndCI = pTmp->Start()->GetContentIndex();
3313 bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete);
3314 nRdlIdx++; //we will decrease it in the loop anyway.
3316 else if (aOrigData.GetType() == RedlineType::Insert
3317 && pTmp->GetType() == RedlineType::Delete && pTmp->GetStackCount() > 1
3318 && pTmp->GetType(1) == RedlineType::Insert
3319 && pTmp->GetRedlineData(1).CanCombineForAcceptReject(aOrigData))
3321 // The Insert redline we want to reject has a deletion redline too
3322 // without the insert, the delete is meaningless
3323 // so we rather just accept the deletion redline
3324 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3326 std::unique_ptr<SwUndoRejectRedline> pUndoRdl
3327 = std::make_unique<SwUndoRejectRedline>(*pTmp, 1);
3328 #if OSL_DEBUG_LEVEL > 0
3329 pUndoRdl->SetRedlineCountDontCheck(true);
3330 #endif
3331 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl));
3333 nPamEndtNI = pTmp->Start()->GetNodeIndex();
3334 nPamEndCI = pTmp->Start()->GetContentIndex();
3335 bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
3336 nRdlIdx++; //we will decrease it in the loop anyway.
3339 } while (nRdlIdx > 0);
3340 return bRet;
3343 bool DocumentRedlineManager::RejectMovedRedlines(sal_uInt32 nMovedID, bool bCallDelete)
3345 assert(nMovedID > 1); // 0, and 1 is reserved
3346 bool bRet = false;
3347 SwRedlineTable::size_type nRdlIdx = maRedlineTable.size();
3349 while (nRdlIdx > 0)
3351 nRdlIdx--;
3352 SwRangeRedline* pTmp = maRedlineTable[nRdlIdx];
3353 if (pTmp->GetMoved(0) == nMovedID
3354 || (pTmp->GetStackCount() > 1 && pTmp->GetMoved(1) == nMovedID))
3356 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3358 std::unique_ptr<SwUndoRejectRedline> pUndoRdl
3359 = std::make_unique<SwUndoRejectRedline>(*pTmp);
3360 #if OSL_DEBUG_LEVEL > 0
3361 pUndoRdl->SetRedlineCountDontCheck(true);
3362 #endif
3363 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl));
3366 if (pTmp->GetMoved(0) == nMovedID)
3367 bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete);
3368 else
3369 bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
3371 nRdlIdx++; //we will decrease it in the loop anyway.
3374 return bRet;
3377 bool DocumentRedlineManager::RejectRedline(SwRedlineTable::size_type nPos,
3378 bool bCallDelete, bool bRange)
3380 bool bRet = false;
3382 // Switch to visible in any case
3383 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
3384 (RedlineFlags::ShowMask & meRedlineFlags) )
3385 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
3387 SwRangeRedline* pTmp = maRedlineTable[ nPos ];
3388 bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized();
3390 pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
3391 pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
3392 if( pTmp->HasMark() && pTmp->IsVisible() )
3394 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3396 SwRewriter aRewriter;
3398 aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
3399 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
3402 int nLoopCnt = 2;
3403 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
3405 if (bRange && !nSeqNo && !bAnonym
3406 && !pTmp->Start()->GetNode().StartOfSectionNode()->IsTableNode())
3408 sal_uInt32 nMovedID = pTmp->GetMoved(0);
3409 if (nMovedID > 1)
3411 // Reject all redlineData with this unique move id
3412 bRet |= RejectMovedRedlines(nMovedID, bCallDelete);
3414 else
3416 SwRedlineTable::size_type nPosStart = nPos;
3417 SwRedlineTable::size_type nPosEnd = nPos;
3418 maRedlineTable.getConnectedArea(nPos, nPosStart, nPosEnd, true);
3420 // Reject items between pPamStart-pPamEnd
3421 // but only those that can be combined with the selected.
3423 bRet |= RejectRedlineRange(nPos, nPosStart, nPosEnd, bCallDelete);
3426 else do {
3428 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3430 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3431 std::make_unique<SwUndoRejectRedline>( *pTmp ) );
3434 bRet |= lcl_RejectRedline( maRedlineTable, nPos, bCallDelete );
3436 if( nSeqNo )
3438 if( SwRedlineTable::npos == nPos )
3439 nPos = 0;
3440 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
3441 ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
3442 : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
3443 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
3444 SwRedlineTable::npos != ( nFndPos =
3445 maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
3447 nPos = nFndPos;
3448 pTmp = maRedlineTable[ nPos ];
3450 else
3451 nLoopCnt = 0;
3453 else
3454 nLoopCnt = 0;
3456 } while (nLoopCnt);
3458 if( bRet )
3460 CompressRedlines();
3461 m_rDoc.getIDocumentState().SetModified();
3464 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3466 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
3469 return bRet;
3471 // #TODO - add 'SwExtraRedlineTable' also ?
3474 bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete, sal_Int8 nDepth )
3476 // Switch to visible in any case
3477 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
3478 (RedlineFlags::ShowMask & meRedlineFlags) )
3479 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
3481 // The Selection is only in the ContentSection. If there are Redlines
3482 // to Non-ContentNodes before or after that, then the Selections
3483 // expand to them.
3484 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
3485 lcl_AdjustRedlineRange( aPam );
3487 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3489 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
3490 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam, nDepth) );
3493 int nRet = 0;
3494 if (nDepth == 0)
3496 nRet = lcl_AcceptRejectRedl(lcl_RejectRedline, maRedlineTable, bCallDelete, aPam);
3498 else
3500 // For now it is called only if it is an Insert redline in a delete redline.
3501 SwRedlineTable::size_type nRdlIdx = 0;
3502 maRedlineTable.FindAtPosition(*rPam.Start(), nRdlIdx);
3503 if (lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete))
3504 nRet = 1;
3507 if( nRet > 0 )
3509 CompressRedlines();
3510 m_rDoc.getIDocumentState().SetModified();
3512 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3514 OUString aTmpStr;
3517 SwRewriter aRewriter;
3518 aRewriter.AddRule(UndoArg1, OUString::number(nRet));
3519 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
3522 SwRewriter aRewriter;
3523 aRewriter.AddRule(UndoArg1, aTmpStr);
3525 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
3528 return nRet != 0;
3530 // #TODO - add 'SwExtraRedlineTable' also ?
3533 void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
3535 bool bSuccess = true;
3536 OUString sUndoStr;
3537 IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
3539 if (maRedlineTable.size() > 1)
3542 SwRewriter aRewriter;
3543 aRewriter.AddRule(UndoArg1, OUString::number(maRedlineTable.size()));
3544 sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
3547 SwRewriter aRewriter;
3548 aRewriter.AddRule(UndoArg1, sUndoStr);
3549 rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
3552 while (!maRedlineTable.empty() && bSuccess)
3554 if (bAccept)
3555 bSuccess = AcceptRedline(maRedlineTable.size() - 1, true);
3556 else
3557 bSuccess = RejectRedline(maRedlineTable.size() - 1, true);
3560 if (!sUndoStr.isEmpty())
3562 rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
3566 const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
3568 rPam.DeleteMark();
3569 rPam.SetMark();
3571 SwPosition& rSttPos = *rPam.GetPoint();
3572 SwPosition aSavePos( rSttPos );
3573 bool bRestart;
3575 // If the starting position points to the last valid ContentNode,
3576 // we take the next Redline in any case.
3577 SwRedlineTable::size_type n = 0;
3578 const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
3579 if( pFnd )
3581 const SwPosition* pEnd = pFnd->End();
3582 if( !pEnd->GetNode().IsContentNode() )
3584 SwNodeIndex aTmp( pEnd->GetNode() );
3585 SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
3586 if( !pCNd || ( aTmp == rSttPos.GetNode() &&
3587 pCNd->Len() == rSttPos.GetContentIndex() ))
3588 pFnd = nullptr;
3590 if( pFnd )
3591 rSttPos = *pFnd->End();
3594 do {
3595 bRestart = false;
3597 for( ; !pFnd && n < maRedlineTable.size(); ++n )
3599 pFnd = maRedlineTable[ n ];
3600 if( pFnd->HasMark() && pFnd->IsVisible() )
3602 *rPam.GetMark() = *pFnd->Start();
3603 rSttPos = *pFnd->End();
3604 break;
3606 else
3607 pFnd = nullptr;
3610 if( pFnd )
3612 // Merge all of the same type and author that are
3613 // consecutive into one Selection.
3614 const SwPosition* pPrevEnd = pFnd->End();
3615 while( ++n < maRedlineTable.size() )
3617 const SwRangeRedline* pTmp = maRedlineTable[ n ];
3618 if( pTmp->HasMark() && pTmp->IsVisible() )
3620 const SwPosition *pRStt;
3621 if( pFnd->GetType() != pTmp->GetType() ||
3622 pFnd->GetAuthor() != pTmp->GetAuthor() )
3623 break;
3624 pRStt = pTmp->Start();
3625 if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
3627 pPrevEnd = pTmp->End();
3628 rSttPos = *pPrevEnd;
3630 else
3631 break;
3636 if( pFnd )
3638 const SwRangeRedline* pSaveFnd = pFnd;
3640 SwContentNode* pCNd;
3641 SwPosition* pPos = rPam.GetMark();
3642 if( !pPos->GetNode().IsContentNode() )
3644 pCNd = SwNodes::GoNextSection(pPos);
3645 if( pCNd )
3647 if( pPos->GetNode() <= rPam.GetPoint()->GetNode() )
3648 pPos->Assign( *pCNd, 0 );
3649 else
3650 pFnd = nullptr;
3654 if( pFnd )
3656 pPos = rPam.GetPoint();
3657 if( !pPos->GetNode().IsContentNode() )
3659 pCNd = SwNodes::GoPrevSection( pPos );
3660 if( pCNd )
3662 if( pPos->GetNode() >= rPam.GetMark()->GetNode() )
3663 pPos->Assign( *pCNd, pCNd->Len() );
3664 else
3665 pFnd = nullptr;
3670 if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3672 if( n < maRedlineTable.size() )
3674 bRestart = true;
3675 *rPam.GetPoint() = *pSaveFnd->End();
3677 else
3679 rPam.DeleteMark();
3680 *rPam.GetPoint() = aSavePos;
3682 pFnd = nullptr;
3685 } while( bRestart );
3687 return pFnd;
3689 // #TODO - add 'SwExtraRedlineTable' also ?
3692 const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
3694 rPam.DeleteMark();
3695 rPam.SetMark();
3697 SwPosition& rSttPos = *rPam.GetPoint();
3698 SwPosition aSavePos( rSttPos );
3699 bool bRestart;
3701 // If the starting position points to the last valid ContentNode,
3702 // we take the previous Redline in any case.
3703 SwRedlineTable::size_type n = 0;
3704 const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
3705 if( pFnd )
3707 const SwPosition* pStart = pFnd->Start();
3708 if( !pStart->GetNode().IsContentNode() )
3710 SwNodeIndex aTmp( pStart->GetNode() );
3711 SwContentNode* pCNd = SwNodes::GoNextSection(&aTmp);
3712 if( !pCNd || ( aTmp == rSttPos.GetNode() &&
3713 !rSttPos.GetContentIndex() ))
3714 pFnd = nullptr;
3716 if( pFnd )
3717 rSttPos = *pFnd->Start();
3720 do {
3721 bRestart = false;
3723 while( !pFnd && 0 < n )
3725 pFnd = maRedlineTable[ --n ];
3726 if( pFnd->HasMark() && pFnd->IsVisible() )
3728 *rPam.GetMark() = *pFnd->End();
3729 rSttPos = *pFnd->Start();
3731 else
3732 pFnd = nullptr;
3735 if( pFnd )
3737 // Merge all of the same type and author that are
3738 // consecutive into one Selection.
3739 const SwPosition* pNextStt = pFnd->Start();
3740 while( 0 < n )
3742 const SwRangeRedline* pTmp = maRedlineTable[ --n ];
3743 if( pTmp->HasMark() && pTmp->IsVisible() )
3745 const SwPosition *pREnd;
3746 if( pFnd->GetType() == pTmp->GetType() &&
3747 pFnd->GetAuthor() == pTmp->GetAuthor() &&
3748 ( *pNextStt == *( pREnd = pTmp->End() ) ||
3749 IsPrevPos( *pREnd, *pNextStt )) )
3751 pNextStt = pTmp->Start();
3752 rSttPos = *pNextStt;
3754 else
3756 ++n;
3757 break;
3763 if( pFnd )
3765 const SwRangeRedline* pSaveFnd = pFnd;
3767 SwContentNode* pCNd;
3768 SwPosition* pPos = rPam.GetMark();
3769 if( !pPos->GetNode().IsContentNode() )
3771 pCNd = SwNodes::GoPrevSection( pPos );
3772 if( pCNd )
3774 if( pPos->GetNode() >= rPam.GetPoint()->GetNode() )
3775 pPos->Assign( *pCNd, pCNd->Len() );
3776 else
3777 pFnd = nullptr;
3781 if( pFnd )
3783 pPos = rPam.GetPoint();
3784 if( !pPos->GetNode().IsContentNode() )
3786 pCNd = SwNodes::GoNextSection(pPos);
3787 if( pCNd )
3789 if( pPos->GetNode() <= rPam.GetMark()->GetNode() )
3790 pPos->Assign( *pCNd, 0 );
3791 else
3792 pFnd = nullptr;
3797 if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3799 if( n )
3801 bRestart = true;
3802 *rPam.GetPoint() = *pSaveFnd->Start();
3804 else
3806 rPam.DeleteMark();
3807 *rPam.GetPoint() = aSavePos;
3809 pFnd = nullptr;
3812 } while( bRestart );
3814 return pFnd;
3816 // #TODO - add 'SwExtraRedlineTable' also ?
3819 // Set comment at the Redline
3820 bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
3822 bool bRet = false;
3823 auto [pStart, pEnd] = rPaM.StartEnd(); // SwPosition*
3824 SwRedlineTable::size_type n = 0;
3825 if( GetRedlineTable().FindAtPosition( *pStart, n ) )
3827 for( ; n < maRedlineTable.size(); ++n )
3829 bRet = true;
3830 SwRangeRedline* pTmp = maRedlineTable[ n ];
3831 if( pStart != pEnd && *pTmp->Start() > *pEnd )
3832 break;
3834 pTmp->SetComment( rS );
3835 if( *pTmp->End() >= *pEnd )
3836 break;
3839 if( bRet )
3840 m_rDoc.getIDocumentState().SetModified();
3842 return bRet;
3844 // #TODO - add 'SwExtraRedlineTable' also ?
3847 // Create a new author if necessary
3848 std::size_t DocumentRedlineManager::GetRedlineAuthor()
3850 return SwModule::get()->GetRedlineAuthor();
3853 /// Insert new author into the Table for the Readers etc.
3854 std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
3856 return SwModule::get()->InsertRedlineAuthor(rNew);
3859 void DocumentRedlineManager::UpdateRedlineAttr()
3861 const SwRedlineTable& rTable = GetRedlineTable();
3862 for(SwRangeRedline* pRedl : rTable)
3864 if( pRedl->IsVisible() )
3865 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
3868 // #TODO - add 'SwExtraRedlineTable' also ?
3871 const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
3873 return maRedlinePasswd;
3876 void DocumentRedlineManager::SetRedlinePassword(
3877 /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
3879 maRedlinePasswd = rNewPassword;
3880 m_rDoc.getIDocumentState().SetModified();
3883 /// Set comment text for the Redline, which is inserted later on via
3884 /// AppendRedline. Is used by Autoformat.
3885 /// A null pointer resets the mode. The pointer is not copied, so it
3886 /// needs to stay valid!
3887 void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
3889 m_rDoc.SetAutoFormatRedline( nullptr != pText );
3890 if( pText )
3892 moAutoFormatRedlnComment = *pText;
3894 else
3896 moAutoFormatRedlnComment.reset();
3899 mnAutoFormatRedlnCommentNo = nSeqNo;
3902 void DocumentRedlineManager::HideAll( bool bDeletion )
3904 const SwRedlineTable& rTable = GetRedlineTable();
3905 for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3907 SwRangeRedline* pRedline = rTable[i-1];
3908 if ( pRedline->GetType() == RedlineType::Delete )
3910 if ( bDeletion && pRedline->IsVisible() )
3912 pRedline->Hide(0, rTable.GetPos(pRedline), false);
3913 pRedline->Hide(1, rTable.GetPos(pRedline), false);
3915 else if ( !bDeletion && !pRedline->IsVisible() )
3917 pRedline->Show(0, rTable.GetPos(pRedline), true);
3918 pRedline->Show(1, rTable.GetPos(pRedline), true);
3921 else if ( pRedline->GetType() == RedlineType::Insert )
3923 if ( !bDeletion && pRedline->IsVisible() )
3925 pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
3926 pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
3928 else if ( bDeletion && !pRedline->IsVisible() )
3930 pRedline->Show(0, rTable.GetPos(pRedline), true);
3931 pRedline->Show(1, rTable.GetPos(pRedline), true);
3937 void DocumentRedlineManager::ShowAll()
3939 const SwRedlineTable& rTable = GetRedlineTable();
3940 for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3942 SwRangeRedline* pRedline = rTable[i-1];
3943 if ( !pRedline->IsVisible() )
3945 pRedline->Show(0, rTable.GetPos(pRedline), true);
3946 pRedline->Show(1, rTable.GetPos(pRedline), true);
3951 DocumentRedlineManager::~DocumentRedlineManager()
3957 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */