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