1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include <DocumentRedlineManager.hxx>
21 #include <rootfrm.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>
36 #include <unocrsr.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
;
48 #define ERROR_PREFIX "redline table corrupted: "
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);
67 assert(pPos
->GetContentIndex() >= 0 && pPos
->GetContentIndex() <= pTextNode
->Len());
71 void lcl_CheckPam( const SwPaM
* 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
)
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 );
121 #define CHECK_REDLINE( pDoc )
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())
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());
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)");
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
);
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);
209 auto const& layouts(rDoc
.GetAllLayouts());
210 assert(std::none_of(layouts
.begin(), layouts
.end(),
211 [](SwRootFrame
const*const pLayout
) { return pLayout
->IsHideRedlines(); }));
215 auto eMode(sw::FrameMode::Existing
);
216 SwTextNode
* pLast(pNode
);
217 for (SwTextFrame
* pFrame
: frames
)
219 SwTextNode
& rFirstNode(pFrame
->GetMergedPara()
220 ? *pFrame
->GetMergedPara()->pFirstNode
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())
260 bool isAppendObjsCalled(false);
261 rDoc
.GetFootnoteIdxs().UpdateFootnote(rPam
.Start()->GetNode());
262 SwPosition
currentStart(*rPam
.Start());
263 SwTextNode
* pStartNode(rPam
.Start()->GetNode().GetTextNode());
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);
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(); }));
318 isAppendObjsCalled
= true; // skip that!
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
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
);
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
) );
395 bool IsPrevPos( const SwPosition
& rPos1
, const SwPosition
& rPos2
)
397 const SwContentNode
* pCNd
;
398 if( 0 != rPos2
.GetContentIndex() )
400 if( rPos2
.GetNodeIndex() - 1 != rPos1
.GetNodeIndex() )
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
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
;
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
);
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();
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
);
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
);
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();
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
,
555 const SwPosition
* pSttRng
= nullptr,
556 const SwPosition
* pEndRng
= nullptr )
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;
579 case SwComparePosition::Inside
:
580 if( *pSttRng
== *pRStt
)
581 pRedl
->SetStart( *pEndRng
, pRStt
);
584 if( *pEndRng
!= *pREnd
)
587 SwRangeRedline
* pNew
= new SwRangeRedline( *pRedl
);
588 pNew
->SetStart( *pEndRng
);
589 rArr
.Insert( pNew
); ++rPos
;
591 pRedl
->SetEnd( *pSttRng
, pREnd
);
596 case SwComparePosition::OverlapBefore
:
597 pRedl
->SetStart( *pEndRng
, pRStt
);
601 case SwComparePosition::OverlapBehind
:
602 pRedl
->SetEnd( *pSttRng
, pREnd
);
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
615 lcl_RemoveTrackingOfTableRow( &aPos
, /*bRejectDelete=*/false );
623 if( bReplace
|| ( bCheck
&& !pRedl
->HasValidRange() ))
626 rArr
.Remove( pRedl
);
627 rArr
.Insert( pRedl
);
631 case RedlineType::Delete
:
633 SwDoc
& rDoc
= pRedl
->GetDoc();
634 const SwPosition
*pDelStt
= nullptr, *pDelEnd
= nullptr;
635 bool bDelRedl
= false;
638 case SwComparePosition::Inside
:
646 case SwComparePosition::OverlapBefore
:
653 case SwComparePosition::OverlapBehind
:
661 case SwComparePosition::Outside
:
662 case SwComparePosition::Equal
:
664 rArr
.Remove( rPos
-- );
668 pDelStt
= pRedl
->Start();
669 pDelEnd
= pRedl
->End();
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
);
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
);
708 rDoc
.getIDocumentContentOperations().DeleteRange(aPam
);
710 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
717 case RedlineType::FmtColl
:
718 rArr
.DeleteAndDestroy( rPos
-- );
721 case RedlineType::ParagraphFormat
:
722 rArr
.DeleteAndDestroy( rPos
-- );
731 bool lcl_RejectRedline( SwRedlineTable
& rArr
, SwRedlineTable::size_type
& rPos
,
733 const SwPosition
* pSttRng
= nullptr,
734 const SwPosition
* pEndRng
= nullptr )
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;
758 case SwComparePosition::Inside
:
766 case SwComparePosition::OverlapBefore
:
773 case SwComparePosition::OverlapBehind
:
780 case SwComparePosition::Outside
:
781 case SwComparePosition::Equal
:
783 // delete the range again
784 rArr
.Remove( rPos
-- );
788 pDelStt
= pRedl
->Start();
789 pDelEnd
= pRedl
->End();
797 if( pDelStt
&& pDelEnd
)
799 SwPaM
aPam( *pDelStt
, *pDelEnd
);
801 SwContentNode
* pCSttNd
= pDelStt
->GetNode().GetContentNode();
802 SwContentNode
* pCEndNd
= pDelEnd
->GetNode().GetContentNode();
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
);
828 rDoc
.getIDocumentContentOperations().DeleteRange(aPam
);
830 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
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 );
851 case SwComparePosition::Inside
:
853 if( 1 < pRedl
->GetStackCount() )
855 pNew
= new SwRangeRedline( *pRedl
);
858 if( *pSttRng
== *pRStt
)
860 pRedl
->SetStart( *pEndRng
, pRStt
);
863 pNew
->SetEnd( *pEndRng
);
867 if( *pEndRng
!= *pREnd
)
870 SwRangeRedline
* pCpy
= new SwRangeRedline( *pRedl
);
871 pCpy
->SetStart( *pEndRng
);
872 rArr
.Insert( pCpy
); ++rPos
;
874 pNew
->SetEnd( *pEndRng
);
877 pRedl
->SetEnd( *pSttRng
, pREnd
);
880 pNew
->SetStart( *pSttRng
);
885 case SwComparePosition::OverlapBefore
:
886 if( 1 < pRedl
->GetStackCount() )
888 pNew
= new SwRangeRedline( *pRedl
);
891 pRedl
->SetStart( *pEndRng
, pRStt
);
894 pNew
->SetEnd( *pEndRng
);
897 case SwComparePosition::OverlapBehind
:
898 if( 1 < pRedl
->GetStackCount() )
900 pNew
= new SwRangeRedline( *pRedl
);
903 pRedl
->SetEnd( *pSttRng
, pREnd
);
906 pNew
->SetStart( *pSttRng
);
909 case SwComparePosition::Outside
:
910 case SwComparePosition::Equal
:
911 if( !pRedl
->PopData() )
912 // deleting the RedlineObject is enough
913 rArr
.DeleteAndDestroy( rPos
-- );
922 rArr
.Insert( pNew
); ++rPos
;
925 if( bReplace
|| ( bCheck
&& !pRedl
->HasValidRange() ))
928 rArr
.Remove( pRedl
);
929 rArr
.Insert( pRedl
);
932 sw::UpdateFramesForRemoveDeleteRedline(rDoc
, updatePaM
);
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();
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
);
977 if ( pTNd
->GetNumRule() )
978 rDoc
.DelNumRules(aPam
);
982 if( pRedl
->GetExtraData() )
983 pRedl
->GetExtraData()->Reject( *pRedl
);
985 rArr
.DeleteAndDestroy( rPos
-- );
995 typedef bool (*Fn_AcceptReject
)( SwRedlineTable
& rArr
, SwRedlineTable::size_type
& rPos
,
997 const SwPosition
* pSttRng
,
998 const SwPosition
* pEndRng
);
1001 int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject
,
1002 SwRedlineTable
& rArr
, bool bCallDelete
,
1005 SwRedlineTable::size_type n
= 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
))
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;
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;
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
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 );
1072 const SwPosition
* pRStt
= pRedl
->Start();
1073 if( !pRStt
->GetContentIndex() && pRStt
->GetNodeIndex() ==
1074 pStt
->GetNodeIndex() - 1 )
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 );
1085 const SwPosition
* pREnd
= pRedl
->End();
1086 if( !pREnd
->GetContentIndex() && pREnd
->GetNodeIndex() ==
1087 pEnd
->GetNodeIndex() + 1 )
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
1098 SwRangeRedline
& m_rRedline
;
1099 std::shared_ptr
<SwUnoCursor
> m_pCursor
;
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
;
1123 DocumentRedlineManager::DocumentRedlineManager(SwDoc
& 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
)
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
;
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())
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
1295 - Text and other Insert overlap - insert Redline Object, the
1296 other Insert is overlapped by
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
;
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 );
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);
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
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
)
1384 const SwRedlineTable::size_type nStartPos
= n
;
1387 for( ; pNewRedl
&& n
< maRedlineTable
.size(); bDec
? n
: ++n
)
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
);
1402 SwComparePosition eCmpPos
= ComparePosition( *pStt
, *pEnd
, *pRStt
, *pREnd
);
1404 if ( SwComparePosition::Before
== eCmpPos
&& !IsPrevPos( *pEnd
, *pRStt
))
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
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() )
1433 maRedlineTable
.Remove( n
);
1434 maRedlineTable
.Insert( pRedl
);
1440 else if( (( SwComparePosition::Before
== eCmpPos
&&
1441 IsPrevPos( *pEnd
, *pRStt
) ) ||
1442 ( SwComparePosition::CollideEnd
== eCmpPos
) ||
1443 ( SwComparePosition::OverlapBefore
== eCmpPos
) ) &&
1444 pRedl
->CanCombine( *pNewRedl
) &&
1446 *maRedlineTable
[ n
-1 ]->End() != *pRStt
))
1448 pRedl
->SetStart( *pStt
, pRStt
);
1450 maRedlineTable
.Remove( n
);
1451 maRedlineTable
.Insert( pRedl
);
1456 else if ( SwComparePosition::Outside
== eCmpPos
)
1458 // own insert-over-insert redlines:
1459 // just scrap the inside ones
1460 maRedlineTable
.DeleteAndDestroy( n
);
1463 else if( SwComparePosition::OverlapBehind
== eCmpPos
)
1466 if( ( *pStt
== *pEnd
) &&
1467 ( pNewRedl
->GetContentIdx() == nullptr ) )
1470 else if( SwComparePosition::OverlapBefore
== eCmpPos
)
1473 if( ( *pStt
== *pEnd
) &&
1474 ( pNewRedl
->GetContentIdx() == nullptr ) )
1477 else if( SwComparePosition::Inside
== eCmpPos
)
1482 else if( SwComparePosition::Equal
== eCmpPos
)
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
)
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
);
1512 else if( !pRedl
->HasValidRange() )
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 )
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
1546 if( ( *pStt
== *pEnd
) &&
1547 ( pNewRedl
->GetContentIdx() == nullptr ) )
1555 case RedlineType::Delete
:
1556 if( SwComparePosition::Inside
== eCmpPos
)
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
);
1572 else if( !pRedl
->HasValidRange() )
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 )
1597 else if ( SwComparePosition::Equal
== eCmpPos
)
1599 // handle identical redlines in broken documents
1600 // delete old (delete) redline
1601 maRedlineTable
.DeleteAndDestroy( n
);
1604 else if ( SwComparePosition::OverlapBehind
== eCmpPos
)
1605 { // Another workaround for broken redlines
1606 pNewRedl
->SetStart( *pREnd
);
1609 case RedlineType::Format
:
1612 case SwComparePosition::OverlapBefore
:
1613 pRedl
->SetStart( *pEnd
, pRStt
);
1615 maRedlineTable
.Remove( n
);
1616 maRedlineTable
.Insert( pRedl
, n
);
1620 case SwComparePosition::OverlapBehind
:
1621 pRedl
->SetEnd( *pStt
, pREnd
);
1622 if( *pStt
== *pRStt
&& pRedl
->GetContentIdx() == nullptr )
1624 maRedlineTable
.DeleteAndDestroy( n
);
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
);
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
1655 pRedl
->SetEnd( *pStt
, pREnd
);
1666 case RedlineType::Delete
:
1667 switch( pRedl
->GetType() )
1669 case RedlineType::Delete
:
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
);
1686 SwRangeRedline
* pNew
= new SwRangeRedline( *pNewRedl
);
1687 pNew
->SetStart( *pREnd
);
1688 pNewRedl
->SetEnd( *pRStt
, pEnd
);
1689 AppendRedline( pNew
, bCallDelete
);
1690 n
= 0; // re-initialize
1696 case SwComparePosition::Inside
:
1697 case SwComparePosition::Equal
:
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
);
1713 pNewRedl
->SetEnd( *pREnd
, pEnd
);
1714 maRedlineTable
.DeleteAndDestroy( n
);
1717 else if( SwComparePosition::OverlapBehind
== eCmpPos
)
1718 pNewRedl
->SetStart( *pREnd
, pStt
);
1720 pNewRedl
->SetEnd( *pRStt
, pEnd
);
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
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
);
1751 pNewRedl
->SetEnd( *pREnd
, pEnd
);
1753 // delete current (below), and restart process with
1755 SwRedlineTable::size_type nToBeDeleted
= n
;
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
1771 maRedlineTable
.DeleteAndDestroy( nToBeDeleted
);
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
);
1796 case SwComparePosition::Equal
:
1798 maRedlineTable
.DeleteAndDestroy( n
);
1802 case SwComparePosition::Inside
:
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
);
1818 m_rDoc
.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl
);
1826 case SwComparePosition::Outside
:
1828 maRedlineTable
.Remove( n
);
1832 TemporaryRedlineUpdater
const u(m_rDoc
, *pNewRedl
);
1833 m_rDoc
.getIDocumentContentOperations().DeleteAndJoin( *pRedl
);
1834 n
= 0; // re-initialize
1840 case SwComparePosition::OverlapBefore
:
1842 SwPaM
aPam( *pRStt
, *pEnd
);
1844 if( *pEnd
== *pREnd
)
1845 maRedlineTable
.DeleteAndDestroy( n
);
1848 pRedl
->SetStart( *pEnd
, pRStt
);
1850 maRedlineTable
.Remove( n
);
1851 maRedlineTable
.Insert( pRedl
, n
);
1856 TemporaryRedlineUpdater
const u(m_rDoc
, *pNewRedl
);
1857 m_rDoc
.getIDocumentContentOperations().DeleteAndJoin( aPam
);
1858 n
= 0; // re-initialize
1864 case SwComparePosition::OverlapBehind
:
1866 SwPaM
aPam( *pStt
, *pREnd
);
1868 if( *pStt
== *pRStt
)
1870 maRedlineTable
.DeleteAndDestroy( n
);
1874 pRedl
->SetEnd( *pStt
, pREnd
);
1878 TemporaryRedlineUpdater
const u(m_rDoc
, *pNewRedl
);
1879 m_rDoc
.getIDocumentContentOperations().DeleteAndJoin( aPam
);
1880 n
= 0; // re-initialize
1889 meRedlineFlags
= eOld
;
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
1897 SwRangeRedline
* pNew
= nullptr;
1901 case SwComparePosition::Equal
:
1903 pRedl
->PushData( *pNewRedl
);
1906 if( IsHideChanges( meRedlineFlags
))
1908 pRedl
->Hide(0, maRedlineTable
.GetPos(pRedl
));
1914 case SwComparePosition::Inside
:
1916 if( *pRStt
== *pStt
)
1919 // redline w/out extent loops
1922 pNewRedl
->PushData( *pRedl
, false );
1923 pRedl
->SetStart( *pEnd
, pRStt
);
1925 maRedlineTable
.Remove( n
);
1926 maRedlineTable
.Insert( pRedl
, n
);
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() )
1942 maRedlineTable
.Remove( n
);
1943 maRedlineTable
.Insert( pRedl
, n
);
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
);
1962 pNew
= new SwRangeRedline( *pNewRedl
);
1963 pNew
->SetEnd( *pRStt
);
1964 pNewRedl
->SetStart( *pREnd
, pStt
);
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
);
1985 pNew
= new SwRangeRedline( *pRedl
);
1986 pNew
->PushData( *pNewRedl
);
1987 pNew
->SetEnd( *pEnd
);
1988 pNewRedl
->SetEnd( *pRStt
, pEnd
);
1989 pRedl
->SetStart( *pNew
->End(), pRStt
) ;
1991 maRedlineTable
.Remove( n
);
1992 maRedlineTable
.Insert( pRedl
);
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
);
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() )
2021 maRedlineTable
.Remove( n
);
2022 maRedlineTable
.Insert( pRedl
);
2031 // insert the pNew part (if it exists)
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)
2049 case RedlineType::Format
:
2052 case SwComparePosition::OverlapBefore
:
2053 pRedl
->SetStart( *pEnd
, pRStt
);
2055 maRedlineTable
.Remove( n
);
2056 maRedlineTable
.Insert( pRedl
, n
);
2060 case SwComparePosition::OverlapBehind
:
2061 pRedl
->SetEnd( *pStt
, pREnd
);
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
);
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
2091 pRedl
->SetEnd( *pStt
, pREnd
);
2102 case RedlineType::Format
:
2103 switch( pRedl
->GetType() )
2105 case RedlineType::Insert
:
2106 case RedlineType::Delete
:
2109 case SwComparePosition::OverlapBefore
:
2110 pNewRedl
->SetEnd( *pRStt
, pEnd
);
2113 case SwComparePosition::OverlapBehind
:
2114 pNewRedl
->SetStart( *pREnd
, pStt
);
2117 case SwComparePosition::Equal
:
2118 case SwComparePosition::Inside
:
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
);
2136 SwRangeRedline
* pNew
= new SwRangeRedline( *pNewRedl
);
2137 pNew
->SetStart( *pREnd
);
2138 pNewRedl
->SetEnd( *pRStt
, pEnd
);
2139 AppendRedline( pNew
, bCallDelete
);
2140 n
= 0; // re-initialize
2148 case RedlineType::Format
:
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
);
2161 case SwComparePosition::Inside
:
2162 if( pRedl
->IsOwnRedline( *pNewRedl
) &&
2163 pRedl
->CanCombine( *pNewRedl
))
2165 // own one can be ignored completely
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
);
2177 maRedlineTable
.Remove( n
);
2178 maRedlineTable
.Insert( pRedl
, n
);
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
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
);
2204 pNewRedl
->SetEnd( *pREnd
, pEnd
);
2205 maRedlineTable
.DeleteAndDestroy( n
);
2208 else if( SwComparePosition::OverlapBehind
== eCmpPos
)
2209 pNewRedl
->SetStart( *pREnd
, pStt
);
2211 pNewRedl
->SetEnd( *pRStt
, pEnd
);
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
);
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
);
2249 case RedlineType::FmtColl
:
2250 // How should we behave here?
2260 if( ( *pStt
== *pEnd
) &&
2261 ( pNewRedl
->GetContentIdx() == nullptr ) )
2262 { // Do not insert empty redlines
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() );
2285 while (pTextNd
!= nullptr && pDelNd
->GetIndex() < pTextNd
->GetIndex())
2287 if( pTextNd
->IsTextNode() )
2289 SwPosition
aPos(aIdx
);
2291 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
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);
2304 std::unique_ptr
<SwRedlineExtraData_FormatColl
> xRedlineExtraData
;
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
);
2319 // modify paragraph formatting
2320 lcl_CopyStyle(*pStt
, aPos
);
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).
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();
2344 pTable
->EndOfSectionNode()->GetIndex() == aIdx
.GetIndex() )
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 );
2382 bool const ret
= maRedlineTable
.Insert( pNewRedl
);
2383 assert(ret
|| !pNewRedl
);
2384 if (ret
&& !pNewRedl
)
2386 bMerged
= true; // treat InsertWithValidRanges as "merge"
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
);
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
);
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)
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
);
2516 pPrev
->SetEnd( *pCur
->End() );
2517 maRedlineTable
.DeleteAndDestroy( n
);
2520 (pPrev
->*pFnc
)(0, nPrevIndex
, false);
2523 CHECK_REDLINE( *this )
2525 // #TODO - add 'SwExtraRedlineTable' also ?
2528 bool DocumentRedlineManager::SplitRedline( const SwPaM
& rRange
)
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
)
2542 if (*pStt
== *pRedlineStart
)
2544 if (*pEnd
== *pRedlineEnd
)
2547 SwRangeRedline
* pNew
= nullptr;
2551 pNew
= new SwRangeRedline( *pRedline
);
2552 pRedline
->SetEnd( *pStt
, pRedlineEnd
);
2553 pNew
->SetStart( *pEnd
);
2557 *pRedlineStart
= *pEnd
;
2561 *pRedlineEnd
= *pStt
;
2565 pRedline
->InvalidateRange(SwRangeRedline::Invalidation::Remove
);
2566 maRedlineTable
.DeleteAndDestroy( n
-- );
2570 if (pRedline
&& !pRedline
->HasValidRange())
2573 maRedlineTable
.Remove( n
);
2574 maRedlineTable
.Insert( pRedline
, n
);
2577 maRedlineTable
.Insert( pNew
, n
);
2579 else if (*pEnd
< *pRedlineStart
)
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() )
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() )
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
-- );
2623 case SwComparePosition::OverlapBefore
:
2624 pRedl
->InvalidateRange(SwRangeRedline::Invalidation::Remove
);
2625 pRedl
->SetStart( *pEnd
, pRStt
);
2626 pRedl
->InvalidateRange(SwRangeRedline::Invalidation::Add
);
2628 maRedlineTable
.Remove( n
);
2629 maRedlineTable
.Insert( pRedl
);
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() )
2640 maRedlineTable
.Remove( n
);
2641 maRedlineTable
.Insert( pRedl
);
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
);
2655 maRedlineTable
.Remove( n
);
2656 maRedlineTable
.Insert( pRedl
);
2661 SwRangeRedline
* pCpy
;
2662 if( *pREnd
!= *pEnd
)
2664 pCpy
= new SwRangeRedline( *pRedl
);
2665 pCpy
->SetStart( *pEnd
);
2666 pCpy
->InvalidateRange(SwRangeRedline::Invalidation::Add
);
2670 pRedl
->SetEnd( *pStt
, pREnd
);
2671 pRedl
->InvalidateRange(SwRangeRedline::Invalidation::Add
);
2672 if( !pRedl
->HasValidRange() )
2675 maRedlineTable
.Remove( n
);
2676 maRedlineTable
.Insert( pRedl
);
2680 maRedlineTable
.Insert( pCpy
);
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
-- );
2697 case SwComparePosition::Before
:
2698 n
= maRedlineTable
.size();
2706 m_rDoc
.getIDocumentState().SetModified();
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
)
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();
2755 std::swap( nMk
, nPt
);
2757 if( ( RedlineType::Any
== nType
|| nType
== pTmp
->GetType()) &&
2758 nMk
<= nNdIdx
&& nNdIdx
<= nPt
)
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
)
2784 if( RedlineType::Any
!= nType
&& nType
!= pTmp
->GetType() )
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
) ) )
2799 const SwRangeRedline
* DocumentRedlineManager::GetRedline( const SwPosition
& rPos
,
2800 SwRedlineTable::size_type
* pFndPos
) const
2802 SwRedlineTable::size_type nO
= maRedlineTable
.size(), nM
, nU
= 0;
2808 nM
= nU
+ ( nO
- nU
) / 2;
2809 const SwRangeRedline
* pRedl
= maRedlineTable
[ nM
];
2810 auto [pStt
, pEnd
] = pRedl
->StartEnd();
2813 : ( *pStt
<= rPos
&& rPos
< *pEnd
) )
2815 while( nM
&& rPos
== *maRedlineTable
[ nM
- 1 ]->End() &&
2816 rPos
== *maRedlineTable
[ nM
- 1 ]->Start() )
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() ) )
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() ) )
2839 pRedl
= maRedlineTable
[ nM
];
2847 else if( *pEnd
<= rPos
)
2863 // #TODO - add 'SwExtraRedlineTable' also ?
2866 bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos
, bool bCallDelete
)
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
);
2889 sal_uInt16 nSeqNo
= pTmp
->GetSeqNo();
2893 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2895 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2896 std::make_unique
<SwUndoAcceptRedline
>(*pTmp
) );
2899 bRet
|= lcl_AcceptRedline( maRedlineTable
, nPos
, bCallDelete
);
2903 if( SwRedlineTable::npos
== nPos
)
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
))) )
2913 pTmp
= maRedlineTable
[ nPos
];
2921 } while( nLoopCnt
);
2926 m_rDoc
.getIDocumentState().SetModified();
2929 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2931 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::END
, nullptr);
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
2949 std::shared_ptr
<SwUnoCursor
> const pPam(m_rDoc
.CreateUnoCursor(*rPam
.GetPoint(), false));
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
);
2968 m_rDoc
.getIDocumentState().SetModified();
2970 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
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
);
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();
3003 std::swap( nMk
, nPt
);
3005 if( RedlineType::ParagraphFormat
== pTmp
->GetType() &&
3006 ( (nSttIdx
<= nMk
&& nMk
<= nEndIdx
) || (nSttIdx
<= nPt
&& nPt
<= nEndIdx
) ) )
3007 AcceptRedline( n
, false );
3014 bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos
, bool bCallDelete
)
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
);
3037 sal_uInt16 nSeqNo
= pTmp
->GetSeqNo();
3041 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3043 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
3044 std::make_unique
<SwUndoRejectRedline
>( *pTmp
) );
3047 bRet
|= lcl_RejectRedline( maRedlineTable
, nPos
, bCallDelete
);
3051 if( SwRedlineTable::npos
== nPos
)
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
))) )
3061 pTmp
= maRedlineTable
[ nPos
];
3069 } while( nLoopCnt
);
3074 m_rDoc
.getIDocumentState().SetModified();
3077 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3079 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::END
, nullptr);
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
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
);
3111 m_rDoc
.getIDocumentState().SetModified();
3113 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
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
);
3131 // #TODO - add 'SwExtraRedlineTable' also ?
3134 void DocumentRedlineManager::AcceptAllRedline(bool bAccept
)
3136 bool bSuccess
= true;
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
)
3156 bSuccess
= AcceptRedline(maRedlineTable
.size() - 1, true);
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
3172 SwPosition
& rSttPos
= *rPam
.GetPoint();
3173 SwPosition
aSavePos( rSttPos
);
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
);
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() ))
3192 rSttPos
= *pFnd
->End();
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();
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() )
3225 pRStt
= pTmp
->Start();
3226 if( *pPrevEnd
== *pRStt
|| IsPrevPos( *pPrevEnd
, *pRStt
) )
3228 pPrevEnd
= pTmp
->End();
3229 rSttPos
= *pPrevEnd
;
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
);
3248 if( pPos
->GetNode() <= rPam
.GetPoint()->GetNode() )
3249 pPos
->Assign( *pCNd
, 0 );
3257 pPos
= rPam
.GetPoint();
3258 if( !pPos
->GetNode().IsContentNode() )
3260 pCNd
= SwNodes::GoPrevSection( pPos
);
3263 if( pPos
->GetNode() >= rPam
.GetMark()->GetNode() )
3264 pPos
->Assign( *pCNd
, pCNd
->Len() );
3271 if( !pFnd
|| *rPam
.GetMark() == *rPam
.GetPoint() )
3273 if( n
< maRedlineTable
.size() )
3276 *rPam
.GetPoint() = *pSaveFnd
->End();
3281 *rPam
.GetPoint() = aSavePos
;
3286 } while( bRestart
);
3290 // #TODO - add 'SwExtraRedlineTable' also ?
3293 const SwRangeRedline
* DocumentRedlineManager::SelPrevRedline( SwPaM
& rPam
) const
3298 SwPosition
& rSttPos
= *rPam
.GetPoint();
3299 SwPosition
aSavePos( rSttPos
);
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 );
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() ))
3318 rSttPos
= *pFnd
->Start();
3324 while( !pFnd
&& 0 < n
)
3326 pFnd
= maRedlineTable
[ --n
];
3327 if( pFnd
->HasMark() && pFnd
->IsVisible() )
3329 *rPam
.GetMark() = *pFnd
->End();
3330 rSttPos
= *pFnd
->Start();
3338 // Merge all of the same type and author that are
3339 // consecutive into one Selection.
3340 const SwPosition
* pNextStt
= pFnd
->Start();
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
;
3366 const SwRangeRedline
* pSaveFnd
= pFnd
;
3368 SwContentNode
* pCNd
;
3369 SwPosition
* pPos
= rPam
.GetMark();
3370 if( !pPos
->GetNode().IsContentNode() )
3372 pCNd
= SwNodes::GoPrevSection( pPos
);
3375 if( pPos
->GetNode() >= rPam
.GetPoint()->GetNode() )
3376 pPos
->Assign( *pCNd
, pCNd
->Len() );
3384 pPos
= rPam
.GetPoint();
3385 if( !pPos
->GetNode().IsContentNode() )
3387 pCNd
= m_rDoc
.GetNodes().GoNextSection( pPos
);
3390 if( pPos
->GetNode() <= rPam
.GetMark()->GetNode() )
3391 pPos
->Assign( *pCNd
, 0 );
3398 if( !pFnd
|| *rPam
.GetMark() == *rPam
.GetPoint() )
3403 *rPam
.GetPoint() = *pSaveFnd
->Start();
3408 *rPam
.GetPoint() = aSavePos
;
3413 } while( bRestart
);
3417 // #TODO - add 'SwExtraRedlineTable' also ?
3420 // Set comment at the Redline
3421 bool DocumentRedlineManager::SetRedlineComment( const SwPaM
& rPaM
, const OUString
& rS
)
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
)
3431 SwRangeRedline
* pTmp
= maRedlineTable
[ n
];
3432 if( pStt
!= pEnd
&& *pTmp
->Start() > *pEnd
)
3435 pTmp
->SetComment( rS
);
3436 if( *pTmp
->End() >= *pEnd
)
3441 m_rDoc
.getIDocumentState().SetModified();
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
);
3493 moAutoFormatRedlnComment
= *pText
;
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: */