android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / DocumentRedlineManager.cxx
blob898590d6201b0b10d9facd7c2d6107aa1c9594c4
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 <doc.hxx>
24 #include <docsh.hxx>
25 #include <wrtsh.hxx>
26 #include <fmtfld.hxx>
27 #include <frmtool.hxx>
28 #include <IDocumentUndoRedo.hxx>
29 #include <IDocumentFieldsAccess.hxx>
30 #include <IDocumentLayoutAccess.hxx>
31 #include <IDocumentState.hxx>
32 #include <redline.hxx>
33 #include <UndoRedline.hxx>
34 #include <docary.hxx>
35 #include <ndtxt.hxx>
36 #include <unocrsr.hxx>
37 #include <ftnidx.hxx>
38 #include <authfld.hxx>
39 #include <strings.hrc>
40 #include <swmodule.hxx>
41 #include <osl/diagnose.h>
42 #include <editeng/prntitem.hxx>
44 using namespace com::sun::star;
46 #ifdef DBG_UTIL
48 #define ERROR_PREFIX "redline table corrupted: "
50 namespace
52 // helper function for lcl_CheckRedline
53 // 1. make sure that pPos->nContent points into pPos->nNode
54 // 2. check that position is valid and doesn't point after text
55 void lcl_CheckPosition( const SwPosition* pPos )
57 assert(dynamic_cast<SwContentIndexReg*>(&pPos->GetNode())
58 == pPos->GetContentNode());
60 SwTextNode* pTextNode = pPos->GetNode().GetTextNode();
61 if( pTextNode == nullptr )
63 assert(pPos->GetContentIndex() == 0);
65 else
67 assert(pPos->GetContentIndex() >= 0 && pPos->GetContentIndex() <= pTextNode->Len());
71 void lcl_CheckPam( const SwPaM* pPam )
73 assert(pPam);
74 lcl_CheckPosition( pPam->GetPoint() );
75 lcl_CheckPosition( pPam->GetMark() );
78 // check validity of the redline table. Checks redline bounds, and make
79 // sure the redlines are sorted and non-overlapping.
80 void lcl_CheckRedline( const IDocumentRedlineAccess& redlineAccess )
82 const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
84 // verify valid redline positions
85 for(SwRangeRedline* i : rTable)
86 lcl_CheckPam( i );
88 for(SwRangeRedline* j : rTable)
90 // check for empty redlines
91 // note: these can destroy sorting in SwTextNode::Update()
92 // if there's another one without mark on the same pos.
93 OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
94 ( j->GetContentIdx() != nullptr ),
95 ERROR_PREFIX "empty redline" );
98 // verify proper redline sorting
99 for( size_t n = 1; n < rTable.size(); ++n )
101 const SwRangeRedline* pPrev = rTable[ n-1 ];
102 const SwRangeRedline* pCurrent = rTable[ n ];
104 // check redline sorting
105 SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
106 ERROR_PREFIX "not sorted correctly" );
108 // check for overlapping redlines
109 SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
110 ERROR_PREFIX "overlapping redlines" );
113 assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
117 #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
119 #else
121 #define CHECK_REDLINE( pDoc )
123 #endif
125 namespace sw {
127 static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
129 auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
130 SwFieldIds::TableOfAuthorities, OUString(), false)));
131 if (pAuthType) // created on demand...
133 pAuthType->DelSequenceArray();
135 rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
136 rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
137 rIDFA.UpdateExpFields(nullptr, false);
138 rIDFA.UpdateRefFields();
141 void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
143 if (rDoc.IsClipBoard())
145 return;
147 // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
148 // the AppendFootnote/RemoveFootnote will do it by itself!
149 rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
150 SwPosition currentStart(*rPam.Start());
151 SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
152 while (!pStartNode)
154 // note: branch only taken for redlines, not fieldmarks
155 SwStartNode *const pTableOrSectionNode(
156 currentStart.GetNode().IsTableNode()
157 ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
158 : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
159 if ( !pTableOrSectionNode )
161 SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
162 return;
164 for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
166 pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
168 for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
170 if (pLayout->HasMergedParas())
172 if (pTableOrSectionNode->IsTableNode())
174 static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
176 else
178 static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
182 currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
183 pStartNode = currentStart.GetNode().GetTextNode();
185 if (currentStart < *rPam.End())
187 SwTextNode * pNode(pStartNode);
190 // deleted text node: remove it from "hidden" list
191 // to update numbering in Show Changes mode
192 SwPosition aPos( *pNode, pNode->Len() );
193 if ( pNode->GetNumRule() && aPos < *rPam.End() )
194 pNode->RemoveFromListRLHidden();
196 std::vector<SwTextFrame*> frames;
197 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
198 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
200 if (pFrame->getRootFrame()->HasMergedParas())
202 frames.push_back(pFrame);
204 // set anchored objects as deleted
205 pFrame->SetDrawObjsAsDeleted(true);
207 if (frames.empty())
209 auto const& layouts(rDoc.GetAllLayouts());
210 assert(std::none_of(layouts.begin(), layouts.end(),
211 [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
212 (void) layouts;
213 break;
215 auto eMode(sw::FrameMode::Existing);
216 SwTextNode * pLast(pNode);
217 for (SwTextFrame * pFrame : frames)
219 SwTextNode & rFirstNode(pFrame->GetMergedPara()
220 ? *pFrame->GetMergedPara()->pFirstNode
221 : *pNode);
222 assert(pNode == pStartNode
223 ? rFirstNode.GetIndex() <= pNode->GetIndex()
224 : &rFirstNode == pNode);
225 // clear old one first to avoid DelFrames confusing updates & asserts...
226 pFrame->SetMergedPara(nullptr);
227 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
228 *pFrame, rFirstNode, eMode));
229 eMode = sw::FrameMode::New; // Existing is not idempotent!
230 // the first node of the new redline is not necessarily the first
231 // node of the merged frame, there could be another redline nearby
232 sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
233 // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
234 if (pFrame->GetMergedPara())
236 pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
239 SwNodeIndex tmp(*pLast);
240 // skip over hidden sections!
241 pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
243 while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
245 // fields last - SwGetRefField::UpdateField requires up-to-date frames
246 UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
248 // update SwPostItMgr / notes in the margin
249 rDoc.GetDocShell()->Broadcast(
250 SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
253 void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
255 // tdf#147006 fieldmark command may be empty => do not call AppendAllObjs()
256 if (rDoc.IsClipBoard() || *rPam.GetPoint() == *rPam.GetMark())
258 return;
260 bool isAppendObjsCalled(false);
261 rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
262 SwPosition currentStart(*rPam.Start());
263 SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
264 while (!pStartNode)
266 // note: branch only taken for redlines, not fieldmarks
267 SwStartNode *const pTableOrSectionNode(
268 currentStart.GetNode().IsTableNode()
269 ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
270 : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
271 assert(pTableOrSectionNode); // known pathology
272 for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
274 pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
276 if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
278 // note: this will also create frames for all currently hidden flys
279 // because it calls AppendAllObjs
280 ::MakeFrames(&rDoc, currentStart.GetNode(), *pTableOrSectionNode->EndOfSectionNode());
281 isAppendObjsCalled = true;
283 currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
284 pStartNode = currentStart.GetNode().GetTextNode();
286 if (currentStart < *rPam.End())
288 SwTextNode * pNode(pStartNode);
291 // undeleted text node: add it to the "hidden" list
292 // to update numbering in Show Changes mode
293 SwPosition aPos( *pNode, pNode->Len() );
294 if ( pNode->GetNumRule() && aPos < *rPam.End() )
295 pNode->AddToListRLHidden();
297 std::vector<SwTextFrame*> frames;
298 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
299 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
301 if (pFrame->getRootFrame()->HasMergedParas())
303 frames.push_back(pFrame);
305 // set anchored objects as not deleted
306 pFrame->SetDrawObjsAsDeleted(false);
308 if (frames.empty())
310 // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
311 if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
313 auto const& layouts(rDoc.GetAllLayouts());
314 assert(std::none_of(layouts.begin(), layouts.end(),
315 [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
316 (void) layouts;
318 isAppendObjsCalled = true; // skip that!
319 break;
322 // no nodes can be unmerged by this - skip MakeFrames() etc.
323 if (rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode())
325 break; // continue with AppendAllObjs()
328 // first, call CheckParaRedlineMerge on the first paragraph,
329 // to init flag on new merge range (if any) + 1st node post the merge
330 auto eMode(sw::FrameMode::Existing);
331 SwTextNode * pLast(pNode);
332 for (SwTextFrame * pFrame : frames)
334 if (auto const pMergedPara = pFrame->GetMergedPara())
336 pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
337 assert(pNode == pStartNode
338 ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
339 : pMergedPara->pFirstNode == pNode);
340 // clear old one first to avoid DelFrames confusing updates & asserts...
341 SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
342 pFrame->SetMergedPara(nullptr);
343 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
344 *pFrame, rFirstNode, eMode));
345 eMode = sw::FrameMode::New; // Existing is not idempotent!
346 // update pNode so MakeFrames starts on 2nd node
347 pNode = &rFirstNode;
350 if (pLast != pNode)
352 // now start node until end of merge + 1 has proper flags; MakeFrames
353 // should pick up from the next node in need of frames by checking flags
354 SwNodeIndex const start(*pNode, +1);
355 SwNodeIndex const end(*pLast, +1); // end is exclusive
356 // note: this will also create frames for all currently hidden flys
357 // both on first and non-first nodes because it calls AppendAllObjs
358 ::MakeFrames(&rDoc, start.GetNode(), end.GetNode());
359 isAppendObjsCalled = true;
360 // re-use this to move flys that are now on the wrong frame, with end
361 // of redline as "second" node; the nodes between start and end should
362 // be complete with MakeFrames already
363 sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
365 SwNodeIndex tmp(*pLast);
366 // skip over hidden sections!
367 pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
369 while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
372 if (!isAppendObjsCalled)
373 { // recreate flys in the one node the hard way...
374 for (auto const& pLayout : rDoc.GetAllLayouts())
376 if (pLayout->HasMergedParas())
378 AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
379 break;
383 // fields last - SwGetRefField::UpdateField requires up-to-date frames
384 UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
386 // update SwPostItMgr / notes in the margin
387 rDoc.GetDocShell()->Broadcast(
388 SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
391 } // namespace sw
393 namespace
395 bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
397 const SwContentNode* pCNd;
398 if( 0 != rPos2.GetContentIndex() )
399 return false;
400 if( rPos2.GetNodeIndex() - 1 != rPos1.GetNodeIndex() )
401 return false;
402 pCNd = rPos1.GetNode().GetContentNode();
403 return pCNd && rPos1.GetContentIndex() == pCNd->Len();
406 // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
407 SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
409 SwTextNode* pToNode = rTo.GetNode().GetTextNode();
410 SwTextNode* pFromNode = rFrom.GetNode().GetTextNode();
411 if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
413 const SwPaM aPam(*pToNode);
414 SwDoc& rDoc = aPam.GetDoc();
415 // using Undo, copy paragraph style
416 SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
417 SwTextFormatColl* pToColl = pToNode->GetTextColl();
418 if (bCopy && pFromColl != pToColl)
419 rDoc.SetTextFormatColl(aPam, pFromColl);
421 // using Undo, remove direct paragraph formatting of the "To" paragraph,
422 // and apply here direct paragraph formatting of the "From" paragraph
423 SfxItemSetFixed<
424 RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
425 RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
426 RES_CNTNT, RES_FRMATR_END - 1>
427 aTmp(rDoc.GetAttrPool());
428 SfxItemSet aTmp2(aTmp);
430 pToNode->GetParaAttr(aTmp, 0, 0);
431 pFromNode->GetParaAttr(aTmp2, 0, 0);
433 bool bSameSet = aTmp == aTmp2;
435 if (!bSameSet)
437 for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
439 sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
440 if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
441 SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
442 aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
446 if (bCopy && !bSameSet)
447 rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
448 else if (!bCopy && (!bSameSet || pFromColl != pToColl))
449 return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
451 return nullptr;
454 // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
455 void lcl_DeleteTrackedTableRow ( const SwPosition* pPos )
457 const SwTableBox* pBox = pPos->GetNode().GetTableBox();
458 if ( !pBox )
459 return;
461 // tracked column deletion
463 const SvxPrintItem *pHasBoxTextChangesOnlyProp =
464 pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
465 // empty table cell with property "HasTextChangesOnly" = false
466 if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
468 SwCursor aCursor( *pPos, nullptr );
469 if ( pBox->IsEmpty() )
471 // tdf#155747 remove table cursor
472 pPos->GetDoc().GetDocShell()->GetWrtShell()->EnterStdMode();
473 // TODO check the other cells of the column
474 // before removing the column
475 pPos->GetDoc().DeleteCol( aCursor );
476 return;
478 else
480 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
481 pPos->GetDoc().SetBoxAttr( aCursor, aHasTextChangesOnly );
485 // tracked row deletion
487 const SwTableLine* pLine = pBox->GetUpper();
488 const SvxPrintItem *pHasTextChangesOnlyProp =
489 pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
490 // empty table row with property "HasTextChangesOnly" = false
491 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
493 if ( pLine->IsEmpty() )
495 SwCursor aCursor( *pPos, nullptr );
496 pPos->GetDoc().DeleteRow( aCursor );
498 else
500 // update property "HasTextChangesOnly"
501 SwRedlineTable::size_type nPos = 0;
502 (void)pLine->UpdateTextChangesOnly(nPos);
507 // at rejection of a deletion in a table, remove the tracking of the table row
508 // (also at accepting the last redline insertion of a tracked table row insertion)
509 void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos, bool bRejectDeletion )
511 const SwTableBox* pBox = pPos->GetNode().GetTableBox();
512 if ( !pBox )
513 return;
515 // tracked column deletion
517 const SvxPrintItem *pHasBoxTextChangesOnlyProp =
518 pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
519 // table cell property "HasTextChangesOnly" is set and its value is false
520 if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
522 SvxPrintItem aUnsetTracking(RES_PRINT, true);
523 SwCursor aCursor( *pPos, nullptr );
524 pPos->GetDoc().SetBoxAttr( aCursor, aUnsetTracking );
527 // tracked row deletion
529 const SwTableLine* pLine = pBox->GetUpper();
530 const SvxPrintItem *pHasTextChangesOnlyProp =
531 pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
532 // table row property "HasTextChangesOnly" is set and its value is false
533 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
535 bool bNoMoreInsertion = false;
536 if ( !bRejectDeletion )
538 SwRedlineTable::size_type nPos = 0;
539 SwRedlineTable::size_type nInsert = pLine->UpdateTextChangesOnly(nPos, /*bUpdateProperty=*/false);
541 if ( SwRedlineTable::npos == nInsert )
542 bNoMoreInsertion = true;
544 if ( bRejectDeletion || bNoMoreInsertion )
546 SvxPrintItem aUnsetTracking(RES_PRINT, true);
547 SwCursor aCursor( *pPos, nullptr );
548 pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
553 bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
554 bool bCallDelete,
555 const SwPosition* pSttRng = nullptr,
556 const SwPosition* pEndRng = nullptr )
558 bool bRet = true;
559 SwRangeRedline* pRedl = rArr[ rPos ];
560 SwPosition *pRStt = nullptr, *pREnd = nullptr;
561 SwComparePosition eCmp = SwComparePosition::Outside;
562 if( pSttRng && pEndRng )
564 pRStt = pRedl->Start();
565 pREnd = pRedl->End();
566 eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
569 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
571 switch( pRedl->GetType() )
573 case RedlineType::Insert:
574 case RedlineType::Format:
576 bool bCheck = false, bReplace = false;
577 switch( eCmp )
579 case SwComparePosition::Inside:
580 if( *pSttRng == *pRStt )
581 pRedl->SetStart( *pEndRng, pRStt );
582 else
584 if( *pEndRng != *pREnd )
586 // split up
587 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
588 pNew->SetStart( *pEndRng );
589 rArr.Insert( pNew ); ++rPos;
591 pRedl->SetEnd( *pSttRng, pREnd );
592 bCheck = true;
594 break;
596 case SwComparePosition::OverlapBefore:
597 pRedl->SetStart( *pEndRng, pRStt );
598 bReplace = true;
599 break;
601 case SwComparePosition::OverlapBehind:
602 pRedl->SetEnd( *pSttRng, pREnd );
603 bCheck = true;
604 break;
606 case SwComparePosition::Outside:
607 case SwComparePosition::Equal:
609 bool bInsert = RedlineType::Insert == pRedl->GetType();
610 SwPosition aPos(pRedl->Start()->GetNode());
611 rArr.DeleteAndDestroy( rPos-- );
613 // remove tracking of the table row, if needed
614 if ( bInsert )
615 lcl_RemoveTrackingOfTableRow( &aPos, /*bRejectDelete=*/false );
617 break;
619 default:
620 bRet = false;
623 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
625 // re-insert
626 rArr.Remove( pRedl );
627 rArr.Insert( pRedl );
630 break;
631 case RedlineType::Delete:
633 SwDoc& rDoc = pRedl->GetDoc();
634 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
635 bool bDelRedl = false;
636 switch( eCmp )
638 case SwComparePosition::Inside:
639 if( bCallDelete )
641 pDelStt = pSttRng;
642 pDelEnd = pEndRng;
644 break;
646 case SwComparePosition::OverlapBefore:
647 if( bCallDelete )
649 pDelStt = pRStt;
650 pDelEnd = pEndRng;
652 break;
653 case SwComparePosition::OverlapBehind:
654 if( bCallDelete )
656 pDelStt = pREnd;
657 pDelEnd = pSttRng;
659 break;
661 case SwComparePosition::Outside:
662 case SwComparePosition::Equal:
664 rArr.Remove( rPos-- );
665 bDelRedl = true;
666 if( bCallDelete )
668 pDelStt = pRedl->Start();
669 pDelEnd = pRedl->End();
672 break;
673 default:
674 bRet = false;
677 if( pDelStt && pDelEnd )
679 SwPaM aPam( *pDelStt, *pDelEnd );
680 SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode();
681 SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
682 pRStt = pRedl->Start();
683 pREnd = pRedl->End();
685 // keep style of the empty paragraph after deletion of wholly paragraphs
686 if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->GetContentIndex() == 0 )
687 lcl_CopyStyle(*pREnd, *pRStt);
689 if( bDelRedl )
690 delete pRedl;
692 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
693 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
695 if( pCSttNd && pCEndNd )
697 rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
698 lcl_DeleteTrackedTableRow( aPam.End() );
700 else if (pCSttNd && !pCEndNd)
702 aPam.GetBound().nContent.Assign( nullptr, 0 );
703 aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
704 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
706 else
708 rDoc.getIDocumentContentOperations().DeleteRange(aPam);
710 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
712 else if( bDelRedl )
713 delete pRedl;
715 break;
717 case RedlineType::FmtColl:
718 rArr.DeleteAndDestroy( rPos-- );
719 break;
721 case RedlineType::ParagraphFormat:
722 rArr.DeleteAndDestroy( rPos-- );
723 break;
725 default:
726 bRet = false;
728 return bRet;
731 bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
732 bool bCallDelete,
733 const SwPosition* pSttRng = nullptr,
734 const SwPosition* pEndRng = nullptr )
736 bool bRet = true;
737 SwRangeRedline* pRedl = rArr[ rPos ];
738 SwDoc& rDoc = pRedl->GetDoc();
739 SwPosition *pRStt = nullptr, *pREnd = nullptr;
740 SwComparePosition eCmp = SwComparePosition::Outside;
741 if( pSttRng && pEndRng )
743 pRStt = pRedl->Start();
744 pREnd = pRedl->End();
745 eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
748 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
750 switch( pRedl->GetType() )
752 case RedlineType::Insert:
754 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
755 bool bDelRedl = false;
756 switch( eCmp )
758 case SwComparePosition::Inside:
759 if( bCallDelete )
761 pDelStt = pSttRng;
762 pDelEnd = pEndRng;
764 break;
766 case SwComparePosition::OverlapBefore:
767 if( bCallDelete )
769 pDelStt = pRStt;
770 pDelEnd = pEndRng;
772 break;
773 case SwComparePosition::OverlapBehind:
774 if( bCallDelete )
776 pDelStt = pREnd;
777 pDelEnd = pSttRng;
779 break;
780 case SwComparePosition::Outside:
781 case SwComparePosition::Equal:
783 // delete the range again
784 rArr.Remove( rPos-- );
785 bDelRedl = true;
786 if( bCallDelete )
788 pDelStt = pRedl->Start();
789 pDelEnd = pRedl->End();
792 break;
794 default:
795 bRet = false;
797 if( pDelStt && pDelEnd )
799 SwPaM aPam( *pDelStt, *pDelEnd );
801 SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode();
802 SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
804 if( bDelRedl )
805 delete pRedl;
807 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
808 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
810 if( pCSttNd && pCEndNd )
812 rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
813 lcl_DeleteTrackedTableRow( aPam.End() );
815 else if (pCSttNd && !pCEndNd)
817 aPam.GetBound().nContent.Assign( nullptr, 0 );
818 aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
819 if (aPam.End()->GetNode().IsStartNode())
820 { // end node will be deleted too! see nNodeDiff+1
821 aPam.End()->Adjust(SwNodeOffset(-1));
823 assert(!aPam.End()->GetNode().IsStartNode());
824 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
826 else
828 rDoc.getIDocumentContentOperations().DeleteRange(aPam);
830 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
832 else if( bDelRedl )
833 delete pRedl;
835 break;
836 case RedlineType::Delete:
838 SwRangeRedline* pNew = nullptr;
839 bool bCheck = false, bReplace = false;
840 SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
841 pEndRng ? *pEndRng : *pRedl->End());
843 if( pRedl->GetExtraData() )
844 pRedl->GetExtraData()->Reject( *pRedl );
846 // remove tracking of the table row, if needed
847 lcl_RemoveTrackingOfTableRow( updatePaM.End(), /*bRejectDelete=*/true );
849 switch( eCmp )
851 case SwComparePosition::Inside:
853 if( 1 < pRedl->GetStackCount() )
855 pNew = new SwRangeRedline( *pRedl );
856 pNew->PopData();
858 if( *pSttRng == *pRStt )
860 pRedl->SetStart( *pEndRng, pRStt );
861 bReplace = true;
862 if( pNew )
863 pNew->SetEnd( *pEndRng );
865 else
867 if( *pEndRng != *pREnd )
869 // split up
870 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
871 pCpy->SetStart( *pEndRng );
872 rArr.Insert( pCpy ); ++rPos;
873 if( pNew )
874 pNew->SetEnd( *pEndRng );
877 pRedl->SetEnd( *pSttRng, pREnd );
878 bCheck = true;
879 if( pNew )
880 pNew->SetStart( *pSttRng );
883 break;
885 case SwComparePosition::OverlapBefore:
886 if( 1 < pRedl->GetStackCount() )
888 pNew = new SwRangeRedline( *pRedl );
889 pNew->PopData();
891 pRedl->SetStart( *pEndRng, pRStt );
892 bReplace = true;
893 if( pNew )
894 pNew->SetEnd( *pEndRng );
895 break;
897 case SwComparePosition::OverlapBehind:
898 if( 1 < pRedl->GetStackCount() )
900 pNew = new SwRangeRedline( *pRedl );
901 pNew->PopData();
903 pRedl->SetEnd( *pSttRng, pREnd );
904 bCheck = true;
905 if( pNew )
906 pNew->SetStart( *pSttRng );
907 break;
909 case SwComparePosition::Outside:
910 case SwComparePosition::Equal:
911 if( !pRedl->PopData() )
912 // deleting the RedlineObject is enough
913 rArr.DeleteAndDestroy( rPos-- );
914 break;
916 default:
917 bRet = false;
920 if( pNew )
922 rArr.Insert( pNew ); ++rPos;
925 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
927 // re-insert
928 rArr.Remove( pRedl );
929 rArr.Insert( pRedl );
932 sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
934 break;
936 case RedlineType::Format:
937 case RedlineType::FmtColl:
938 case RedlineType::ParagraphFormat:
940 // tdf#52391 instead of hidden acception at the requested
941 // rejection, remove direct text formatting to get the potential
942 // original state of the text (FIXME if the original text
943 // has already contained direct text formatting: unfortunately
944 // ODF 1.2 doesn't support rejection of format-only changes)
945 if ( pRedl->GetType() == RedlineType::Format )
947 SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
948 rDoc.ResetAttrs(aPam);
950 else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
952 // handle paragraph formatting changes
953 // (range is only a full paragraph or a part of it)
954 const SwPosition* pStt = pRedl->Start();
955 SwTextNode* pTNd = pStt->GetNode().GetTextNode();
956 if( pTNd )
958 // expand range to the whole paragraph
959 // and reset only the paragraph attributes
960 SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
961 o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
963 constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
964 { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
965 { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
968 for (const auto& [nBegin, nEnd] : aResetableSetRange)
970 for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
971 aResetAttrsArray.insert( i );
974 rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
976 // remove numbering
977 if ( pTNd->GetNumRule() )
978 rDoc.DelNumRules(aPam);
982 if( pRedl->GetExtraData() )
983 pRedl->GetExtraData()->Reject( *pRedl );
985 rArr.DeleteAndDestroy( rPos-- );
987 break;
989 default:
990 bRet = false;
992 return bRet;
995 typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
996 bool bCallDelete,
997 const SwPosition* pSttRng,
998 const SwPosition* pEndRng);
1001 int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
1002 SwRedlineTable& rArr, bool bCallDelete,
1003 const SwPaM& rPam)
1005 SwRedlineTable::size_type n = 0;
1006 int nCount = 0;
1008 const SwPosition* pStt = rPam.Start(),
1009 * pEnd = rPam.End();
1010 const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
1011 if( pFnd && // Is new a part of it?
1012 ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
1014 // Only revoke the partial selection
1015 if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
1016 nCount++;
1017 ++n;
1020 // tdf#119824 first we will accept only overlapping paragraph format changes
1021 // in the first loop to avoid potential content changes during Redo
1022 bool bHasParagraphFormatChange = false;
1023 for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
1025 for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
1027 SwRangeRedline* pTmp = rArr[ o ];
1028 if( pTmp->HasMark() && pTmp->IsVisible() )
1030 if( *pTmp->End() <= *pEnd )
1032 if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
1033 (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
1035 bHasParagraphFormatChange = true;
1036 nCount++;
1039 else
1041 if( *pTmp->Start() < *pEnd )
1043 // Only revoke the partial selection
1044 if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
1045 (*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
1047 bHasParagraphFormatChange = true;
1048 nCount++;
1051 break;
1056 return nCount;
1059 void lcl_AdjustRedlineRange( SwPaM& rPam )
1061 // The Selection is only in the ContentSection. If there are Redlines
1062 // to Non-ContentNodes before or after that, then the Selections
1063 // expand to them.
1064 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
1065 SwDoc& rDoc = rPam.GetDoc();
1066 if( !pStt->GetContentIndex() &&
1067 !rDoc.GetNodes()[ pStt->GetNodeIndex() - 1 ]->IsContentNode() )
1069 const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
1070 if( pRedl )
1072 const SwPosition* pRStt = pRedl->Start();
1073 if( !pRStt->GetContentIndex() && pRStt->GetNodeIndex() ==
1074 pStt->GetNodeIndex() - 1 )
1075 *pStt = *pRStt;
1078 if( pEnd->GetNode().IsContentNode() &&
1079 !rDoc.GetNodes()[ pEnd->GetNodeIndex() + 1 ]->IsContentNode() &&
1080 pEnd->GetContentIndex() == pEnd->GetNode().GetContentNode()->Len() )
1082 const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
1083 if( pRedl )
1085 const SwPosition* pREnd = pRedl->End();
1086 if( !pREnd->GetContentIndex() && pREnd->GetNodeIndex() ==
1087 pEnd->GetNodeIndex() + 1 )
1088 *pEnd = *pREnd;
1093 /// in case some text is deleted, ensure that the not-yet-inserted
1094 /// SwRangeRedline has its positions corrected not to point to deleted node
1095 class TemporaryRedlineUpdater
1097 private:
1098 SwRangeRedline & m_rRedline;
1099 std::shared_ptr<SwUnoCursor> m_pCursor;
1100 public:
1101 TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
1102 : m_rRedline(rRedline)
1103 , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
1105 if (m_rRedline.HasMark())
1107 m_pCursor->SetMark();
1108 *m_pCursor->GetMark() = *m_rRedline.GetMark();
1109 m_rRedline.GetMark()->Assign(rDoc.GetNodes().GetEndOfContent());
1111 m_rRedline.GetPoint()->Assign(rDoc.GetNodes().GetEndOfContent());
1113 ~TemporaryRedlineUpdater()
1115 static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
1120 namespace sw
1123 DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
1124 : m_rDoc(i_rSwdoc)
1125 , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
1126 , mbIsRedlineMove(false)
1127 , mnAutoFormatRedlnCommentNo(0)
1131 RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
1133 return meRedlineFlags;
1136 void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
1138 if( meRedlineFlags == eMode )
1139 return;
1141 if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
1142 || !(RedlineFlags::ShowMask & eMode) )
1144 bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
1145 m_rDoc.SetInXMLImport( false );
1146 // and then hide/display everything
1147 void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
1148 // uninitialized ptr is possible
1150 RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
1151 if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1152 pFnc = &SwRangeRedline::Show;
1153 else if (eShowMode == RedlineFlags::ShowInsert)
1154 pFnc = &SwRangeRedline::Hide;
1155 else if (eShowMode == RedlineFlags::ShowDelete)
1156 pFnc = &SwRangeRedline::ShowOriginal;
1157 else
1159 pFnc = &SwRangeRedline::Hide;
1160 eMode |= RedlineFlags::ShowInsert;
1163 CheckAnchoredFlyConsistency(m_rDoc);
1164 CHECK_REDLINE( *this )
1166 o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
1167 if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1169 // sw_redlinehide: the problem here is that MoveFromSection
1170 // creates the frames wrongly (non-merged), because its own
1171 // SwRangeRedline has wrong positions until after the nodes
1172 // are all moved, so fix things up by force by re-creating
1173 // all merged frames from scratch.
1174 o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
1175 for (SwRootFrame *const pLayout : layouts)
1177 if (pLayout->IsHideRedlines())
1179 pLayout->SetHideRedlines(false);
1180 hiddenLayouts.insert(pLayout);
1185 for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
1186 for (size_t i = 0; i < maRedlineTable.size(); ++i)
1188 SwRangeRedline *const pRedline = maRedlineTable[i];
1189 (pRedline->*pFnc)(nLoop, i, false);
1190 while (maRedlineTable.size() <= i
1191 || maRedlineTable[i] != pRedline)
1192 { // ensure current position
1193 --i; // a previous redline may have been deleted
1197 //SwRangeRedline::MoveFromSection routinely changes
1198 //the keys that mpRedlineTable is sorted by
1199 maRedlineTable.Resort();
1201 CheckAnchoredFlyConsistency(m_rDoc);
1202 CHECK_REDLINE( *this )
1204 for (SwRootFrame *const pLayout : hiddenLayouts)
1206 pLayout->SetHideRedlines(true);
1209 m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
1211 meRedlineFlags = eMode;
1212 m_rDoc.getIDocumentState().SetModified();
1214 // #TODO - add 'SwExtraRedlineTable' also ?
1217 bool DocumentRedlineManager::IsRedlineOn() const
1219 return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
1222 bool DocumentRedlineManager::IsIgnoreRedline() const
1224 return bool(RedlineFlags::Ignore & meRedlineFlags);
1227 void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
1229 meRedlineFlags = eMode;
1232 const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
1234 return maRedlineTable;
1237 SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
1239 return maRedlineTable;
1242 const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
1244 return maExtraRedlineTable;
1247 SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
1249 return maExtraRedlineTable;
1252 bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
1254 if (&rNode.GetNodes() != &m_rDoc.GetNodes())
1255 return false;
1257 SwPosition aPos(rNode);
1258 SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
1259 SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
1260 SwPosition(rEndOfRedlines));
1262 return aPam.ContainsPosition(aPos);
1265 bool DocumentRedlineManager::IsRedlineMove() const
1267 return mbIsRedlineMove;
1270 void DocumentRedlineManager::SetRedlineMove(bool bFlag)
1272 mbIsRedlineMove = bFlag;
1276 Text means Text not "polluted" by Redlines.
1278 Behaviour of Insert-Redline:
1279 - in the Text - insert Redline Object
1280 - in InsertRedline (own) - ignore, existing is extended
1281 - in InsertRedline (others) - split up InsertRedline and
1282 insert Redline Object
1283 - in DeleteRedline - split up DeleteRedline or
1284 move at the end/beginning
1286 Behaviour of Delete-Redline:
1287 - in the Text - insert Redline Object
1288 - in DeleteRedline (own/others) - ignore
1289 - in InsertRedline (own) - ignore, but delete character
1290 - in InsertRedline (others) - split up InsertRedline and
1291 insert Redline Object
1292 - Text and own Insert overlap - delete Text in the own Insert,
1293 extend in the other Text
1294 (up to the Insert!)
1295 - Text and other Insert overlap - insert Redline Object, the
1296 other Insert is overlapped by
1297 the Delete
1299 IDocumentRedlineAccess::AppendResult
1300 DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
1302 CHECK_REDLINE( *this )
1304 if (!IsRedlineOn() || IsShowOriginal(meRedlineFlags))
1306 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
1308 RedlineFlags eOld = meRedlineFlags;
1309 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
1310 // The ShowMode needs to be retained!
1311 meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
1312 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1313 meRedlineFlags = eOld;
1315 delete pNewRedl;
1316 pNewRedl = nullptr;
1317 CHECK_REDLINE( *this )
1318 return AppendResult::IGNORED;
1322 bool bMerged = false;
1324 pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
1326 if( m_rDoc.IsAutoFormatRedline() )
1328 pNewRedl->SetAutoFormat();
1329 if( moAutoFormatRedlnComment && !moAutoFormatRedlnComment->isEmpty() )
1331 pNewRedl->SetComment( *moAutoFormatRedlnComment );
1332 pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
1336 auto [pStt, pEnd] = pNewRedl->StartEnd(); // SwPosition*
1338 SwTextNode* pTextNode = pStt->GetNode().GetTextNode();
1339 if( pTextNode == nullptr )
1341 if( pStt->GetContentIndex() > 0 )
1343 OSL_ENSURE( false, "Redline start: non-text-node with content" );
1344 pStt->SetContent( 0 );
1347 else
1349 if( pStt->GetContentIndex() > pTextNode->Len() )
1351 OSL_ENSURE( false, "Redline start: index after text" );
1352 pStt->SetContent( pTextNode->Len() );
1355 pTextNode = pEnd->GetNode().GetTextNode();
1356 if( pTextNode == nullptr )
1358 if( pEnd->GetContentIndex() > 0 )
1360 OSL_ENSURE( false, "Redline end: non-text-node with content" );
1361 pEnd->SetContent(0);
1364 else
1366 if( pEnd->GetContentIndex() > pTextNode->Len() )
1368 OSL_ENSURE( false, "Redline end: index after text" );
1369 pEnd->SetContent( pTextNode->Len() );
1373 if( ( *pStt == *pEnd ) &&
1374 ( pNewRedl->GetContentIdx() == nullptr ) )
1375 { // Do not insert empty redlines
1376 delete pNewRedl;
1377 return AppendResult::IGNORED;
1379 bool bCompress = false;
1380 SwRedlineTable::size_type n = 0;
1381 // look up the first Redline for the starting position
1382 if( !GetRedline( *pStt, &n ) && n )
1383 --n;
1384 const SwRedlineTable::size_type nStartPos = n;
1385 bool bDec = false;
1387 for( ; pNewRedl && n < maRedlineTable.size(); bDec ? n : ++n )
1389 bDec = false;
1391 SwRangeRedline* pRedl = maRedlineTable[ n ];
1392 auto [pRStt, pREnd] = pRedl->StartEnd();
1394 // #i8518# remove empty redlines while we're at it
1395 if( ( *pRStt == *pREnd ) &&
1396 ( pRedl->GetContentIdx() == nullptr ) )
1398 maRedlineTable.DeleteAndDestroy(n);
1399 continue;
1402 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1404 if ( SwComparePosition::Before == eCmpPos && !IsPrevPos( *pEnd, *pRStt ))
1405 break;
1407 switch( pNewRedl->GetType() )
1409 case RedlineType::Insert:
1410 switch( pRedl->GetType() )
1412 case RedlineType::Insert:
1413 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1414 // don't join inserted characters with moved text
1415 !pRedl->IsMoved() )
1417 bool bDelete = false;
1419 // Merge if applicable?
1420 if( (( SwComparePosition::Behind == eCmpPos &&
1421 IsPrevPos( *pREnd, *pStt ) ) ||
1422 ( SwComparePosition::CollideStart == eCmpPos ) ||
1423 ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
1424 pRedl->CanCombine( *pNewRedl ) &&
1425 ( n+1 >= maRedlineTable.size() ||
1426 ( *maRedlineTable[ n+1 ]->Start() >= *pEnd &&
1427 *maRedlineTable[ n+1 ]->Start() != *pREnd ) ) )
1429 pRedl->SetEnd( *pEnd, pREnd );
1430 if( !pRedl->HasValidRange() )
1432 // re-insert
1433 maRedlineTable.Remove( n );
1434 maRedlineTable.Insert( pRedl );
1437 bMerged = true;
1438 bDelete = true;
1440 else if( (( SwComparePosition::Before == eCmpPos &&
1441 IsPrevPos( *pEnd, *pRStt ) ) ||
1442 ( SwComparePosition::CollideEnd == eCmpPos ) ||
1443 ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
1444 pRedl->CanCombine( *pNewRedl ) &&
1445 ( !n ||
1446 *maRedlineTable[ n-1 ]->End() != *pRStt ))
1448 pRedl->SetStart( *pStt, pRStt );
1449 // re-insert
1450 maRedlineTable.Remove( n );
1451 maRedlineTable.Insert( pRedl );
1453 bMerged = true;
1454 bDelete = true;
1456 else if ( SwComparePosition::Outside == eCmpPos )
1458 // own insert-over-insert redlines:
1459 // just scrap the inside ones
1460 maRedlineTable.DeleteAndDestroy( n );
1461 bDec = true;
1463 else if( SwComparePosition::OverlapBehind == eCmpPos )
1465 *pStt = *pREnd;
1466 if( ( *pStt == *pEnd ) &&
1467 ( pNewRedl->GetContentIdx() == nullptr ) )
1468 bDelete = true;
1470 else if( SwComparePosition::OverlapBefore == eCmpPos )
1472 *pEnd = *pRStt;
1473 if( ( *pStt == *pEnd ) &&
1474 ( pNewRedl->GetContentIdx() == nullptr ) )
1475 bDelete = true;
1477 else if( SwComparePosition::Inside == eCmpPos )
1479 bDelete = true;
1480 bMerged = true;
1482 else if( SwComparePosition::Equal == eCmpPos )
1483 bDelete = true;
1485 if( bDelete )
1487 delete pNewRedl;
1488 pNewRedl = nullptr;
1489 bCompress = true;
1491 // set IsMoved checking nearby redlines
1492 if (n < maRedlineTable.size()) // in case above 're-insert' failed
1493 maRedlineTable.isMoved(n);
1496 else if( SwComparePosition::Inside == eCmpPos )
1498 // split up
1499 if( *pEnd != *pREnd )
1501 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1502 pCpy->SetStart( *pEnd );
1503 maRedlineTable.Insert( pCpy );
1505 pRedl->SetEnd( *pStt, pREnd );
1506 if( ( *pStt == *pRStt ) &&
1507 ( pRedl->GetContentIdx() == nullptr ) )
1509 maRedlineTable.DeleteAndDestroy( n );
1510 bDec = true;
1512 else if( !pRedl->HasValidRange() )
1514 // re-insert
1515 maRedlineTable.Remove( n );
1516 maRedlineTable.Insert( pRedl );
1519 else if ( SwComparePosition::Outside == eCmpPos )
1521 // handle overlapping redlines in broken documents
1523 // split up the new redline, since it covers the
1524 // existing redline. Insert the first part, and
1525 // progress with the remainder as usual
1526 SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1527 pSplit->SetEnd( *pRStt );
1528 pNewRedl->SetStart( *pREnd );
1529 maRedlineTable.Insert( pSplit );
1530 if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1532 delete pNewRedl;
1533 pNewRedl = nullptr;
1534 bCompress = true;
1537 else if ( SwComparePosition::OverlapBehind == eCmpPos )
1539 // handle overlapping redlines in broken documents
1540 pNewRedl->SetStart( *pREnd );
1542 else if ( SwComparePosition::OverlapBefore == eCmpPos )
1544 // handle overlapping redlines in broken documents
1545 *pEnd = *pRStt;
1546 if( ( *pStt == *pEnd ) &&
1547 ( pNewRedl->GetContentIdx() == nullptr ) )
1549 delete pNewRedl;
1550 pNewRedl = nullptr;
1551 bCompress = true;
1554 break;
1555 case RedlineType::Delete:
1556 if( SwComparePosition::Inside == eCmpPos )
1558 // split up
1559 if( *pEnd != *pREnd )
1561 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1562 pCpy->SetStart( *pEnd );
1563 maRedlineTable.Insert( pCpy );
1565 pRedl->SetEnd( *pStt, pREnd );
1566 if( ( *pStt == *pRStt ) &&
1567 ( pRedl->GetContentIdx() == nullptr ) )
1569 maRedlineTable.DeleteAndDestroy( n );
1570 bDec = true;
1572 else if( !pRedl->HasValidRange() )
1574 // re-insert
1575 maRedlineTable.Remove( n );
1576 maRedlineTable.Insert( pRedl, n );
1579 else if ( SwComparePosition::Outside == eCmpPos )
1581 // handle overlapping redlines in broken documents
1583 // split up the new redline, since it covers the
1584 // existing redline. Insert the first part, and
1585 // progress with the remainder as usual
1586 SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1587 pSplit->SetEnd( *pRStt );
1588 pNewRedl->SetStart( *pREnd );
1589 maRedlineTable.Insert( pSplit );
1590 if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1592 delete pNewRedl;
1593 pNewRedl = nullptr;
1594 bCompress = true;
1597 else if ( SwComparePosition::Equal == eCmpPos )
1599 // handle identical redlines in broken documents
1600 // delete old (delete) redline
1601 maRedlineTable.DeleteAndDestroy( n );
1602 bDec = true;
1604 else if ( SwComparePosition::OverlapBehind == eCmpPos )
1605 { // Another workaround for broken redlines
1606 pNewRedl->SetStart( *pREnd );
1608 break;
1609 case RedlineType::Format:
1610 switch( eCmpPos )
1612 case SwComparePosition::OverlapBefore:
1613 pRedl->SetStart( *pEnd, pRStt );
1614 // re-insert
1615 maRedlineTable.Remove( n );
1616 maRedlineTable.Insert( pRedl, n );
1617 bDec = true;
1618 break;
1620 case SwComparePosition::OverlapBehind:
1621 pRedl->SetEnd( *pStt, pREnd );
1622 if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
1624 maRedlineTable.DeleteAndDestroy( n );
1625 bDec = true;
1627 break;
1629 case SwComparePosition::Equal:
1630 case SwComparePosition::Outside:
1631 // Overlaps the current one completely or has the
1632 // same dimension, delete the old one
1633 maRedlineTable.DeleteAndDestroy( n );
1634 bDec = true;
1635 break;
1637 case SwComparePosition::Inside:
1638 // Overlaps the current one completely,
1639 // split or shorten the new one
1640 if( *pEnd != *pREnd )
1642 if( *pEnd != *pRStt )
1644 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
1645 pNew->SetStart( *pEnd );
1646 pRedl->SetEnd( *pStt, pREnd );
1647 if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
1648 maRedlineTable.DeleteAndDestroy( n );
1649 AppendRedline( pNew, bCallDelete );
1650 n = 0; // re-initialize
1651 bDec = true;
1654 else
1655 pRedl->SetEnd( *pStt, pREnd );
1656 break;
1657 default:
1658 break;
1660 break;
1661 default:
1662 break;
1664 break;
1666 case RedlineType::Delete:
1667 switch( pRedl->GetType() )
1669 case RedlineType::Delete:
1670 switch( eCmpPos )
1672 case SwComparePosition::Outside:
1674 // Overlaps the current one completely,
1675 // split the new one
1676 if (*pEnd == *pREnd)
1678 pNewRedl->SetEnd(*pRStt, pEnd);
1680 else if (*pStt == *pRStt)
1682 pNewRedl->SetStart(*pREnd, pStt);
1684 else
1686 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
1687 pNew->SetStart( *pREnd );
1688 pNewRedl->SetEnd( *pRStt, pEnd );
1689 AppendRedline( pNew, bCallDelete );
1690 n = 0; // re-initialize
1691 bDec = true;
1694 break;
1696 case SwComparePosition::Inside:
1697 case SwComparePosition::Equal:
1698 delete pNewRedl;
1699 pNewRedl = nullptr;
1700 bCompress = true;
1701 break;
1703 case SwComparePosition::OverlapBefore:
1704 case SwComparePosition::OverlapBehind:
1705 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1706 pRedl->CanCombine( *pNewRedl ))
1708 // If that's the case we can merge it, meaning
1709 // the new one covers this well
1710 if( SwComparePosition::OverlapBehind == eCmpPos )
1711 pNewRedl->SetStart( *pRStt, pStt );
1712 else
1713 pNewRedl->SetEnd( *pREnd, pEnd );
1714 maRedlineTable.DeleteAndDestroy( n );
1715 bDec = true;
1717 else if( SwComparePosition::OverlapBehind == eCmpPos )
1718 pNewRedl->SetStart( *pREnd, pStt );
1719 else
1720 pNewRedl->SetEnd( *pRStt, pEnd );
1721 break;
1723 case SwComparePosition::CollideEnd:
1724 if (pRStt->GetContentIndex() != 0
1725 && pRStt->GetNode() != pREnd->GetNode())
1726 { // tdf#147466 HACK: don't combine in this case to avoid the tdf#119571 code from *undeleting* section nodes
1727 break;
1729 [[fallthrough]];
1730 case SwComparePosition::CollideStart:
1731 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1732 pRedl->CanCombine( *pNewRedl ) )
1734 if( IsHideChanges( meRedlineFlags ))
1736 // Before we can merge, we make it visible!
1737 // We insert temporarily so that pNew is
1738 // also dealt with when moving the indices.
1739 maRedlineTable.Insert(pNewRedl);
1740 pRedl->Show(0, maRedlineTable.GetPos(pRedl));
1741 maRedlineTable.Remove( pNewRedl );
1742 pRStt = pRedl->Start();
1743 pREnd = pRedl->End();
1746 // If that's the case we can merge it, meaning
1747 // the new one covers this well
1748 if( SwComparePosition::CollideStart == eCmpPos )
1749 pNewRedl->SetStart( *pRStt, pStt );
1750 else
1751 pNewRedl->SetEnd( *pREnd, pEnd );
1753 // delete current (below), and restart process with
1754 // previous
1755 SwRedlineTable::size_type nToBeDeleted = n;
1756 bDec = true;
1758 if( *(pNewRedl->Start()) <= *pREnd )
1760 // Whoooah, we just extended the new 'redline'
1761 // beyond previous redlines, so better start
1762 // again. Of course this is not supposed to
1763 // happen, and in an ideal world it doesn't,
1764 // but unfortunately this code is buggy and
1765 // totally rotten so it does happen and we
1766 // better fix it.
1767 n = 0;
1768 bDec = true;
1771 maRedlineTable.DeleteAndDestroy( nToBeDeleted );
1773 break;
1774 default:
1775 break;
1777 break;
1779 case RedlineType::Insert:
1781 // b62341295: Do not throw away redlines
1782 // even if they are not allowed to be combined
1783 RedlineFlags eOld = meRedlineFlags;
1784 if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
1785 pRedl->IsOwnRedline( *pNewRedl ) &&
1786 // tdf#116084 tdf#121176 don't combine anonymized deletion
1787 // and anonymized insertion, i.e. with the same dummy timestamp
1788 !pRedl->GetRedlineData(0).IsAnonymized() )
1791 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
1792 // The ShowMode needs to be retained!
1793 meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
1794 switch( eCmpPos )
1796 case SwComparePosition::Equal:
1797 bCompress = true;
1798 maRedlineTable.DeleteAndDestroy( n );
1799 bDec = true;
1800 [[fallthrough]];
1802 case SwComparePosition::Inside:
1803 if( bCallDelete )
1805 // DeleteAndJoin does not yield the
1806 // desired result if there is no paragraph to
1807 // join with, i.e. at the end of the document.
1808 // For this case, we completely delete the
1809 // paragraphs (if, of course, we also start on
1810 // a paragraph boundary).
1811 if( (pStt->GetContentIndex() == 0) &&
1812 pEnd->GetNode().IsEndNode() )
1814 pEnd->Adjust(SwNodeOffset(-1));
1815 m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
1817 else
1818 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1820 bCompress = true;
1822 delete pNewRedl;
1823 pNewRedl = nullptr;
1824 break;
1826 case SwComparePosition::Outside:
1828 maRedlineTable.Remove( n );
1829 bDec = true;
1830 if( bCallDelete )
1832 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1833 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
1834 n = 0; // re-initialize
1836 delete pRedl;
1838 break;
1840 case SwComparePosition::OverlapBefore:
1842 SwPaM aPam( *pRStt, *pEnd );
1844 if( *pEnd == *pREnd )
1845 maRedlineTable.DeleteAndDestroy( n );
1846 else
1848 pRedl->SetStart( *pEnd, pRStt );
1849 // re-insert
1850 maRedlineTable.Remove( n );
1851 maRedlineTable.Insert( pRedl, n );
1854 if( bCallDelete )
1856 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1857 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1858 n = 0; // re-initialize
1860 bDec = true;
1862 break;
1864 case SwComparePosition::OverlapBehind:
1866 SwPaM aPam( *pStt, *pREnd );
1868 if( *pStt == *pRStt )
1870 maRedlineTable.DeleteAndDestroy( n );
1871 bDec = true;
1873 else
1874 pRedl->SetEnd( *pStt, pREnd );
1876 if( bCallDelete )
1878 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1879 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1880 n = 0; // re-initialize
1881 bDec = true;
1884 break;
1885 default:
1886 break;
1889 meRedlineFlags = eOld;
1891 else
1893 // it may be necessary to split the existing redline in
1894 // two. In this case, pRedl will be changed to cover
1895 // only part of its former range, and pNew will cover
1896 // the remainder.
1897 SwRangeRedline* pNew = nullptr;
1899 switch( eCmpPos )
1901 case SwComparePosition::Equal:
1903 pRedl->PushData( *pNewRedl );
1904 delete pNewRedl;
1905 pNewRedl = nullptr;
1906 if( IsHideChanges( meRedlineFlags ))
1908 pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
1910 bCompress = true;
1912 break;
1914 case SwComparePosition::Inside:
1916 if( *pRStt == *pStt )
1918 // #i97421#
1919 // redline w/out extent loops
1920 if (*pStt != *pEnd)
1922 pNewRedl->PushData( *pRedl, false );
1923 pRedl->SetStart( *pEnd, pRStt );
1924 // re-insert
1925 maRedlineTable.Remove( n );
1926 maRedlineTable.Insert( pRedl, n );
1927 bDec = true;
1930 else
1932 pNewRedl->PushData( *pRedl, false );
1933 if( *pREnd != *pEnd )
1935 pNew = new SwRangeRedline( *pRedl );
1936 pNew->SetStart( *pEnd );
1938 pRedl->SetEnd( *pStt, pREnd );
1939 if( !pRedl->HasValidRange() )
1941 // re-insert
1942 maRedlineTable.Remove( n );
1943 maRedlineTable.Insert( pRedl, n );
1947 break;
1949 case SwComparePosition::Outside:
1951 pRedl->PushData( *pNewRedl );
1952 if( *pEnd == *pREnd )
1954 pNewRedl->SetEnd( *pRStt, pEnd );
1956 else if (*pStt == *pRStt)
1958 pNewRedl->SetStart(*pREnd, pStt);
1960 else
1962 pNew = new SwRangeRedline( *pNewRedl );
1963 pNew->SetEnd( *pRStt );
1964 pNewRedl->SetStart( *pREnd, pStt );
1966 bCompress = true;
1968 break;
1970 case SwComparePosition::OverlapBefore:
1972 if( *pEnd == *pREnd )
1974 pRedl->PushData( *pNewRedl );
1975 pNewRedl->SetEnd( *pRStt, pEnd );
1976 if( IsHideChanges( meRedlineFlags ))
1978 maRedlineTable.Insert(pNewRedl);
1979 pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
1980 maRedlineTable.Remove( pNewRedl );
1983 else
1985 pNew = new SwRangeRedline( *pRedl );
1986 pNew->PushData( *pNewRedl );
1987 pNew->SetEnd( *pEnd );
1988 pNewRedl->SetEnd( *pRStt, pEnd );
1989 pRedl->SetStart( *pNew->End(), pRStt ) ;
1990 // re-insert
1991 maRedlineTable.Remove( n );
1992 maRedlineTable.Insert( pRedl );
1993 bDec = true;
1996 break;
1998 case SwComparePosition::OverlapBehind:
2000 if( *pStt == *pRStt )
2002 pRedl->PushData( *pNewRedl );
2003 pNewRedl->SetStart( *pREnd, pStt );
2004 if( IsHideChanges( meRedlineFlags ))
2006 maRedlineTable.Insert( pNewRedl );
2007 pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
2008 maRedlineTable.Remove( pNewRedl );
2011 else
2013 pNew = new SwRangeRedline( *pRedl );
2014 pNew->PushData( *pNewRedl );
2015 pNew->SetStart( *pStt );
2016 pNewRedl->SetStart( *pREnd, pStt );
2017 pRedl->SetEnd( *pNew->Start(), pREnd );
2018 if( !pRedl->HasValidRange() )
2020 // re-insert
2021 maRedlineTable.Remove( n );
2022 maRedlineTable.Insert( pRedl );
2026 break;
2027 default:
2028 break;
2031 // insert the pNew part (if it exists)
2032 if( pNew )
2034 maRedlineTable.Insert( pNew );
2036 // pNew must be deleted if Insert() wasn't
2037 // successful. But that can't happen, since pNew is
2038 // part of the original pRedl redline.
2039 // OSL_ENSURE( bRet, "Can't insert existing redline?" );
2041 // restart (now with pRedl being split up)
2042 n = 0;
2043 bDec = true;
2047 break;
2049 case RedlineType::Format:
2050 switch( eCmpPos )
2052 case SwComparePosition::OverlapBefore:
2053 pRedl->SetStart( *pEnd, pRStt );
2054 // re-insert
2055 maRedlineTable.Remove( n );
2056 maRedlineTable.Insert( pRedl, n );
2057 bDec = true;
2058 break;
2060 case SwComparePosition::OverlapBehind:
2061 pRedl->SetEnd( *pStt, pREnd );
2062 break;
2064 case SwComparePosition::Equal:
2065 case SwComparePosition::Outside:
2066 // Overlaps the current one completely or has the
2067 // same dimension, delete the old one
2068 maRedlineTable.DeleteAndDestroy( n );
2069 bDec = true;
2070 break;
2072 case SwComparePosition::Inside:
2073 // Overlaps the current one completely,
2074 // split or shorten the new one
2075 if( *pEnd != *pREnd )
2077 if( *pEnd != *pRStt )
2079 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
2080 pNew->SetStart( *pEnd );
2081 pRedl->SetEnd( *pStt, pREnd );
2082 if( ( *pStt == *pRStt ) &&
2083 ( pRedl->GetContentIdx() == nullptr ) )
2084 maRedlineTable.DeleteAndDestroy( n );
2085 AppendRedline( pNew, bCallDelete );
2086 n = 0; // re-initialize
2087 bDec = true;
2090 else
2091 pRedl->SetEnd( *pStt, pREnd );
2092 break;
2093 default:
2094 break;
2096 break;
2097 default:
2098 break;
2100 break;
2102 case RedlineType::Format:
2103 switch( pRedl->GetType() )
2105 case RedlineType::Insert:
2106 case RedlineType::Delete:
2107 switch( eCmpPos )
2109 case SwComparePosition::OverlapBefore:
2110 pNewRedl->SetEnd( *pRStt, pEnd );
2111 break;
2113 case SwComparePosition::OverlapBehind:
2114 pNewRedl->SetStart( *pREnd, pStt );
2115 break;
2117 case SwComparePosition::Equal:
2118 case SwComparePosition::Inside:
2119 delete pNewRedl;
2120 pNewRedl = nullptr;
2121 break;
2123 case SwComparePosition::Outside:
2124 // Overlaps the current one completely,
2125 // split or shorten the new one
2126 if (*pEnd == *pREnd)
2128 pNewRedl->SetEnd(*pRStt, pEnd);
2130 else if (*pStt == *pRStt)
2132 pNewRedl->SetStart(*pREnd, pStt);
2134 else
2136 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
2137 pNew->SetStart( *pREnd );
2138 pNewRedl->SetEnd( *pRStt, pEnd );
2139 AppendRedline( pNew, bCallDelete );
2140 n = 0; // re-initialize
2141 bDec = true;
2143 break;
2144 default:
2145 break;
2147 break;
2148 case RedlineType::Format:
2149 switch( eCmpPos )
2151 case SwComparePosition::Outside:
2152 case SwComparePosition::Equal:
2154 // Overlaps the current one completely or has the
2155 // same dimension, delete the old one
2156 maRedlineTable.DeleteAndDestroy( n );
2157 bDec = true;
2159 break;
2161 case SwComparePosition::Inside:
2162 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2163 pRedl->CanCombine( *pNewRedl ))
2165 // own one can be ignored completely
2166 delete pNewRedl;
2167 pNewRedl = nullptr;
2169 else if( *pREnd == *pEnd )
2170 // or else only shorten the current one
2171 pRedl->SetEnd( *pStt, pREnd );
2172 else if( *pRStt == *pStt )
2174 // or else only shorten the current one
2175 pRedl->SetStart( *pEnd, pRStt );
2176 // re-insert
2177 maRedlineTable.Remove( n );
2178 maRedlineTable.Insert( pRedl, n );
2179 bDec = true;
2181 else
2183 // If it lies completely within the current one
2184 // we need to split it
2185 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
2186 pNew->SetStart( *pEnd );
2187 pRedl->SetEnd( *pStt, pREnd );
2188 AppendRedline( pNew, bCallDelete );
2189 n = 0; // re-initialize
2190 bDec = true;
2192 break;
2194 case SwComparePosition::OverlapBefore:
2195 case SwComparePosition::OverlapBehind:
2196 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2197 pRedl->CanCombine( *pNewRedl ))
2199 // If that's the case we can merge it, meaning
2200 // the new one covers this well
2201 if( SwComparePosition::OverlapBehind == eCmpPos )
2202 pNewRedl->SetStart( *pRStt, pStt );
2203 else
2204 pNewRedl->SetEnd( *pREnd, pEnd );
2205 maRedlineTable.DeleteAndDestroy( n );
2206 bDec = false;
2208 else if( SwComparePosition::OverlapBehind == eCmpPos )
2209 pNewRedl->SetStart( *pREnd, pStt );
2210 else
2211 pNewRedl->SetEnd( *pRStt, pEnd );
2212 break;
2214 case SwComparePosition::CollideEnd:
2215 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2216 pRedl->CanCombine( *pNewRedl ) &&
2217 (n == 0 || *maRedlineTable[ n-1 ]->End() < *pStt))
2219 // If that's the case we can merge it, meaning
2220 // the new one covers this well
2221 pNewRedl->SetEnd( *pREnd, pEnd );
2222 maRedlineTable.DeleteAndDestroy( n );
2223 bDec = true;
2225 break;
2226 case SwComparePosition::CollideStart:
2227 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2228 pRedl->CanCombine( *pNewRedl ) &&
2229 (n+1 >= maRedlineTable.size() ||
2230 (*maRedlineTable[ n+1 ]->Start() >= *pEnd &&
2231 *maRedlineTable[ n+1 ]->Start() != *pREnd)))
2233 // If that's the case we can merge it, meaning
2234 // the new one covers this well
2235 pNewRedl->SetStart( *pRStt, pStt );
2236 maRedlineTable.DeleteAndDestroy( n );
2237 bDec = true;
2239 break;
2240 default:
2241 break;
2243 break;
2244 default:
2245 break;
2247 break;
2249 case RedlineType::FmtColl:
2250 // How should we behave here?
2251 // insert as is
2252 break;
2253 default:
2254 break;
2258 if( pNewRedl )
2260 if( ( *pStt == *pEnd ) &&
2261 ( pNewRedl->GetContentIdx() == nullptr ) )
2262 { // Do not insert empty redlines
2263 delete pNewRedl;
2264 pNewRedl = nullptr;
2266 else
2268 if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2270 if ( pStt->GetContentIndex() != 0 )
2272 // tdf#119571 update the style of the joined paragraph
2273 // after a partially deleted paragraph to show its correct style
2274 // in "Show changes" mode, too. All removed paragraphs
2275 // get the style of the first (partially deleted) paragraph
2276 // to avoid text insertion with bad style in the deleted
2277 // area later (except paragraphs of the removed tables).
2279 SwContentNode* pDelNd = pStt->GetNode().GetContentNode();
2280 // start copying the style of the first paragraph from the end of the range
2281 SwContentNode* pTextNd = pEnd->GetNode().GetContentNode();
2282 SwNodeIndex aIdx( pEnd->GetNode() );
2283 bool bFirst = true;
2285 while (pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
2287 if( pTextNd->IsTextNode() )
2289 SwPosition aPos(aIdx);
2291 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2293 bCompress = true;
2295 // split redline to store ExtraData per paragraphs
2296 SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
2297 pPar->SetStart( aPos );
2298 pNewRedl->SetEnd( aPos );
2300 // get extradata for reset formatting of the modified paragraph
2301 SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
2302 if (pExtraData)
2304 std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
2305 if (!bFirst)
2306 pExtraData->SetFormatAll(false);
2307 xRedlineExtraData.reset(pExtraData);
2308 pPar->SetExtraData( xRedlineExtraData.get() );
2311 // skip empty redlines without ExtraData
2312 // FIXME: maybe checking pExtraData is redundant here
2313 if ( pExtraData || *pPar->Start() != *pPar->End() )
2314 maRedlineTable.Insert( pPar );
2315 else
2316 delete pPar;
2319 // modify paragraph formatting
2320 lcl_CopyStyle(*pStt, aPos);
2323 if (bFirst)
2324 bFirst = false;
2326 // Jump to the previous paragraph and if needed, skip paragraphs of
2327 // the removed table(s) in the range to avoid leaving empty tables
2328 // because of the non-continuous redline range over the table.
2329 // FIXME: this is not enough for tables with inner redlines, where
2330 // tracked deletion of the text containing such a table leaves an
2331 // empty table at the place of the table (a problem inherited from OOo).
2332 pTextNd = nullptr;
2333 while( --aIdx > *pDelNd && !aIdx.GetNode().IsContentNode() )
2335 // possible table end
2336 if( aIdx.GetNode().IsEndNode() && aIdx.GetNode().FindTableNode() )
2338 SwNodeIndex aIdx2 = aIdx;
2339 // search table start and skip table paragraphs
2340 while ( pDelNd->GetIndex() < aIdx2.GetIndex() )
2342 SwTableNode* pTable = aIdx2.GetNode().GetTableNode();
2343 if( pTable &&
2344 pTable->EndOfSectionNode()->GetIndex() == aIdx.GetIndex() )
2346 aIdx = aIdx2;
2347 break;
2349 --aIdx2;
2354 if (aIdx.GetNode().IsContentNode())
2355 pTextNd = aIdx.GetNode().GetContentNode();
2359 // delete tables of the deletion explicitly, to avoid
2360 // remaining empty tables after accepting the rejection
2361 // and visible empty tables in Hide Changes mode
2362 // (this was the case, if tables have already contained
2363 // other tracked changes)
2364 // FIXME: because of recursive nature of AppendRedline,
2365 // this doesn't work for selections with multiple tables
2366 if ( m_rDoc.GetIDocumentUndoRedo().DoesUndo() )
2368 SwNodeIndex aSttIdx( pStt->GetNode() );
2369 SwNodeIndex aEndIdx( pEnd->GetNode() );
2370 while ( aSttIdx < aEndIdx )
2372 if ( aSttIdx.GetNode().IsTableNode() )
2374 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
2375 SwCursor aCursor( SwPosition(aSttIdx), nullptr );
2376 m_rDoc.SetRowNotTracked( aCursor, aHasTextChangesOnly, /*bAll=*/true );
2378 ++aSttIdx;
2382 bool const ret = maRedlineTable.Insert( pNewRedl );
2383 assert(ret || !pNewRedl);
2384 if (ret && !pNewRedl)
2386 bMerged = true; // treat InsertWithValidRanges as "merge"
2391 if( bCompress )
2392 CompressRedlines(nStartPos);
2394 CHECK_REDLINE( *this )
2396 return (nullptr != pNewRedl)
2397 ? AppendResult::APPENDED
2398 : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
2401 bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
2403 // #TODO - equivalent for 'SwTableRowRedline'
2405 CHECK_REDLINE( this )
2408 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2410 // #TODO - equivalent for 'SwTableRowRedline'
2412 pNewRedl->InvalidateRange();
2415 // Make equivalent of 'AppendRedline' checks inside here too
2417 maExtraRedlineTable.Insert( pNewRedl );
2419 else
2421 // TO DO - equivalent for 'SwTableRowRedline'
2423 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2425 RedlineFlags eOld = meRedlineFlags;
2426 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2427 // The ShowMode needs to be retained!
2428 meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2429 DeleteAndJoin( *pNewRedl );
2430 meRedlineFlags = eOld;
2432 delete pNewRedl, pNewRedl = 0;
2435 // #TODO - equivalent for 'SwTableRowRedline'
2437 CHECK_REDLINE( this )
2440 return nullptr != pNewRedl;
2443 bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
2445 // #TODO - equivalent for 'SwTableCellRedline'
2447 CHECK_REDLINE( this )
2450 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2452 // #TODO - equivalent for 'SwTableCellRedline'
2454 pNewRedl->InvalidateRange();
2457 // Make equivalent of 'AppendRedline' checks inside here too
2459 maExtraRedlineTable.Insert( pNewRedl );
2461 else
2463 // TO DO - equivalent for 'SwTableCellRedline'
2465 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2467 RedlineFlags eOld = meRedlineFlags;
2468 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2469 // The ShowMode needs to be retained!
2470 meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2471 DeleteAndJoin( *pNewRedl );
2472 meRedlineFlags = eOld;
2474 delete pNewRedl, pNewRedl = 0;
2477 // #TODO - equivalent for 'SwTableCellRedline'
2479 CHECK_REDLINE( this )
2482 return nullptr != pNewRedl;
2485 void DocumentRedlineManager::CompressRedlines(size_t nStartIndex)
2487 CHECK_REDLINE( *this )
2489 void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
2490 RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
2491 if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
2492 pFnc = &SwRangeRedline::Show;
2493 else if (eShow == RedlineFlags::ShowInsert)
2494 pFnc = &SwRangeRedline::Hide;
2496 // Try to merge identical ones
2497 if (nStartIndex == 0)
2498 nStartIndex = 1;
2499 for( SwRedlineTable::size_type n = nStartIndex; n < maRedlineTable.size(); ++n )
2501 SwRangeRedline* pPrev = maRedlineTable[ n-1 ],
2502 * pCur = maRedlineTable[ n ];
2503 auto [pPrevStt,pPrevEnd] = pPrev->StartEnd();
2504 auto [pCurStt, pCurEnd] = pCur->StartEnd();
2506 if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
2507 pPrevStt->GetNode().StartOfSectionNode() ==
2508 pCurEnd->GetNode().StartOfSectionNode() &&
2509 !pCurEnd->GetNode().StartOfSectionNode()->IsTableNode() )
2511 // we then can merge them
2512 SwRedlineTable::size_type nPrevIndex = n-1;
2513 pPrev->Show(0, nPrevIndex);
2514 pCur->Show(0, n);
2516 pPrev->SetEnd( *pCur->End() );
2517 maRedlineTable.DeleteAndDestroy( n );
2518 --n;
2519 if( pFnc )
2520 (pPrev->*pFnc)(0, nPrevIndex, false);
2523 CHECK_REDLINE( *this )
2525 // #TODO - add 'SwExtraRedlineTable' also ?
2528 bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
2530 bool bChg = false;
2531 auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
2532 SwRedlineTable::size_type n = 0;
2533 //FIXME overlapping problem GetRedline( *pStt, &n );
2534 for ( ; n < maRedlineTable.size(); ++n)
2536 SwRangeRedline * pRedline = maRedlineTable[ n ];
2537 auto [pRedlineStart, pRedlineEnd] = pRedline->StartEnd();
2538 if (*pRedlineStart <= *pStt && *pEnd <= *pRedlineEnd)
2540 bChg = true;
2541 int nn = 0;
2542 if (*pStt == *pRedlineStart)
2543 nn += 1;
2544 if (*pEnd == *pRedlineEnd)
2545 nn += 2;
2547 SwRangeRedline* pNew = nullptr;
2548 switch( nn )
2550 case 0:
2551 pNew = new SwRangeRedline( *pRedline );
2552 pRedline->SetEnd( *pStt, pRedlineEnd );
2553 pNew->SetStart( *pEnd );
2554 break;
2556 case 1:
2557 *pRedlineStart = *pEnd;
2558 break;
2560 case 2:
2561 *pRedlineEnd = *pStt;
2562 break;
2564 case 3:
2565 pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2566 maRedlineTable.DeleteAndDestroy( n-- );
2567 pRedline = nullptr;
2568 break;
2570 if (pRedline && !pRedline->HasValidRange())
2572 // re-insert
2573 maRedlineTable.Remove( n );
2574 maRedlineTable.Insert( pRedline, n );
2576 if( pNew )
2577 maRedlineTable.Insert( pNew, n );
2579 else if (*pEnd < *pRedlineStart)
2580 break;
2582 return bChg;
2584 // #TODO - add 'SwExtraRedlineTable' also ?
2587 bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
2588 RedlineType nDelType )
2590 if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
2591 return false;
2593 bool bChg = false;
2595 if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2597 std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
2598 if( pUndo->GetRedlSaveCount() )
2600 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2604 auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
2605 SwRedlineTable::size_type n = 0;
2606 GetRedline( *pStt, &n );
2607 for( ; n < maRedlineTable.size() ; ++n )
2609 SwRangeRedline* pRedl = maRedlineTable[ n ];
2610 if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
2611 continue;
2613 auto [pRStt, pREnd] = pRedl->StartEnd(); // SwPosition*
2614 switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
2616 case SwComparePosition::Equal:
2617 case SwComparePosition::Outside:
2618 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2619 maRedlineTable.DeleteAndDestroy( n-- );
2620 bChg = true;
2621 break;
2623 case SwComparePosition::OverlapBefore:
2624 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2625 pRedl->SetStart( *pEnd, pRStt );
2626 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2627 // re-insert
2628 maRedlineTable.Remove( n );
2629 maRedlineTable.Insert( pRedl );
2630 --n;
2631 break;
2633 case SwComparePosition::OverlapBehind:
2634 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2635 pRedl->SetEnd( *pStt, pREnd );
2636 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2637 if( !pRedl->HasValidRange() )
2639 // re-insert
2640 maRedlineTable.Remove( n );
2641 maRedlineTable.Insert( pRedl );
2642 --n;
2644 break;
2646 case SwComparePosition::Inside:
2648 // this one needs to be split
2649 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2650 if( *pRStt == *pStt )
2652 pRedl->SetStart( *pEnd, pRStt );
2653 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2654 // re-insert
2655 maRedlineTable.Remove( n );
2656 maRedlineTable.Insert( pRedl );
2657 --n;
2659 else
2661 SwRangeRedline* pCpy;
2662 if( *pREnd != *pEnd )
2664 pCpy = new SwRangeRedline( *pRedl );
2665 pCpy->SetStart( *pEnd );
2666 pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
2668 else
2669 pCpy = nullptr;
2670 pRedl->SetEnd( *pStt, pREnd );
2671 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2672 if( !pRedl->HasValidRange() )
2674 // re-insert
2675 maRedlineTable.Remove( n );
2676 maRedlineTable.Insert( pRedl );
2677 --n;
2679 if( pCpy )
2680 maRedlineTable.Insert( pCpy );
2683 break;
2685 case SwComparePosition::CollideEnd:
2686 // remove (not hidden) empty redlines created for fixing tdf#119571
2687 // (Note: hidden redlines are all empty, i.e. start and end are equal.)
2688 if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
2690 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2691 maRedlineTable.DeleteAndDestroy( n-- );
2692 bChg = true;
2693 break;
2695 [[fallthrough]];
2697 case SwComparePosition::Before:
2698 n = maRedlineTable.size();
2699 break;
2700 default:
2701 break;
2705 if( bChg )
2706 m_rDoc.getIDocumentState().SetModified();
2708 return bChg;
2710 // #TODO - add 'SwExtraRedlineTable' also ?
2713 bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
2714 RedlineType nDelType )
2716 SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
2717 return DeleteRedline(aTemp, bSaveInUndo, nDelType);
2720 SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
2722 const SwNodeOffset nNdIdx = rNd.GetIndex();
2723 // if the table only contains good (i.e. non-overlapping) data, we can do a binary search
2724 if (!maRedlineTable.HasOverlappingElements())
2726 // binary search to the first redline with end >= the needle
2727 auto it = std::lower_bound(maRedlineTable.begin(), maRedlineTable.end(), rNd,
2728 [&nNdIdx](const SwRangeRedline* lhs, const SwNode& /*rhs*/)
2730 return lhs->End()->GetNodeIndex() < nNdIdx;
2732 for( ; it != maRedlineTable.end(); ++it)
2734 const SwRangeRedline* pTmp = *it;
2735 auto [pStart, pEnd] = pTmp->StartEnd(); // SwPosition*
2736 SwNodeOffset nStart = pStart->GetNodeIndex(),
2737 nEnd = pEnd->GetNodeIndex();
2739 if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
2740 nStart <= nNdIdx && nNdIdx <= nEnd )
2741 return std::distance(maRedlineTable.begin(), it);
2743 if( nStart > nNdIdx )
2744 break;
2747 else
2749 for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
2751 const SwRangeRedline* pTmp = maRedlineTable[ n ];
2752 SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(),
2753 nMk = pTmp->GetMark()->GetNodeIndex();
2754 if( nPt < nMk )
2755 std::swap( nMk, nPt );
2757 if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
2758 nMk <= nNdIdx && nNdIdx <= nPt )
2759 return n;
2761 if( nMk > nNdIdx )
2762 break;
2765 return SwRedlineTable::npos;
2767 // #TODO - add 'SwExtraRedlineTable' also ?
2770 bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const
2772 SwPosition currentStart(*rPam.Start());
2773 SwPosition currentEnd(*rPam.End());
2774 const SwNode& rEndNode(currentEnd.GetNode());
2776 for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->GetNode(), nType );
2777 n < maRedlineTable.size(); ++n )
2779 const SwRangeRedline* pTmp = maRedlineTable[ n ];
2781 if ( pTmp->Start()->GetNode() > rEndNode )
2782 break;
2784 if( RedlineType::Any != nType && nType != pTmp->GetType() )
2785 continue;
2787 // redline over the range
2788 if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
2789 // starting or ending within the range
2790 ( !bStartOrEndInRange ||
2791 ( currentStart < *pTmp->Start() || *pTmp->End() < currentEnd ) ) )
2793 return true;
2796 return false;
2799 const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
2800 SwRedlineTable::size_type* pFndPos ) const
2802 SwRedlineTable::size_type nO = maRedlineTable.size(), nM, nU = 0;
2803 if( nO > 0 )
2805 nO--;
2806 while( nU <= nO )
2808 nM = nU + ( nO - nU ) / 2;
2809 const SwRangeRedline* pRedl = maRedlineTable[ nM ];
2810 auto [pStt, pEnd] = pRedl->StartEnd();
2811 if( pEnd == pStt
2812 ? *pStt == rPos
2813 : ( *pStt <= rPos && rPos < *pEnd ) )
2815 while( nM && rPos == *maRedlineTable[ nM - 1 ]->End() &&
2816 rPos == *maRedlineTable[ nM - 1 ]->Start() )
2818 --nM;
2819 pRedl = maRedlineTable[ nM ];
2821 // if there are format and insert changes in the same position
2822 // show insert change first.
2823 // since the redlines are sorted by position, only check the redline
2824 // before and after the current redline
2825 if( RedlineType::Format == pRedl->GetType() )
2827 if( nM && rPos >= *maRedlineTable[ nM - 1 ]->Start() &&
2828 rPos <= *maRedlineTable[ nM - 1 ]->End() &&
2829 ( RedlineType::Insert == maRedlineTable[ nM - 1 ]->GetType() ) )
2831 --nM;
2832 pRedl = maRedlineTable[ nM ];
2834 else if( ( nM + 1 ) <= nO && rPos >= *maRedlineTable[ nM + 1 ]->Start() &&
2835 rPos <= *maRedlineTable[ nM + 1 ]->End() &&
2836 ( RedlineType::Insert == maRedlineTable[ nM + 1 ]->GetType() ) )
2838 ++nM;
2839 pRedl = maRedlineTable[ nM ];
2843 if( pFndPos )
2844 *pFndPos = nM;
2845 return pRedl;
2847 else if( *pEnd <= rPos )
2848 nU = nM + 1;
2849 else if( nM == 0 )
2851 if( pFndPos )
2852 *pFndPos = nU;
2853 return nullptr;
2855 else
2856 nO = nM - 1;
2859 if( pFndPos )
2860 *pFndPos = nU;
2861 return nullptr;
2863 // #TODO - add 'SwExtraRedlineTable' also ?
2866 bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
2868 bool bRet = false;
2870 // Switch to visible in any case
2871 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2872 (RedlineFlags::ShowMask & meRedlineFlags) )
2873 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2875 SwRangeRedline* pTmp = maRedlineTable[ nPos ];
2876 pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
2877 pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
2878 if( pTmp->HasMark() && pTmp->IsVisible() )
2880 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2882 SwRewriter aRewriter;
2884 aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
2885 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
2888 int nLoopCnt = 2;
2889 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
2891 do {
2893 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2895 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2896 std::make_unique<SwUndoAcceptRedline>(*pTmp) );
2899 bRet |= lcl_AcceptRedline( maRedlineTable, nPos, bCallDelete );
2901 if( nSeqNo )
2903 if( SwRedlineTable::npos == nPos )
2904 nPos = 0;
2905 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
2906 ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
2907 : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
2908 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
2909 SwRedlineTable::npos != ( nFndPos =
2910 maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
2912 nPos = nFndPos;
2913 pTmp = maRedlineTable[ nPos ];
2915 else
2916 nLoopCnt = 0;
2918 else
2919 nLoopCnt = 0;
2921 } while( nLoopCnt );
2923 if( bRet )
2925 CompressRedlines();
2926 m_rDoc.getIDocumentState().SetModified();
2929 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2931 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
2934 return bRet;
2936 // #TODO - add 'SwExtraRedlineTable' also ?
2939 bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
2941 // Switch to visible in any case
2942 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2943 (RedlineFlags::ShowMask & meRedlineFlags) )
2944 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2946 // The Selection is only in the ContentSection. If there are Redlines
2947 // to Non-ContentNodes before or after that, then the Selections
2948 // expand to them.
2949 std::shared_ptr<SwUnoCursor> const pPam(m_rDoc.CreateUnoCursor(*rPam.GetPoint(), false));
2950 if (rPam.HasMark())
2952 pPam->SetMark();
2953 *pPam->GetMark() = *rPam.GetMark();
2955 lcl_AdjustRedlineRange(*pPam);
2957 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2959 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
2960 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAcceptRedline>(*pPam));
2963 int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, maRedlineTable,
2964 bCallDelete, *pPam );
2965 if( nRet > 0 )
2967 CompressRedlines();
2968 m_rDoc.getIDocumentState().SetModified();
2970 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2972 OUString aTmpStr;
2975 SwRewriter aRewriter;
2976 aRewriter.AddRule(UndoArg1, OUString::number(nRet));
2977 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2980 SwRewriter aRewriter;
2981 aRewriter.AddRule(UndoArg1, aTmpStr);
2983 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
2985 return nRet != 0;
2987 // #TODO - add 'SwExtraRedlineTable' also ?
2990 void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
2992 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
2994 const SwNodeOffset nSttIdx = pStt->GetNodeIndex();
2995 const SwNodeOffset nEndIdx = pEnd->GetNodeIndex();
2997 for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
2999 const SwRangeRedline* pTmp = maRedlineTable[ n ];
3000 SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(),
3001 nMk = pTmp->GetMark()->GetNodeIndex();
3002 if( nPt < nMk )
3003 std::swap( nMk, nPt );
3005 if( RedlineType::ParagraphFormat == pTmp->GetType() &&
3006 ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
3007 AcceptRedline( n, false );
3009 if( nMk > nEndIdx )
3010 break;
3014 bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
3016 bool bRet = false;
3018 // Switch to visible in any case
3019 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
3020 (RedlineFlags::ShowMask & meRedlineFlags) )
3021 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
3023 SwRangeRedline* pTmp = maRedlineTable[ nPos ];
3024 pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
3025 pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
3026 if( pTmp->HasMark() && pTmp->IsVisible() )
3028 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3030 SwRewriter aRewriter;
3032 aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
3033 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
3036 int nLoopCnt = 2;
3037 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
3039 do {
3041 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3043 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
3044 std::make_unique<SwUndoRejectRedline>( *pTmp ) );
3047 bRet |= lcl_RejectRedline( maRedlineTable, nPos, bCallDelete );
3049 if( nSeqNo )
3051 if( SwRedlineTable::npos == nPos )
3052 nPos = 0;
3053 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
3054 ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
3055 : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
3056 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
3057 SwRedlineTable::npos != ( nFndPos =
3058 maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
3060 nPos = nFndPos;
3061 pTmp = maRedlineTable[ nPos ];
3063 else
3064 nLoopCnt = 0;
3066 else
3067 nLoopCnt = 0;
3069 } while( nLoopCnt );
3071 if( bRet )
3073 CompressRedlines();
3074 m_rDoc.getIDocumentState().SetModified();
3077 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3079 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
3082 return bRet;
3084 // #TODO - add 'SwExtraRedlineTable' also ?
3087 bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
3089 // Switch to visible in any case
3090 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
3091 (RedlineFlags::ShowMask & meRedlineFlags) )
3092 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
3094 // The Selection is only in the ContentSection. If there are Redlines
3095 // to Non-ContentNodes before or after that, then the Selections
3096 // expand to them.
3097 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
3098 lcl_AdjustRedlineRange( aPam );
3100 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3102 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
3103 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) );
3106 int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, maRedlineTable,
3107 bCallDelete, aPam );
3108 if( nRet > 0 )
3110 CompressRedlines();
3111 m_rDoc.getIDocumentState().SetModified();
3113 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3115 OUString aTmpStr;
3118 SwRewriter aRewriter;
3119 aRewriter.AddRule(UndoArg1, OUString::number(nRet));
3120 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
3123 SwRewriter aRewriter;
3124 aRewriter.AddRule(UndoArg1, aTmpStr);
3126 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
3129 return nRet != 0;
3131 // #TODO - add 'SwExtraRedlineTable' also ?
3134 void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
3136 bool bSuccess = true;
3137 OUString sUndoStr;
3138 IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
3140 if (maRedlineTable.size() > 1)
3143 SwRewriter aRewriter;
3144 aRewriter.AddRule(UndoArg1, OUString::number(maRedlineTable.size()));
3145 sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
3148 SwRewriter aRewriter;
3149 aRewriter.AddRule(UndoArg1, sUndoStr);
3150 rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
3153 while (!maRedlineTable.empty() && bSuccess)
3155 if (bAccept)
3156 bSuccess = AcceptRedline(maRedlineTable.size() - 1, true);
3157 else
3158 bSuccess = RejectRedline(maRedlineTable.size() - 1, true);
3161 if (!sUndoStr.isEmpty())
3163 rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
3167 const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
3169 rPam.DeleteMark();
3170 rPam.SetMark();
3172 SwPosition& rSttPos = *rPam.GetPoint();
3173 SwPosition aSavePos( rSttPos );
3174 bool bRestart;
3176 // If the starting position points to the last valid ContentNode,
3177 // we take the next Redline in any case.
3178 SwRedlineTable::size_type n = 0;
3179 const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
3180 if( pFnd )
3182 const SwPosition* pEnd = pFnd->End();
3183 if( !pEnd->GetNode().IsContentNode() )
3185 SwNodeIndex aTmp( pEnd->GetNode() );
3186 SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
3187 if( !pCNd || ( aTmp == rSttPos.GetNode() &&
3188 pCNd->Len() == rSttPos.GetContentIndex() ))
3189 pFnd = nullptr;
3191 if( pFnd )
3192 rSttPos = *pFnd->End();
3195 do {
3196 bRestart = false;
3198 for( ; !pFnd && n < maRedlineTable.size(); ++n )
3200 pFnd = maRedlineTable[ n ];
3201 if( pFnd->HasMark() && pFnd->IsVisible() )
3203 *rPam.GetMark() = *pFnd->Start();
3204 rSttPos = *pFnd->End();
3205 break;
3207 else
3208 pFnd = nullptr;
3211 if( pFnd )
3213 // Merge all of the same type and author that are
3214 // consecutive into one Selection.
3215 const SwPosition* pPrevEnd = pFnd->End();
3216 while( ++n < maRedlineTable.size() )
3218 const SwRangeRedline* pTmp = maRedlineTable[ n ];
3219 if( pTmp->HasMark() && pTmp->IsVisible() )
3221 const SwPosition *pRStt;
3222 if( pFnd->GetType() != pTmp->GetType() ||
3223 pFnd->GetAuthor() != pTmp->GetAuthor() )
3224 break;
3225 pRStt = pTmp->Start();
3226 if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
3228 pPrevEnd = pTmp->End();
3229 rSttPos = *pPrevEnd;
3231 else
3232 break;
3237 if( pFnd )
3239 const SwRangeRedline* pSaveFnd = pFnd;
3241 SwContentNode* pCNd;
3242 SwPosition* pPos = rPam.GetMark();
3243 if( !pPos->GetNode().IsContentNode() )
3245 pCNd = m_rDoc.GetNodes().GoNextSection( pPos );
3246 if( pCNd )
3248 if( pPos->GetNode() <= rPam.GetPoint()->GetNode() )
3249 pPos->Assign( *pCNd, 0 );
3250 else
3251 pFnd = nullptr;
3255 if( pFnd )
3257 pPos = rPam.GetPoint();
3258 if( !pPos->GetNode().IsContentNode() )
3260 pCNd = SwNodes::GoPrevSection( pPos );
3261 if( pCNd )
3263 if( pPos->GetNode() >= rPam.GetMark()->GetNode() )
3264 pPos->Assign( *pCNd, pCNd->Len() );
3265 else
3266 pFnd = nullptr;
3271 if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3273 if( n < maRedlineTable.size() )
3275 bRestart = true;
3276 *rPam.GetPoint() = *pSaveFnd->End();
3278 else
3280 rPam.DeleteMark();
3281 *rPam.GetPoint() = aSavePos;
3283 pFnd = nullptr;
3286 } while( bRestart );
3288 return pFnd;
3290 // #TODO - add 'SwExtraRedlineTable' also ?
3293 const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
3295 rPam.DeleteMark();
3296 rPam.SetMark();
3298 SwPosition& rSttPos = *rPam.GetPoint();
3299 SwPosition aSavePos( rSttPos );
3300 bool bRestart;
3302 // If the starting position points to the last valid ContentNode,
3303 // we take the previous Redline in any case.
3304 SwRedlineTable::size_type n = 0;
3305 const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
3306 if( pFnd )
3308 const SwPosition* pStt = pFnd->Start();
3309 if( !pStt->GetNode().IsContentNode() )
3311 SwNodeIndex aTmp( pStt->GetNode() );
3312 SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
3313 if( !pCNd || ( aTmp == rSttPos.GetNode() &&
3314 !rSttPos.GetContentIndex() ))
3315 pFnd = nullptr;
3317 if( pFnd )
3318 rSttPos = *pFnd->Start();
3321 do {
3322 bRestart = false;
3324 while( !pFnd && 0 < n )
3326 pFnd = maRedlineTable[ --n ];
3327 if( pFnd->HasMark() && pFnd->IsVisible() )
3329 *rPam.GetMark() = *pFnd->End();
3330 rSttPos = *pFnd->Start();
3332 else
3333 pFnd = nullptr;
3336 if( pFnd )
3338 // Merge all of the same type and author that are
3339 // consecutive into one Selection.
3340 const SwPosition* pNextStt = pFnd->Start();
3341 while( 0 < n )
3343 const SwRangeRedline* pTmp = maRedlineTable[ --n ];
3344 if( pTmp->HasMark() && pTmp->IsVisible() )
3346 const SwPosition *pREnd;
3347 if( pFnd->GetType() == pTmp->GetType() &&
3348 pFnd->GetAuthor() == pTmp->GetAuthor() &&
3349 ( *pNextStt == *( pREnd = pTmp->End() ) ||
3350 IsPrevPos( *pREnd, *pNextStt )) )
3352 pNextStt = pTmp->Start();
3353 rSttPos = *pNextStt;
3355 else
3357 ++n;
3358 break;
3364 if( pFnd )
3366 const SwRangeRedline* pSaveFnd = pFnd;
3368 SwContentNode* pCNd;
3369 SwPosition* pPos = rPam.GetMark();
3370 if( !pPos->GetNode().IsContentNode() )
3372 pCNd = SwNodes::GoPrevSection( pPos );
3373 if( pCNd )
3375 if( pPos->GetNode() >= rPam.GetPoint()->GetNode() )
3376 pPos->Assign( *pCNd, pCNd->Len() );
3377 else
3378 pFnd = nullptr;
3382 if( pFnd )
3384 pPos = rPam.GetPoint();
3385 if( !pPos->GetNode().IsContentNode() )
3387 pCNd = m_rDoc.GetNodes().GoNextSection( pPos );
3388 if( pCNd )
3390 if( pPos->GetNode() <= rPam.GetMark()->GetNode() )
3391 pPos->Assign( *pCNd, 0 );
3392 else
3393 pFnd = nullptr;
3398 if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3400 if( n )
3402 bRestart = true;
3403 *rPam.GetPoint() = *pSaveFnd->Start();
3405 else
3407 rPam.DeleteMark();
3408 *rPam.GetPoint() = aSavePos;
3410 pFnd = nullptr;
3413 } while( bRestart );
3415 return pFnd;
3417 // #TODO - add 'SwExtraRedlineTable' also ?
3420 // Set comment at the Redline
3421 bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
3423 bool bRet = false;
3424 auto [pStt, pEnd] = rPaM.StartEnd(); // SwPosition*
3425 SwRedlineTable::size_type n = 0;
3426 if( GetRedlineTable().FindAtPosition( *pStt, n ) )
3428 for( ; n < maRedlineTable.size(); ++n )
3430 bRet = true;
3431 SwRangeRedline* pTmp = maRedlineTable[ n ];
3432 if( pStt != pEnd && *pTmp->Start() > *pEnd )
3433 break;
3435 pTmp->SetComment( rS );
3436 if( *pTmp->End() >= *pEnd )
3437 break;
3440 if( bRet )
3441 m_rDoc.getIDocumentState().SetModified();
3443 return bRet;
3445 // #TODO - add 'SwExtraRedlineTable' also ?
3448 // Create a new author if necessary
3449 std::size_t DocumentRedlineManager::GetRedlineAuthor()
3451 return SW_MOD()->GetRedlineAuthor();
3454 /// Insert new author into the Table for the Readers etc.
3455 std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
3457 return SW_MOD()->InsertRedlineAuthor(rNew);
3460 void DocumentRedlineManager::UpdateRedlineAttr()
3462 const SwRedlineTable& rTable = GetRedlineTable();
3463 for(SwRangeRedline* pRedl : rTable)
3465 if( pRedl->IsVisible() )
3466 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
3469 // #TODO - add 'SwExtraRedlineTable' also ?
3472 const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
3474 return maRedlinePasswd;
3477 void DocumentRedlineManager::SetRedlinePassword(
3478 /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
3480 maRedlinePasswd = rNewPassword;
3481 m_rDoc.getIDocumentState().SetModified();
3484 /// Set comment text for the Redline, which is inserted later on via
3485 /// AppendRedline. Is used by Autoformat.
3486 /// A null pointer resets the mode. The pointer is not copied, so it
3487 /// needs to stay valid!
3488 void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
3490 m_rDoc.SetAutoFormatRedline( nullptr != pText );
3491 if( pText )
3493 moAutoFormatRedlnComment = *pText;
3495 else
3497 moAutoFormatRedlnComment.reset();
3500 mnAutoFormatRedlnCommentNo = nSeqNo;
3503 void DocumentRedlineManager::HideAll( bool bDeletion )
3505 const SwRedlineTable& rTable = GetRedlineTable();
3506 for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3508 SwRangeRedline* pRedline = rTable[i-1];
3509 if ( pRedline->GetType() == RedlineType::Delete )
3511 if ( bDeletion && pRedline->IsVisible() )
3513 pRedline->Hide(0, rTable.GetPos(pRedline), false);
3514 pRedline->Hide(1, rTable.GetPos(pRedline), false);
3516 else if ( !bDeletion && !pRedline->IsVisible() )
3518 pRedline->Show(0, rTable.GetPos(pRedline), true);
3519 pRedline->Show(1, rTable.GetPos(pRedline), true);
3522 else if ( pRedline->GetType() == RedlineType::Insert )
3524 if ( !bDeletion && pRedline->IsVisible() )
3526 pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
3527 pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
3529 else if ( bDeletion && !pRedline->IsVisible() )
3531 pRedline->Show(0, rTable.GetPos(pRedline), true);
3532 pRedline->Show(1, rTable.GetPos(pRedline), true);
3538 void DocumentRedlineManager::ShowAll()
3540 const SwRedlineTable& rTable = GetRedlineTable();
3541 for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3543 SwRangeRedline* pRedline = rTable[i-1];
3544 if ( !pRedline->IsVisible() )
3546 pRedline->Show(0, rTable.GetPos(pRedline), true);
3547 pRedline->Show(1, rTable.GetPos(pRedline), true);
3552 DocumentRedlineManager::~DocumentRedlineManager()
3558 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */