1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19 #include <DocumentRedlineManager.hxx>
20 #include <frmfmt.hxx>
21 #include <rootfrm.hxx>
22 #include <txtfrm.hxx>
23 #include <doc.hxx>
24 #include <docsh.hxx>
25 #include <fmtfld.hxx>
26 #include <frmtool.hxx>
27 #include <IDocumentUndoRedo.hxx>
28 #include <IDocumentFieldsAccess.hxx>
29 #include <IDocumentLayoutAccess.hxx>
30 #include <IDocumentState.hxx>
31 #include <redline.hxx>
32 #include <UndoRedline.hxx>
33 #include <docary.hxx>
34 #include <ndtxt.hxx>
35 #include <unocrsr.hxx>
36 #include <ftnidx.hxx>
37 #include <authfld.hxx>
38 #include <strings.hrc>
39 #include <swmodule.hxx>
40 #include <osl/diagnose.h>
41 #include <editeng/prntitem.hxx>
42 
43 using namespace com::sun::star;
44 
45 #ifdef DBG_UTIL
46 
47     #define ERROR_PREFIX "redline table corrupted: "
48 
49     namespace
50     {
51         // helper function for lcl_CheckRedline
52         // 1. make sure that pPos->nContent points into pPos->nNode
53         // 2. check that position is valid and doesn't point after text
lcl_CheckPosition(const SwPosition * pPos)54         void lcl_CheckPosition( const SwPosition* pPos )
55         {
56             assert(dynamic_cast<SwIndexReg*>(&pPos->nNode.GetNode())
57                     == pPos->nContent.GetIdxReg());
58 
59             SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode();
60             if( pTextNode == nullptr )
61             {
62                 assert(pPos->nContent == 0);
63             }
64             else
65             {
66                 assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len());
67             }
68         }
69 
lcl_CheckPam(const SwPaM * pPam)70         void lcl_CheckPam( const SwPaM* pPam )
71         {
72             assert(pPam);
73             lcl_CheckPosition( pPam->GetPoint() );
74             lcl_CheckPosition( pPam->GetMark() );
75         }
76 
77         // check validity of the redline table. Checks redline bounds, and make
78         // sure the redlines are sorted and non-overlapping.
lcl_CheckRedline(IDocumentRedlineAccess & redlineAccess)79         void lcl_CheckRedline( IDocumentRedlineAccess& redlineAccess )
80         {
81             const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
82 
83             // verify valid redline positions
84             for(SwRangeRedline* i : rTable)
85                 lcl_CheckPam( i );
86 
87             for(SwRangeRedline* j : rTable)
88             {
89                 // check for empty redlines
90                 // note: these can destroy sorting in SwTextNode::Update()
91                 // if there's another one without mark on the same pos.
92                 OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
93                             ( j->GetContentIdx() != nullptr ),
94                             ERROR_PREFIX "empty redline" );
95              }
96 
97             // verify proper redline sorting
98             for( size_t n = 1; n < rTable.size(); ++n )
99             {
100                 const SwRangeRedline* pPrev = rTable[ n-1 ];
101                 const SwRangeRedline* pCurrent = rTable[ n ];
102 
103                 // check redline sorting
104                 SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
105                              ERROR_PREFIX "not sorted correctly" );
106 
107                 // check for overlapping redlines
108                 SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
109                              ERROR_PREFIX "overlapping redlines" );
110             }
111 
112             assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
113         }
114     }
115 
116     #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
117 
118 #else
119 
120     #define CHECK_REDLINE( pDoc )
121 
122 #endif
123 
124 namespace sw {
125 
UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)126 static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
127 {
128     auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
129         SwFieldIds::TableOfAuthorities, OUString(), false)));
130     if (pAuthType) // created on demand...
131     {
132         pAuthType->DelSequenceArray();
133     }
134     rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
135     rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
136     rIDFA.UpdateExpFields(nullptr, false);
137     rIDFA.UpdateRefFields();
138 }
139 
UpdateFramesForAddDeleteRedline(SwDoc & rDoc,SwPaM const & rPam)140 void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
141 {
142     if (rDoc.IsClipBoard())
143     {
144         return;
145     }
146     // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
147     // the AppendFootnote/RemoveFootnote will do it by itself!
148     rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
149     SwPosition currentStart(*rPam.Start());
150     SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
151     while (!pStartNode)
152     {
153         // note: branch only taken for redlines, not fieldmarks
154         SwStartNode *const pTableOrSectionNode(
155             currentStart.nNode.GetNode().IsTableNode()
156                 ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
157                 : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
158         if ( !pTableOrSectionNode )
159         {
160             SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
161             return;
162         }
163         for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
164         {
165             pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
166         }
167         for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
168         {
169             if (pLayout->HasMergedParas())
170             {
171                 if (pTableOrSectionNode->IsTableNode())
172                 {
173                     static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
174                 }
175                 else
176                 {
177                     static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
178                 }
179             }
180         }
181         currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
182         currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
183         pStartNode = currentStart.nNode.GetNode().GetTextNode();
184     }
185     if (currentStart < *rPam.End())
186     {
187         SwTextNode * pNode(pStartNode);
188         do
189         {
190             std::vector<SwTextFrame*> frames;
191             SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
192             for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
193             {
194                 if (pFrame->getRootFrame()->HasMergedParas())
195                 {
196                     frames.push_back(pFrame);
197                 }
198                 // set anchored objects as deleted
199                 pFrame->SetDrawObjsAsDeleted(true);
200             }
201             if (frames.empty())
202             {
203                 auto const& layouts(rDoc.GetAllLayouts());
204                 assert(std::none_of(layouts.begin(), layouts.end(),
205                     [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
206                 (void) layouts;
207                 break;
208             }
209             auto eMode(sw::FrameMode::Existing);
210             SwTextNode * pLast(pNode);
211             for (SwTextFrame * pFrame : frames)
212             {
213                 SwTextNode & rFirstNode(pFrame->GetMergedPara()
214                     ? *pFrame->GetMergedPara()->pFirstNode
215                     : *pNode);
216                 assert(pNode == pStartNode
217                         ? rFirstNode.GetIndex() <= pNode->GetIndex()
218                         : &rFirstNode == pNode);
219                 // clear old one first to avoid DelFrames confusing updates & asserts...
220                 pFrame->SetMergedPara(nullptr);
221                 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
222                     *pFrame, rFirstNode, eMode));
223                 eMode = sw::FrameMode::New; // Existing is not idempotent!
224                 // the first node of the new redline is not necessarily the first
225                 // node of the merged frame, there could be another redline nearby
226                 sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
227                 // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
228                 if (pFrame->GetMergedPara())
229                 {
230                     pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
231                 }
232             }
233             SwNodeIndex tmp(*pLast);
234             // skip over hidden sections!
235             pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
236         }
237         while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
238     }
239     // fields last - SwGetRefField::UpdateField requires up-to-date frames
240     UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
241 
242     // update SwPostItMgr / notes in the margin
243     rDoc.GetDocShell()->Broadcast(
244             SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
245 }
246 
UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc,SwPaM const & rPam)247 void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
248 {
249     if (rDoc.IsClipBoard())
250     {
251         return;
252     }
253     bool isAppendObjsCalled(false);
254     rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
255     SwPosition currentStart(*rPam.Start());
256     SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
257     while (!pStartNode)
258     {
259         // note: branch only taken for redlines, not fieldmarks
260         SwStartNode const*const pTableOrSectionNode(
261             currentStart.nNode.GetNode().IsTableNode()
262                 ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
263                 : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
264         assert(pTableOrSectionNode); // known pathology
265         for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
266         {
267             pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
268         }
269         if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
270         {
271             // note: this will also create frames for all currently hidden flys
272             // because it calls AppendAllObjs
273             SwNodeIndex const end(*pTableOrSectionNode->EndOfSectionNode());
274             ::MakeFrames(&rDoc, currentStart.nNode, end);
275             isAppendObjsCalled = true;
276         }
277         currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
278         currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
279         pStartNode = currentStart.nNode.GetNode().GetTextNode();
280     }
281     if (currentStart < *rPam.End())
282     {
283         SwTextNode * pNode(pStartNode);
284         do
285         {
286             std::vector<SwTextFrame*> frames;
287             SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
288             for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
289             {
290                 if (pFrame->getRootFrame()->HasMergedParas())
291                 {
292                     frames.push_back(pFrame);
293                 }
294                 // set anchored objects as not deleted
295                 pFrame->SetDrawObjsAsDeleted(false);
296             }
297             if (frames.empty())
298             {
299                 // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
300                 if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
301                 {
302                     auto const& layouts(rDoc.GetAllLayouts());
303                     assert(std::none_of(layouts.begin(), layouts.end(),
304                         [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
305                     (void) layouts;
306                 }
307                 isAppendObjsCalled = true; // skip that!
308                 break;
309             }
310 
311             // first, call CheckParaRedlineMerge on the first paragraph,
312             // to init flag on new merge range (if any) + 1st node post the merge
313             auto eMode(sw::FrameMode::Existing);
314             SwTextNode * pLast(pNode);
315             for (SwTextFrame * pFrame : frames)
316             {
317                 if (auto const pMergedPara = pFrame->GetMergedPara())
318                 {
319                     pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
320                     assert(pNode == pStartNode
321                         ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
322                         : pMergedPara->pFirstNode == pNode);
323                     // clear old one first to avoid DelFrames confusing updates & asserts...
324                     SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
325                     pFrame->SetMergedPara(nullptr);
326                     pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
327                         *pFrame, rFirstNode, eMode));
328                     eMode = sw::FrameMode::New; // Existing is not idempotent!
329                     // update pNode so MakeFrames starts on 2nd node
330                     pNode = &rFirstNode;
331                 }
332             }
333             if (pLast != pNode)
334             {
335                 // now start node until end of merge + 1 has proper flags; MakeFrames
336                 // should pick up from the next node in need of frames by checking flags
337                 SwNodeIndex const start(*pNode, +1);
338                 SwNodeIndex const end(*pLast, +1); // end is exclusive
339                 // note: this will also create frames for all currently hidden flys
340                 // both on first and non-first nodes because it calls AppendAllObjs
341                 ::MakeFrames(&rDoc, start, end);
342                 isAppendObjsCalled = true;
343                 // re-use this to move flys that are now on the wrong frame, with end
344                 // of redline as "second" node; the nodes between start and end should
345                 // be complete with MakeFrames already
346                 sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
347             }
348             SwNodeIndex tmp(*pLast);
349             // skip over hidden sections!
350             pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
351         }
352         while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
353     }
354 
355     if (!isAppendObjsCalled)
356     {   // recreate flys in the one node the hard way...
357         for (auto const& pLayout : rDoc.GetAllLayouts())
358         {
359             if (pLayout->HasMergedParas())
360             {
361                 AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
362                 break;
363             }
364         }
365     }
366     // fields last - SwGetRefField::UpdateField requires up-to-date frames
367     UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
368 
369     // update SwPostItMgr / notes in the margin
370     rDoc.GetDocShell()->Broadcast(
371             SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
372 }
373 
374 } // namespace sw
375 
376 namespace
377 {
IsPrevPos(const SwPosition & rPos1,const SwPosition & rPos2)378     bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
379     {
380         const SwContentNode* pCNd;
381         if( 0 != rPos2.nContent.GetIndex() )
382             return false;
383         if( rPos2.nNode.GetIndex() - 1 != rPos1.nNode.GetIndex() )
384             return false;
385         pCNd = rPos1.nNode.GetNode().GetContentNode();
386         return pCNd && rPos1.nContent.GetIndex() == pCNd->Len();
387     }
388 
389     // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
lcl_CopyStyle(const SwPosition & rFrom,const SwPosition & rTo,bool bCopy=true)390     SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
391     {
392         SwTextNode* pToNode = rTo.nNode.GetNode().GetTextNode();
393         SwTextNode* pFromNode = rFrom.nNode.GetNode().GetTextNode();
394         if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
395         {
396             const SwPaM aPam(*pToNode);
397             SwDoc& rDoc = aPam.GetDoc();
398             // using Undo, copy paragraph style
399             SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
400             SwTextFormatColl* pToColl = pToNode->GetTextColl();
401             if (bCopy && pFromColl != pToColl)
402                 rDoc.SetTextFormatColl(aPam, pFromColl);
403 
404             // using Undo, remove direct paragraph formatting of the "To" paragraph,
405             // and apply here direct paragraph formatting of the "From" paragraph
406             SfxItemSet aTmp(
407                 rDoc.GetAttrPool(),
408                 svl::Items<
409                     RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
410                     RES_PARATR_LIST_BEGIN, RES_UL_SPACE,  // skip PAGEDESC and BREAK
411                     RES_CNTNT, RES_FRMATR_END - 1>{});
412             SfxItemSet aTmp2(aTmp);
413 
414             pToNode->GetParaAttr(aTmp, 0, 0);
415             pFromNode->GetParaAttr(aTmp2, 0, 0);
416 
417             bool bSameSet = aTmp == aTmp2;
418 
419             if (!bSameSet)
420             {
421                 for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
422                 {
423                     sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
424                     if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
425                         SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
426                             aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
427                 }
428             }
429 
430             if (bCopy && !bSameSet)
431                 rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
432             else if (!bCopy && (!bSameSet || pFromColl != pToColl))
433                 return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
434         }
435         return nullptr;
436     }
437 
438     // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
lcl_DeleteTrackedTableRow(SwPosition * pPos)439     void lcl_DeleteTrackedTableRow ( SwPosition* pPos )
440     {
441         if ( const SwTableBox* pBox = pPos->nNode.GetNode().GetTableBox() )
442         {
443             const SwTableLine* pLine = pBox->GetUpper();
444             const SvxPrintItem *pHasTextChangesOnlyProp =
445                     pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
446             // empty table row with property "HasTextChangesOnly" = false
447             if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() &&
448                  pLine->IsEmpty() )
449             {
450                 SwCursor aCursor( *pPos, nullptr );
451                 pPos->GetDoc().DeleteRow( aCursor );
452             }
453         }
454     }
455 
456     // at rejection of a deletion in a table, remove the tracking of the table row
lcl_RemoveTrackingOfTableRow(const SwPosition * pPos)457     void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos )
458     {
459         if ( const SwTableBox* pBox = pPos->nNode.GetNode().GetTableBox() )
460         {
461             const SwTableLine* pLine = pBox->GetUpper();
462             const SvxPrintItem *pHasTextChangesOnlyProp =
463                     pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
464             // table row property "HasTextChangesOnly" is set and its value is false
465             if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
466             {
467                 SvxPrintItem aUnsetTracking(RES_PRINT, true);
468                 SwCursor aCursor( *pPos, nullptr );
469                 pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
470             }
471         }
472     }
473 
lcl_AcceptRedline(SwRedlineTable & rArr,SwRedlineTable::size_type & rPos,bool bCallDelete,const SwPosition * pSttRng=nullptr,const SwPosition * pEndRng=nullptr)474     bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
475                             bool bCallDelete,
476                             const SwPosition* pSttRng = nullptr,
477                             const SwPosition* pEndRng = nullptr )
478     {
479         bool bRet = true;
480         SwRangeRedline* pRedl = rArr[ rPos ];
481         SwPosition *pRStt = nullptr, *pREnd = nullptr;
482         SwComparePosition eCmp = SwComparePosition::Outside;
483         if( pSttRng && pEndRng )
484         {
485             pRStt = pRedl->Start();
486             pREnd = pRedl->End();
487             eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
488         }
489 
490         pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
491 
492         switch( pRedl->GetType() )
493         {
494         case RedlineType::Insert:
495         case RedlineType::Format:
496             {
497                 bool bCheck = false, bReplace = false;
498                 switch( eCmp )
499                 {
500                 case SwComparePosition::Inside:
501                     if( *pSttRng == *pRStt )
502                         pRedl->SetStart( *pEndRng, pRStt );
503                     else
504                     {
505                         if( *pEndRng != *pREnd )
506                         {
507                             // split up
508                             SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
509                             pNew->SetStart( *pEndRng );
510                             rArr.Insert( pNew ); ++rPos;
511                         }
512                         pRedl->SetEnd( *pSttRng, pREnd );
513                         bCheck = true;
514                     }
515                     break;
516 
517                 case SwComparePosition::OverlapBefore:
518                     pRedl->SetStart( *pEndRng, pRStt );
519                     bReplace = true;
520                     break;
521 
522                 case SwComparePosition::OverlapBehind:
523                     pRedl->SetEnd( *pSttRng, pREnd );
524                     bCheck = true;
525                     break;
526 
527                 case SwComparePosition::Outside:
528                 case SwComparePosition::Equal:
529                     rArr.DeleteAndDestroy( rPos-- );
530                     break;
531 
532                 default:
533                     bRet = false;
534                 }
535 
536                 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
537                 {
538                     // re-insert
539                     rArr.Remove( pRedl );
540                     rArr.Insert( pRedl );
541                 }
542             }
543             break;
544         case RedlineType::Delete:
545             {
546                 SwDoc& rDoc = pRedl->GetDoc();
547                 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
548                 bool bDelRedl = false;
549                 switch( eCmp )
550                 {
551                 case SwComparePosition::Inside:
552                     if( bCallDelete )
553                     {
554                         pDelStt = pSttRng;
555                         pDelEnd = pEndRng;
556                     }
557                     break;
558 
559                 case SwComparePosition::OverlapBefore:
560                     if( bCallDelete )
561                     {
562                         pDelStt = pRStt;
563                         pDelEnd = pEndRng;
564                     }
565                     break;
566                 case SwComparePosition::OverlapBehind:
567                     if( bCallDelete )
568                     {
569                         pDelStt = pREnd;
570                         pDelEnd = pSttRng;
571                     }
572                     break;
573 
574                 case SwComparePosition::Outside:
575                 case SwComparePosition::Equal:
576                     {
577                         rArr.Remove( rPos-- );
578                         bDelRedl = true;
579                         if( bCallDelete )
580                         {
581                             pDelStt = pRedl->Start();
582                             pDelEnd = pRedl->End();
583                         }
584                     }
585                     break;
586                 default:
587                     bRet = false;
588                 }
589 
590                 if( pDelStt && pDelEnd )
591                 {
592                     SwPaM aPam( *pDelStt, *pDelEnd );
593                     SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
594                     SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
595                     pRStt = pRedl->Start();
596                     pREnd = pRedl->End();
597 
598                     // keep style of the empty paragraph after deletion of wholly paragraphs
599                     if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->nContent == 0 )
600                         lcl_CopyStyle(*pREnd, *pRStt);
601 
602                     if( bDelRedl )
603                         delete pRedl;
604 
605                     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
606                     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
607 
608                     if( pCSttNd && pCEndNd )
609                     {
610                         rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
611                         lcl_DeleteTrackedTableRow( aPam.End() );
612                     }
613                     else if (pCSttNd && !pCEndNd)
614                         {
615                             aPam.GetBound().nContent.Assign( nullptr, 0 );
616                             aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
617                             rDoc.getIDocumentContentOperations().DelFullPara( aPam );
618                         }
619                     else
620                     {
621                         rDoc.getIDocumentContentOperations().DeleteRange(aPam);
622                     }
623                     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
624                 }
625                 else if( bDelRedl )
626                     delete pRedl;
627             }
628             break;
629 
630         case RedlineType::FmtColl:
631             rArr.DeleteAndDestroy( rPos-- );
632             break;
633 
634         case RedlineType::ParagraphFormat:
635             rArr.DeleteAndDestroy( rPos-- );
636             break;
637 
638         default:
639             bRet = false;
640         }
641         return bRet;
642     }
643 
lcl_RejectRedline(SwRedlineTable & rArr,SwRedlineTable::size_type & rPos,bool bCallDelete,const SwPosition * pSttRng=nullptr,const SwPosition * pEndRng=nullptr)644     bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
645                             bool bCallDelete,
646                             const SwPosition* pSttRng = nullptr,
647                             const SwPosition* pEndRng = nullptr )
648     {
649         bool bRet = true;
650         SwRangeRedline* pRedl = rArr[ rPos ];
651         SwDoc& rDoc = pRedl->GetDoc();
652         SwPosition *pRStt = nullptr, *pREnd = nullptr;
653         SwComparePosition eCmp = SwComparePosition::Outside;
654         if( pSttRng && pEndRng )
655         {
656             pRStt = pRedl->Start();
657             pREnd = pRedl->End();
658             eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
659         }
660 
661         pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
662 
663         switch( pRedl->GetType() )
664         {
665         case RedlineType::Insert:
666             {
667                 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
668                 bool bDelRedl = false;
669                 switch( eCmp )
670                 {
671                 case SwComparePosition::Inside:
672                     if( bCallDelete )
673                     {
674                         pDelStt = pSttRng;
675                         pDelEnd = pEndRng;
676                     }
677                     break;
678 
679                 case SwComparePosition::OverlapBefore:
680                     if( bCallDelete )
681                     {
682                         pDelStt = pRStt;
683                         pDelEnd = pEndRng;
684                     }
685                     break;
686                 case SwComparePosition::OverlapBehind:
687                     if( bCallDelete )
688                     {
689                         pDelStt = pREnd;
690                         pDelEnd = pSttRng;
691                     }
692                     break;
693                 case SwComparePosition::Outside:
694                 case SwComparePosition::Equal:
695                     {
696                         // delete the range again
697                         rArr.Remove( rPos-- );
698                         bDelRedl = true;
699                         if( bCallDelete )
700                         {
701                             pDelStt = pRedl->Start();
702                             pDelEnd = pRedl->End();
703                         }
704                     }
705                     break;
706 
707                 default:
708                     bRet = false;
709                 }
710                 if( pDelStt && pDelEnd )
711                 {
712                     SwPaM aPam( *pDelStt, *pDelEnd );
713 
714                     SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
715                     SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
716 
717                     if( bDelRedl )
718                         delete pRedl;
719 
720                     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
721                     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
722 
723                     if( pCSttNd && pCEndNd )
724                     {
725                         rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
726                         lcl_DeleteTrackedTableRow( aPam.End() );
727                     }
728                     else if (pCSttNd && !pCEndNd)
729                         {
730                             aPam.GetBound().nContent.Assign( nullptr, 0 );
731                             aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
732                             if (aPam.End()->nNode.GetNode().IsStartNode())
733                             {   // end node will be deleted too! see nNodeDiff+1
734                                 --aPam.End()->nNode;
735                             }
736                             assert(!aPam.End()->nNode.GetNode().IsStartNode());
737                             rDoc.getIDocumentContentOperations().DelFullPara( aPam );
738                         }
739                     else
740                     {
741                         rDoc.getIDocumentContentOperations().DeleteRange(aPam);
742                     }
743                     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
744                 }
745                 else if( bDelRedl )
746                     delete pRedl;
747             }
748             break;
749         case RedlineType::Delete:
750             {
751                 SwRangeRedline* pNew = nullptr;
752                 bool bCheck = false, bReplace = false;
753                 SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
754                                       pEndRng ? *pEndRng : *pRedl->End());
755 
756                 if( pRedl->GetExtraData() )
757                     pRedl->GetExtraData()->Reject( *pRedl );
758 
759                 // remove tracking of the table row, if needed
760                 lcl_RemoveTrackingOfTableRow( updatePaM.End() );
761 
762                 switch( eCmp )
763                 {
764                 case SwComparePosition::Inside:
765                     {
766                         if( 1 < pRedl->GetStackCount() )
767                         {
768                             pNew = new SwRangeRedline( *pRedl );
769                             pNew->PopData();
770                         }
771                         if( *pSttRng == *pRStt )
772                         {
773                             pRedl->SetStart( *pEndRng, pRStt );
774                             bReplace = true;
775                             if( pNew )
776                                 pNew->SetEnd( *pEndRng );
777                         }
778                         else
779                         {
780                             if( *pEndRng != *pREnd )
781                             {
782                                 // split up
783                                 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
784                                 pCpy->SetStart( *pEndRng );
785                                 rArr.Insert( pCpy ); ++rPos;
786                                 if( pNew )
787                                     pNew->SetEnd( *pEndRng );
788                             }
789 
790                             pRedl->SetEnd( *pSttRng, pREnd );
791                             bCheck = true;
792                             if( pNew )
793                                 pNew->SetStart( *pSttRng );
794                         }
795                     }
796                     break;
797 
798                 case SwComparePosition::OverlapBefore:
799                     if( 1 < pRedl->GetStackCount() )
800                     {
801                         pNew = new SwRangeRedline( *pRedl );
802                         pNew->PopData();
803                     }
804                     pRedl->SetStart( *pEndRng, pRStt );
805                     bReplace = true;
806                     if( pNew )
807                         pNew->SetEnd( *pEndRng );
808                     break;
809 
810                 case SwComparePosition::OverlapBehind:
811                     if( 1 < pRedl->GetStackCount() )
812                     {
813                         pNew = new SwRangeRedline( *pRedl );
814                         pNew->PopData();
815                     }
816                     pRedl->SetEnd( *pSttRng, pREnd );
817                     bCheck = true;
818                     if( pNew )
819                         pNew->SetStart( *pSttRng );
820                     break;
821 
822                 case SwComparePosition::Outside:
823                 case SwComparePosition::Equal:
824                     if( !pRedl->PopData() )
825                         // deleting the RedlineObject is enough
826                         rArr.DeleteAndDestroy( rPos-- );
827                     break;
828 
829                 default:
830                     bRet = false;
831                 }
832 
833                 if( pNew )
834                 {
835                     rArr.Insert( pNew ); ++rPos;
836                 }
837 
838                 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
839                 {
840                     // re-insert
841                     rArr.Remove( pRedl );
842                     rArr.Insert( pRedl );
843                 }
844 
845                 sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
846             }
847             break;
848 
849         case RedlineType::Format:
850         case RedlineType::FmtColl:
851         case RedlineType::ParagraphFormat:
852             {
853                 // tdf#52391 instead of hidden acception at the requested
854                 // rejection, remove direct text formatting to get the potential
855                 // original state of the text (FIXME if the original text
856                 // has already contained direct text formatting: unfortunately
857                 // ODF 1.2 doesn't support rejection of format-only changes)
858                 if ( pRedl->GetType() == RedlineType::Format )
859                 {
860                     SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
861                     rDoc.ResetAttrs(aPam);
862                 }
863                 else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
864                 {
865                     // handle paragraph formatting changes
866                     // (range is only a full paragraph or a part of it)
867                     const SwPosition* pStt = pRedl->Start();
868                     SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
869                     if( pTNd )
870                     {
871                         // expand range to the whole paragraph
872                         // and reset only the paragraph attributes
873                         SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
874                         o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
875 
876                         constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
877                             { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
878                             { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
879                         };
880 
881                         for (const auto& [nBegin, nEnd] : aResetableSetRange)
882                         {
883                             for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
884                                 aResetAttrsArray.insert( i );
885                         }
886 
887                         rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
888 
889                         // remove numbering
890                         if ( pTNd->GetNumRule() )
891                             rDoc.DelNumRules(aPam);
892                     }
893                 }
894 
895                 if( pRedl->GetExtraData() )
896                     pRedl->GetExtraData()->Reject( *pRedl );
897 
898                 rArr.DeleteAndDestroy( rPos-- );
899             }
900             break;
901 
902         default:
903             bRet = false;
904         }
905         return bRet;
906     }
907 
908     typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
909                             bool bCallDelete,
910                             const SwPosition* pSttRng,
911                             const SwPosition* pEndRng);
912 
913 
lcl_AcceptRejectRedl(Fn_AcceptReject fn_AcceptReject,SwRedlineTable & rArr,bool bCallDelete,const SwPaM & rPam)914     int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
915                                 SwRedlineTable& rArr, bool bCallDelete,
916                                 const SwPaM& rPam)
917     {
918         SwRedlineTable::size_type n = 0;
919         int nCount = 0;
920 
921         const SwPosition* pStt = rPam.Start(),
922                         * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
923                                                          : rPam.GetPoint();
924         const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
925         if( pFnd &&     // Is new a part of it?
926             ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
927         {
928             // Only revoke the partial selection
929             if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
930                 nCount++;
931             ++n;
932         }
933 
934         // tdf#119824 first we will accept only overlapping paragraph format changes
935         // in the first loop to avoid potential content changes during Redo
936         bool bHasParagraphFormatChange = false;
937         for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
938         {
939             for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
940             {
941                 SwRangeRedline* pTmp = rArr[ o ];
942                 if( pTmp->HasMark() && pTmp->IsVisible() )
943                 {
944                     if( *pTmp->End() <= *pEnd )
945                     {
946                         if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
947                             (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
948                         {
949                             bHasParagraphFormatChange = true;
950                             nCount++;
951                         }
952                     }
953                     else
954                     {
955                         if( *pTmp->Start() < *pEnd )
956                         {
957                             // Only revoke the partial selection
958                             if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
959                                 (*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
960                             {
961                                 bHasParagraphFormatChange = true;
962                                 nCount++;
963                             }
964                         }
965                         break;
966                     }
967                 }
968             }
969         }
970         return nCount;
971     }
972 
lcl_AdjustRedlineRange(SwPaM & rPam)973     void lcl_AdjustRedlineRange( SwPaM& rPam )
974     {
975         // The Selection is only in the ContentSection. If there are Redlines
976         // to Non-ContentNodes before or after that, then the Selections
977         // expand to them.
978         SwPosition* pStt = rPam.Start(),
979                   * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
980                                                    : rPam.GetPoint();
981         SwDoc& rDoc = rPam.GetDoc();
982         if( !pStt->nContent.GetIndex() &&
983             !rDoc.GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() )
984         {
985             const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
986             if( pRedl )
987             {
988                 const SwPosition* pRStt = pRedl->Start();
989                 if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() ==
990                     pStt->nNode.GetIndex() - 1 )
991                     *pStt = *pRStt;
992             }
993         }
994         if( pEnd->nNode.GetNode().IsContentNode() &&
995             !rDoc.GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() &&
996             pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len()    )
997         {
998             const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
999             if( pRedl )
1000             {
1001                 const SwPosition* pREnd = pRedl->End();
1002                 if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() ==
1003                     pEnd->nNode.GetIndex() + 1 )
1004                     *pEnd = *pREnd;
1005             }
1006         }
1007     }
1008 
1009     /// in case some text is deleted, ensure that the not-yet-inserted
1010     /// SwRangeRedline has its positions corrected not to point to deleted node
1011     class TemporaryRedlineUpdater
1012     {
1013     private:
1014         SwRangeRedline & m_rRedline;
1015         std::shared_ptr<SwUnoCursor> m_pCursor;
1016     public:
TemporaryRedlineUpdater(SwDoc & rDoc,SwRangeRedline & rRedline)1017         TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
1018             : m_rRedline(rRedline)
1019             , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
1020         {
1021             if (m_rRedline.HasMark())
1022             {
1023                 m_pCursor->SetMark();
1024                 *m_pCursor->GetMark() = *m_rRedline.GetMark();
1025                 *m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent());
1026             }
1027             *m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent());
1028         }
~TemporaryRedlineUpdater()1029         ~TemporaryRedlineUpdater()
1030         {
1031             static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
1032         }
1033     };
1034 }
1035 
1036 namespace sw
1037 {
1038 
DocumentRedlineManager(SwDoc & i_rSwdoc)1039 DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
1040     : m_rDoc(i_rSwdoc)
1041     , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
1042     , mpRedlineTable(new SwRedlineTable)
1043     , mpExtraRedlineTable(new SwExtraRedlineTable)
1044     , mbIsRedlineMove(false)
1045     , mnAutoFormatRedlnCommentNo(0)
1046 {
1047 }
1048 
GetRedlineFlags() const1049 RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
1050 {
1051     return meRedlineFlags;
1052 }
1053 
SetRedlineFlags(RedlineFlags eMode)1054 void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
1055 {
1056     if( meRedlineFlags == eMode )
1057         return;
1058 
1059     if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
1060         || !(RedlineFlags::ShowMask & eMode) )
1061     {
1062         bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
1063         m_rDoc.SetInXMLImport( false );
1064         // and then hide/display everything
1065         void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
1066                                                           // uninitialized ptr is possible
1067 
1068         RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
1069         if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1070             pFnc = &SwRangeRedline::Show;
1071         else if (eShowMode == RedlineFlags::ShowInsert)
1072             pFnc = &SwRangeRedline::Hide;
1073         else if (eShowMode == RedlineFlags::ShowDelete)
1074             pFnc = &SwRangeRedline::ShowOriginal;
1075         else
1076         {
1077             pFnc = &SwRangeRedline::Hide;
1078             eMode |= RedlineFlags::ShowInsert;
1079         }
1080 
1081         CheckAnchoredFlyConsistency(m_rDoc);
1082         CHECK_REDLINE( *this )
1083 
1084         o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
1085         if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1086         {
1087             // sw_redlinehide: the problem here is that MoveFromSection
1088             // creates the frames wrongly (non-merged), because its own
1089             // SwRangeRedline has wrong positions until after the nodes
1090             // are all moved, so fix things up by force by re-creating
1091             // all merged frames from scratch.
1092             o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
1093             for (SwRootFrame *const pLayout : layouts)
1094             {
1095                 if (pLayout->IsHideRedlines())
1096                 {
1097                     pLayout->SetHideRedlines(false);
1098                     hiddenLayouts.insert(pLayout);
1099                 }
1100             }
1101         }
1102 
1103         for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
1104             for (size_t i = 0; i < mpRedlineTable->size(); ++i)
1105             {
1106                 SwRangeRedline *const pRedline((*mpRedlineTable)[i]);
1107                 (pRedline->*pFnc)(nLoop, i, false);
1108                 while (mpRedlineTable->size() <= i
1109                     || (*mpRedlineTable)[i] != pRedline)
1110                 {        // ensure current position
1111                     --i; // a previous redline may have been deleted
1112                 }
1113             }
1114 
1115         //SwRangeRedline::MoveFromSection routinely changes
1116         //the keys that mpRedlineTable is sorted by
1117         mpRedlineTable->Resort();
1118 
1119         CheckAnchoredFlyConsistency(m_rDoc);
1120         CHECK_REDLINE( *this )
1121 
1122         for (SwRootFrame *const pLayout : hiddenLayouts)
1123         {
1124             pLayout->SetHideRedlines(true);
1125         }
1126 
1127         m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
1128     }
1129     meRedlineFlags = eMode;
1130     m_rDoc.getIDocumentState().SetModified();
1131 
1132     // #TODO - add 'SwExtraRedlineTable' also ?
1133 }
1134 
IsRedlineOn() const1135 bool DocumentRedlineManager::IsRedlineOn() const
1136 {
1137     return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
1138 }
1139 
IsIgnoreRedline() const1140 bool DocumentRedlineManager::IsIgnoreRedline() const
1141 {
1142     return bool(RedlineFlags::Ignore & meRedlineFlags);
1143 }
1144 
SetRedlineFlags_intern(RedlineFlags eMode)1145 void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
1146 {
1147     meRedlineFlags = eMode;
1148 }
1149 
GetRedlineTable() const1150 const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
1151 {
1152     return *mpRedlineTable;
1153 }
1154 
GetRedlineTable()1155 SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
1156 {
1157     return *mpRedlineTable;
1158 }
1159 
GetExtraRedlineTable() const1160 const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
1161 {
1162     return *mpExtraRedlineTable;
1163 }
1164 
GetExtraRedlineTable()1165 SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
1166 {
1167     return *mpExtraRedlineTable;
1168 }
1169 
HasExtraRedlineTable() const1170 bool DocumentRedlineManager::HasExtraRedlineTable() const
1171 {
1172     return mpExtraRedlineTable != nullptr;
1173 }
1174 
IsInRedlines(const SwNode & rNode) const1175 bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
1176 {
1177     SwPosition aPos(rNode);
1178     SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
1179     SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
1180                SwPosition(rEndOfRedlines));
1181 
1182     return aPam.ContainsPosition(aPos);
1183 }
1184 
IsRedlineMove() const1185 bool DocumentRedlineManager::IsRedlineMove() const
1186 {
1187     return mbIsRedlineMove;
1188 }
1189 
SetRedlineMove(bool bFlag)1190 void DocumentRedlineManager::SetRedlineMove(bool bFlag)
1191 {
1192     mbIsRedlineMove = bFlag;
1193 }
1194 
1195 /*
1196 Text means Text not "polluted" by Redlines.
1197 
1198 Behaviour of Insert-Redline:
1199     - in the Text                       - insert Redline Object
1200     - in InsertRedline (own)            - ignore, existing is extended
1201     - in InsertRedline (others)         - split up InsertRedline and
1202                                           insert Redline Object
1203     - in DeleteRedline                  - split up DeleteRedline or
1204                                           move at the end/beginning
1205 
1206 Behaviour of Delete-Redline:
1207     - in the Text                       - insert Redline Object
1208     - in DeleteRedline (own/others)     - ignore
1209     - in InsertRedline (own)            - ignore, but delete character
1210     - in InsertRedline (others)         - split up InsertRedline and
1211                                           insert Redline Object
1212     - Text and own Insert overlap       - delete Text in the own Insert,
1213                                           extend in the other Text
1214                                           (up to the Insert!)
1215     - Text and other Insert overlap     - insert Redline Object, the
1216                                           other Insert is overlapped by
1217                                           the Delete
1218 */
1219 IDocumentRedlineAccess::AppendResult
AppendRedline(SwRangeRedline * pNewRedl,bool const bCallDelete)1220 DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
1221 {
1222     bool bMerged = false;
1223     CHECK_REDLINE( *this )
1224 
1225     if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
1226     {
1227         pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
1228 
1229         if( m_rDoc.IsAutoFormatRedline() )
1230         {
1231             pNewRedl->SetAutoFormat();
1232             if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() )
1233             {
1234                 pNewRedl->SetComment( *mpAutoFormatRedlnComment );
1235                 pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
1236             }
1237         }
1238 
1239         SwPosition* pStt = pNewRedl->Start(),
1240                   * pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark()
1241                                                         : pNewRedl->GetPoint();
1242         {
1243             SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode();
1244             if( pTextNode == nullptr )
1245             {
1246                 if( pStt->nContent > 0 )
1247                 {
1248                     OSL_ENSURE( false, "Redline start: non-text-node with content" );
1249                     pStt->nContent = 0;
1250                 }
1251             }
1252             else
1253             {
1254                 if( pStt->nContent > pTextNode->Len() )
1255                 {
1256                     OSL_ENSURE( false, "Redline start: index after text" );
1257                     pStt->nContent = pTextNode->Len();
1258                 }
1259             }
1260             pTextNode = pEnd->nNode.GetNode().GetTextNode();
1261             if( pTextNode == nullptr )
1262             {
1263                 if( pEnd->nContent > 0 )
1264                 {
1265                     OSL_ENSURE( false, "Redline end: non-text-node with content" );
1266                     pEnd->nContent = 0;
1267                 }
1268             }
1269             else
1270             {
1271                 if( pEnd->nContent > pTextNode->Len() )
1272                 {
1273                     OSL_ENSURE( false, "Redline end: index after text" );
1274                     pEnd->nContent = pTextNode->Len();
1275                 }
1276             }
1277         }
1278         if( ( *pStt == *pEnd ) &&
1279             ( pNewRedl->GetContentIdx() == nullptr ) )
1280         {   // Do not insert empty redlines
1281             delete pNewRedl;
1282             return AppendResult::IGNORED;
1283         }
1284         bool bCompress = false;
1285         SwRedlineTable::size_type n = 0;
1286         // look up the first Redline for the starting position
1287         if( !GetRedline( *pStt, &n ) && n )
1288             --n;
1289         bool bDec = false;
1290 
1291         for( ; pNewRedl && n < mpRedlineTable->size(); bDec ? n : ++n )
1292         {
1293             bDec = false;
1294 
1295             SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
1296             SwPosition* pRStt = pRedl->Start(),
1297                       * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
1298                                                            : pRedl->GetPoint();
1299 
1300             // #i8518# remove empty redlines while we're at it
1301             if( ( *pRStt == *pREnd ) &&
1302                 ( pRedl->GetContentIdx() == nullptr ) )
1303             {
1304                 mpRedlineTable->DeleteAndDestroy(n);
1305                 continue;
1306             }
1307 
1308             SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1309 
1310             switch( pNewRedl->GetType() )
1311             {
1312             case RedlineType::Insert:
1313                 switch( pRedl->GetType() )
1314                 {
1315                 case RedlineType::Insert:
1316                     if( pRedl->IsOwnRedline( *pNewRedl ) )
1317                     {
1318                         bool bDelete = false;
1319 
1320                         // Merge if applicable?
1321                         if( (( SwComparePosition::Behind == eCmpPos &&
1322                                IsPrevPos( *pREnd, *pStt ) ) ||
1323                              ( SwComparePosition::CollideStart == eCmpPos ) ||
1324                              ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
1325                             pRedl->CanCombine( *pNewRedl ) &&
1326                             ( n+1 >= mpRedlineTable->size() ||
1327                              ( *(*mpRedlineTable)[ n+1 ]->Start() >= *pEnd &&
1328                              *(*mpRedlineTable)[ n+1 ]->Start() != *pREnd ) ) )
1329                         {
1330                             pRedl->SetEnd( *pEnd, pREnd );
1331                             if( !pRedl->HasValidRange() )
1332                             {
1333                                 // re-insert
1334                                 mpRedlineTable->Remove( n );
1335                                 mpRedlineTable->Insert( pRedl );
1336                             }
1337 
1338                             bMerged = true;
1339                             bDelete = true;
1340                         }
1341                         else if( (( SwComparePosition::Before == eCmpPos &&
1342                                     IsPrevPos( *pEnd, *pRStt ) ) ||
1343                                    ( SwComparePosition::CollideEnd == eCmpPos ) ||
1344                                   ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
1345                             pRedl->CanCombine( *pNewRedl ) &&
1346                             ( !n ||
1347                              *(*mpRedlineTable)[ n-1 ]->End() != *pRStt ))
1348                         {
1349                             pRedl->SetStart( *pStt, pRStt );
1350                             // re-insert
1351                             mpRedlineTable->Remove( n );
1352                             mpRedlineTable->Insert( pRedl );
1353 
1354                             bMerged = true;
1355                             bDelete = true;
1356                         }
1357                         else if ( SwComparePosition::Outside == eCmpPos )
1358                         {
1359                             // own insert-over-insert redlines:
1360                             // just scrap the inside ones
1361                             mpRedlineTable->DeleteAndDestroy( n );
1362                             bDec = true;
1363                         }
1364                         else if( SwComparePosition::OverlapBehind == eCmpPos )
1365                         {
1366                             *pStt = *pREnd;
1367                             if( ( *pStt == *pEnd ) &&
1368                                 ( pNewRedl->GetContentIdx() == nullptr ) )
1369                                 bDelete = true;
1370                         }
1371                         else if( SwComparePosition::OverlapBefore == eCmpPos )
1372                         {
1373                             *pEnd = *pRStt;
1374                             if( ( *pStt == *pEnd ) &&
1375                                 ( pNewRedl->GetContentIdx() == nullptr ) )
1376                                 bDelete = true;
1377                         }
1378                         else if( SwComparePosition::Inside == eCmpPos )
1379                         {
1380                             bDelete = true;
1381                             bMerged = true;
1382                         }
1383                         else if( SwComparePosition::Equal == eCmpPos )
1384                             bDelete = true;
1385 
1386                         if( bDelete )
1387                         {
1388                             delete pNewRedl;
1389                             pNewRedl = nullptr;
1390                             bCompress = true;
1391                         }
1392                     }
1393                     else if( SwComparePosition::Inside == eCmpPos )
1394                     {
1395                         // split up
1396                         if( *pEnd != *pREnd )
1397                         {
1398                             SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1399                             pCpy->SetStart( *pEnd );
1400                             mpRedlineTable->Insert( pCpy );
1401                         }
1402                         pRedl->SetEnd( *pStt, pREnd );
1403                         if( ( *pStt == *pRStt ) &&
1404                             ( pRedl->GetContentIdx() == nullptr ) )
1405                         {
1406                             mpRedlineTable->DeleteAndDestroy( n );
1407                             bDec = true;
1408                         }
1409                         else if( !pRedl->HasValidRange() )
1410                         {
1411                             // re-insert
1412                             mpRedlineTable->Remove( n );
1413                             mpRedlineTable->Insert( pRedl );
1414                         }
1415                     }
1416                     else if ( SwComparePosition::Outside == eCmpPos )
1417                     {
1418                         // handle overlapping redlines in broken documents
1419 
1420                         // split up the new redline, since it covers the
1421                         // existing redline. Insert the first part, and
1422                         // progress with the remainder as usual
1423                         SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1424                         pSplit->SetEnd( *pRStt );
1425                         pNewRedl->SetStart( *pREnd );
1426                         mpRedlineTable->Insert( pSplit );
1427                         if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1428                         {
1429                             delete pNewRedl;
1430                             pNewRedl = nullptr;
1431                             bCompress = true;
1432                         }
1433                     }
1434                     else if ( SwComparePosition::OverlapBehind == eCmpPos )
1435                     {
1436                         // handle overlapping redlines in broken documents
1437                         pNewRedl->SetStart( *pREnd );
1438                     }
1439                     else if ( SwComparePosition::OverlapBefore == eCmpPos )
1440                     {
1441                         // handle overlapping redlines in broken documents
1442                         *pEnd = *pRStt;
1443                         if( ( *pStt == *pEnd ) &&
1444                             ( pNewRedl->GetContentIdx() == nullptr ) )
1445                         {
1446                             delete pNewRedl;
1447                             pNewRedl = nullptr;
1448                             bCompress = true;
1449                         }
1450                     }
1451                     break;
1452                 case RedlineType::Delete:
1453                     if( SwComparePosition::Inside == eCmpPos )
1454                     {
1455                         // split up
1456                         if( *pEnd != *pREnd )
1457                         {
1458                             SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1459                             pCpy->SetStart( *pEnd );
1460                             mpRedlineTable->Insert( pCpy );
1461                         }
1462                         pRedl->SetEnd( *pStt, pREnd );
1463                         if( ( *pStt == *pRStt ) &&
1464                             ( pRedl->GetContentIdx() == nullptr ) )
1465                         {
1466                             mpRedlineTable->DeleteAndDestroy( n );
1467                             bDec = true;
1468                         }
1469                         else if( !pRedl->HasValidRange() )
1470                         {
1471                             // re-insert
1472                             mpRedlineTable->Remove( n );
1473                             mpRedlineTable->Insert( pRedl, n );
1474                         }
1475                     }
1476                     else if ( SwComparePosition::Outside == eCmpPos )
1477                     {
1478                         // handle overlapping redlines in broken documents
1479 
1480                         // split up the new redline, since it covers the
1481                         // existing redline. Insert the first part, and
1482                         // progress with the remainder as usual
1483                         SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1484                         pSplit->SetEnd( *pRStt );
1485                         pNewRedl->SetStart( *pREnd );
1486                         mpRedlineTable->Insert( pSplit );
1487                         if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1488                         {
1489                             delete pNewRedl;
1490                             pNewRedl = nullptr;
1491                             bCompress = true;
1492                         }
1493                     }
1494                     else if ( SwComparePosition::Equal == eCmpPos )
1495                     {
1496                         // handle identical redlines in broken documents
1497                         // delete old (delete) redline
1498                         mpRedlineTable->DeleteAndDestroy( n );
1499                         bDec = true;
1500                     }
1501                     else if ( SwComparePosition::OverlapBehind == eCmpPos )
1502                     {   // Another workaround for broken redlines
1503                         pNewRedl->SetStart( *pREnd );
1504                     }
1505                     break;
1506                 case RedlineType::Format:
1507                     switch( eCmpPos )
1508                     {
1509                     case SwComparePosition::OverlapBefore:
1510                         pRedl->SetStart( *pEnd, pRStt );
1511                         // re-insert
1512                         mpRedlineTable->Remove( n );
1513                         mpRedlineTable->Insert( pRedl, n );
1514                         bDec = true;
1515                         break;
1516 
1517                     case SwComparePosition::OverlapBehind:
1518                         pRedl->SetEnd( *pStt, pREnd );
1519                         if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
1520                         {
1521                             mpRedlineTable->DeleteAndDestroy( n );
1522                             bDec = true;
1523                         }
1524                         break;
1525 
1526                     case SwComparePosition::Equal:
1527                     case SwComparePosition::Outside:
1528                         // Overlaps the current one completely or has the
1529                         // same dimension, delete the old one
1530                         mpRedlineTable->DeleteAndDestroy( n );
1531                         bDec = true;
1532                         break;
1533 
1534                     case SwComparePosition::Inside:
1535                         // Overlaps the current one completely,
1536                         // split or shorten the new one
1537                         if( *pEnd != *pREnd )
1538                         {
1539                             if( *pEnd != *pRStt )
1540                             {
1541                                 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
1542                                 pNew->SetStart( *pEnd );
1543                                 pRedl->SetEnd( *pStt, pREnd );
1544                                 if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
1545                                     mpRedlineTable->DeleteAndDestroy( n );
1546                                 AppendRedline( pNew, bCallDelete );
1547                                 n = 0;      // re-initialize
1548                                 bDec = true;
1549                             }
1550                         }
1551                         else
1552                             pRedl->SetEnd( *pStt, pREnd );
1553                         break;
1554                     default:
1555                         break;
1556                     }
1557                     break;
1558                 default:
1559                     break;
1560                 }
1561                 break;
1562 
1563             case RedlineType::Delete:
1564                 switch( pRedl->GetType() )
1565                 {
1566                 case RedlineType::Delete:
1567                     switch( eCmpPos )
1568                     {
1569                     case SwComparePosition::Outside:
1570                         {
1571                             // Overlaps the current one completely,
1572                             // split the new one
1573                             if (*pEnd == *pREnd)
1574                             {
1575                                 pNewRedl->SetEnd(*pRStt, pEnd);
1576                             }
1577                             else if (*pStt == *pRStt)
1578                             {
1579                                 pNewRedl->SetStart(*pREnd, pStt);
1580                             }
1581                             else
1582                             {
1583                                 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
1584                                 pNew->SetStart( *pREnd );
1585                                 pNewRedl->SetEnd( *pRStt, pEnd );
1586                                 AppendRedline( pNew, bCallDelete );
1587                                 n = 0;      // re-initialize
1588                                 bDec = true;
1589                             }
1590                         }
1591                         break;
1592 
1593                     case SwComparePosition::Inside:
1594                     case SwComparePosition::Equal:
1595                         delete pNewRedl;
1596                         pNewRedl = nullptr;
1597                         bCompress = true;
1598                         break;
1599 
1600                     case SwComparePosition::OverlapBefore:
1601                     case SwComparePosition::OverlapBehind:
1602                         if( pRedl->IsOwnRedline( *pNewRedl ) &&
1603                             pRedl->CanCombine( *pNewRedl ))
1604                         {
1605                             // If that's the case we can merge it, meaning
1606                             // the new one covers this well
1607                             if( SwComparePosition::OverlapBehind == eCmpPos )
1608                                 pNewRedl->SetStart( *pRStt, pStt );
1609                             else
1610                                 pNewRedl->SetEnd( *pREnd, pEnd );
1611                             mpRedlineTable->DeleteAndDestroy( n );
1612                             bDec = true;
1613                         }
1614                         else if( SwComparePosition::OverlapBehind == eCmpPos )
1615                             pNewRedl->SetStart( *pREnd, pStt );
1616                         else
1617                             pNewRedl->SetEnd( *pRStt, pEnd );
1618                         break;
1619 
1620                     case SwComparePosition::CollideStart:
1621                     case SwComparePosition::CollideEnd:
1622                         if( pRedl->IsOwnRedline( *pNewRedl ) &&
1623                             pRedl->CanCombine( *pNewRedl ) )
1624                         {
1625                             if( IsHideChanges( meRedlineFlags ))
1626                             {
1627                                 // Before we can merge, we make it visible!
1628                                 // We insert temporarily so that pNew is
1629                                 // also dealt with when moving the indices.
1630                                 mpRedlineTable->Insert(pNewRedl);
1631                                 pRedl->Show(0, mpRedlineTable->GetPos(pRedl));
1632                                 mpRedlineTable->Remove( pNewRedl );
1633                                 pRStt = pRedl->Start();
1634                                 pREnd = pRedl->End();
1635                             }
1636 
1637                             // If that's the case we can merge it, meaning
1638                             // the new one covers this well
1639                             if( SwComparePosition::CollideStart == eCmpPos )
1640                                 pNewRedl->SetStart( *pRStt, pStt );
1641                             else
1642                                 pNewRedl->SetEnd( *pREnd, pEnd );
1643 
1644                             // delete current (below), and restart process with
1645                             // previous
1646                             SwRedlineTable::size_type nToBeDeleted = n;
1647                             bDec = true;
1648 
1649                             if( *(pNewRedl->Start()) <= *pREnd )
1650                             {
1651                                 // Whoooah, we just extended the new 'redline'
1652                                 // beyond previous redlines, so better start
1653                                 // again. Of course this is not supposed to
1654                                 // happen, and in an ideal world it doesn't,
1655                                 // but unfortunately this code is buggy and
1656                                 // totally rotten so it does happen and we
1657                                 // better fix it.
1658                                 n = 0;
1659                                 bDec = true;
1660                             }
1661 
1662                             mpRedlineTable->DeleteAndDestroy( nToBeDeleted );
1663                         }
1664                         break;
1665                     default:
1666                         break;
1667                     }
1668                     break;
1669 
1670                 case RedlineType::Insert:
1671                 {
1672                     // b62341295: Do not throw away redlines
1673                     // even if they are not allowed to be combined
1674                     RedlineFlags eOld = meRedlineFlags;
1675                     if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
1676                         pRedl->IsOwnRedline( *pNewRedl ) )
1677                     {
1678 
1679                         // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
1680                         // The ShowMode needs to be retained!
1681                         meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
1682                         switch( eCmpPos )
1683                         {
1684                         case SwComparePosition::Equal:
1685                             bCompress = true;
1686                             mpRedlineTable->DeleteAndDestroy( n );
1687                             bDec = true;
1688                             [[fallthrough]];
1689 
1690                         case SwComparePosition::Inside:
1691                             if( bCallDelete )
1692                             {
1693                                 // DeleteAndJoin does not yield the
1694                                 // desired result if there is no paragraph to
1695                                 // join with, i.e. at the end of the document.
1696                                 // For this case, we completely delete the
1697                                 // paragraphs (if, of course, we also start on
1698                                 // a paragraph boundary).
1699                                 if( (pStt->nContent == 0) &&
1700                                     pEnd->nNode.GetNode().IsEndNode() )
1701                                 {
1702                                     pEnd->nNode--;
1703                                     pEnd->nContent.Assign(
1704                                         pEnd->nNode.GetNode().GetTextNode(), 0);
1705                                     m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
1706                                 }
1707                                 else
1708                                     m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1709 
1710                                 bCompress = true;
1711                             }
1712                             if( !bCallDelete && !bDec && *pEnd == *pREnd )
1713                             {
1714                                 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1715                                 bCompress = true;
1716                             }
1717                             else if ( bCallDelete || !bDec )
1718                             {
1719                                 // delete new redline, except in some cases of fallthrough from previous
1720                                 // case ::Equal (eg. same portion w:del in w:ins in OOXML import)
1721                                 delete pNewRedl;
1722                                 pNewRedl = nullptr;
1723                             }
1724                             break;
1725 
1726                         case SwComparePosition::Outside:
1727                             {
1728                                 mpRedlineTable->Remove( n );
1729                                 bDec = true;
1730                                 if( bCallDelete )
1731                                 {
1732                                     TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1733                                     m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
1734                                     n = 0;      // re-initialize
1735                                 }
1736                                 delete pRedl;
1737                             }
1738                             break;
1739 
1740                         case SwComparePosition::OverlapBefore:
1741                             {
1742                                 SwPaM aPam( *pRStt, *pEnd );
1743 
1744                                 if( *pEnd == *pREnd )
1745                                     mpRedlineTable->DeleteAndDestroy( n );
1746                                 else
1747                                 {
1748                                     pRedl->SetStart( *pEnd, pRStt );
1749                                     // re-insert
1750                                     mpRedlineTable->Remove( n );
1751                                     mpRedlineTable->Insert( pRedl, n );
1752                                 }
1753 
1754                                 if( bCallDelete )
1755                                 {
1756                                     TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1757                                     m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1758                                     n = 0;      // re-initialize
1759                                 }
1760                                 bDec = true;
1761                             }
1762                             break;
1763 
1764                         case SwComparePosition::OverlapBehind:
1765                             {
1766                                 SwPaM aPam( *pStt, *pREnd );
1767 
1768                                 if( *pStt == *pRStt )
1769                                 {
1770                                     mpRedlineTable->DeleteAndDestroy( n );
1771                                     bDec = true;
1772                                 }
1773                                 else
1774                                     pRedl->SetEnd( *pStt, pREnd );
1775 
1776                                 if( bCallDelete )
1777                                 {
1778                                     TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1779                                     m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1780                                     n = 0;      // re-initialize
1781                                     bDec = true;
1782                                 }
1783                             }
1784                             break;
1785                         default:
1786                             break;
1787                         }
1788 
1789                         meRedlineFlags = eOld;
1790                     }
1791                     else
1792                     {
1793                         // it may be necessary to split the existing redline in
1794                         // two. In this case, pRedl will be changed to cover
1795                         // only part of its former range, and pNew will cover
1796                         // the remainder.
1797                         SwRangeRedline* pNew = nullptr;
1798 
1799                         switch( eCmpPos )
1800                         {
1801                         case SwComparePosition::Equal:
1802                             {
1803                                 pRedl->PushData( *pNewRedl );
1804                                 delete pNewRedl;
1805                                 pNewRedl = nullptr;
1806                                 if( IsHideChanges( meRedlineFlags ))
1807                                 {
1808                                     pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
1809                                 }
1810                                 bCompress = true;
1811                             }
1812                             break;
1813 
1814                         case SwComparePosition::Inside:
1815                             {
1816                                 if( *pRStt == *pStt )
1817                                 {
1818                                     // #i97421#
1819                                     // redline w/out extent loops
1820                                     if (*pStt != *pEnd)
1821                                     {
1822                                         pNewRedl->PushData( *pRedl, false );
1823                                         pRedl->SetStart( *pEnd, pRStt );
1824                                         // re-insert
1825                                         mpRedlineTable->Remove( n );
1826                                         mpRedlineTable->Insert( pRedl, n );
1827                                         bDec = true;
1828                                     }
1829                                 }
1830                                 else
1831                                 {
1832                                     pNewRedl->PushData( *pRedl, false );
1833                                     if( *pREnd != *pEnd )
1834                                     {
1835                                         pNew = new SwRangeRedline( *pRedl );
1836                                         pNew->SetStart( *pEnd );
1837                                     }
1838                                     pRedl->SetEnd( *pStt, pREnd );
1839                                     if( !pRedl->HasValidRange() )
1840                                     {
1841                                         // re-insert
1842                                         mpRedlineTable->Remove( n );
1843                                         mpRedlineTable->Insert( pRedl, n );
1844                                     }
1845                                 }
1846                             }
1847                             break;
1848 
1849                         case SwComparePosition::Outside:
1850                             {
1851                                 pRedl->PushData( *pNewRedl );
1852                                 if( *pEnd == *pREnd )
1853                                 {
1854                                     pNewRedl->SetEnd( *pRStt, pEnd );
1855                                 }
1856                                 else if (*pStt == *pRStt)
1857                                 {
1858                                     pNewRedl->SetStart(*pREnd, pStt);
1859                                 }
1860                                 else
1861                                 {
1862                                     pNew = new SwRangeRedline( *pNewRedl );
1863                                     pNew->SetEnd( *pRStt );
1864                                     pNewRedl->SetStart( *pREnd, pStt );
1865                                 }
1866                                 bCompress = true;
1867                             }
1868                             break;
1869 
1870                         case SwComparePosition::OverlapBefore:
1871                             {
1872                                 if( *pEnd == *pREnd )
1873                                 {
1874                                     pRedl->PushData( *pNewRedl );
1875                                     pNewRedl->SetEnd( *pRStt, pEnd );
1876                                     if( IsHideChanges( meRedlineFlags ))
1877                                     {
1878                                         mpRedlineTable->Insert(pNewRedl);
1879                                         pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
1880                                         mpRedlineTable->Remove( pNewRedl );
1881                                     }
1882                                 }
1883                                 else
1884                                 {
1885                                     pNew = new SwRangeRedline( *pRedl );
1886                                     pNew->PushData( *pNewRedl );
1887                                     pNew->SetEnd( *pEnd );
1888                                     pNewRedl->SetEnd( *pRStt, pEnd );
1889                                     pRedl->SetStart( *pNew->End(), pRStt ) ;
1890                                     // re-insert
1891                                     mpRedlineTable->Remove( n );
1892                                     mpRedlineTable->Insert( pRedl );
1893                                     bDec = true;
1894                                 }
1895                             }
1896                             break;
1897 
1898                         case SwComparePosition::OverlapBehind:
1899                             {
1900                                 if( *pStt == *pRStt )
1901                                 {
1902                                     pRedl->PushData( *pNewRedl );
1903                                     pNewRedl->SetStart( *pREnd, pStt );
1904                                     if( IsHideChanges( meRedlineFlags ))
1905                                     {
1906                                         mpRedlineTable->Insert( pNewRedl );
1907                                         pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
1908                                         mpRedlineTable->Remove( pNewRedl );
1909                                     }
1910                                 }
1911                                 else
1912                                 {
1913                                     pNew = new SwRangeRedline( *pRedl );
1914                                     pNew->PushData( *pNewRedl );
1915                                     pNew->SetStart( *pStt );
1916                                     pNewRedl->SetStart( *pREnd, pStt );
1917                                     pRedl->SetEnd( *pNew->Start(), pREnd );
1918                                     if( !pRedl->HasValidRange() )
1919                                     {
1920                                         // re-insert
1921                                         mpRedlineTable->Remove( n );
1922                                         mpRedlineTable->Insert( pRedl );
1923                                     }
1924                                 }
1925                             }
1926                             break;
1927                         default:
1928                             break;
1929                         }
1930 
1931                         // insert the pNew part (if it exists)
1932                         if( pNew )
1933                         {
1934                             mpRedlineTable->Insert( pNew );
1935 
1936                             // pNew must be deleted if Insert() wasn't
1937                             // successful. But that can't happen, since pNew is
1938                             // part of the original pRedl redline.
1939                             // OSL_ENSURE( bRet, "Can't insert existing redline?" );
1940 
1941                             // restart (now with pRedl being split up)
1942                             n = 0;
1943                             bDec = true;
1944                         }
1945                     }
1946                 }
1947                 break;
1948 
1949                 case RedlineType::Format:
1950                     switch( eCmpPos )
1951                     {
1952                     case SwComparePosition::OverlapBefore:
1953                         pRedl->SetStart( *pEnd, pRStt );
1954                         // re-insert
1955                         mpRedlineTable->Remove( n );
1956                         mpRedlineTable->Insert( pRedl, n );
1957                         bDec = true;
1958                         break;
1959 
1960                     case SwComparePosition::OverlapBehind:
1961                         pRedl->SetEnd( *pStt, pREnd );
1962                         break;
1963 
1964                     case SwComparePosition::Equal:
1965                     case SwComparePosition::Outside:
1966                         // Overlaps the current one completely or has the
1967                         // same dimension, delete the old one
1968                         mpRedlineTable->DeleteAndDestroy( n );
1969                         bDec = true;
1970                         break;
1971 
1972                     case SwComparePosition::Inside:
1973                         // Overlaps the current one completely,
1974                         // split or shorten the new one
1975                         if( *pEnd != *pREnd )
1976                         {
1977                             if( *pEnd != *pRStt )
1978                             {
1979                                 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
1980                                 pNew->SetStart( *pEnd );
1981                                 pRedl->SetEnd( *pStt, pREnd );
1982                                 if( ( *pStt == *pRStt ) &&
1983                                     ( pRedl->GetContentIdx() == nullptr ) )
1984                                     mpRedlineTable->DeleteAndDestroy( n );
1985                                 AppendRedline( pNew, bCallDelete );
1986                                 n = 0;      // re-initialize
1987                                 bDec = true;
1988                             }
1989                         }
1990                         else
1991                             pRedl->SetEnd( *pStt, pREnd );
1992                         break;
1993                     default:
1994                         break;
1995                     }
1996                     break;
1997                 default:
1998                     break;
1999                 }
2000                 break;
2001 
2002             case RedlineType::Format:
2003                 switch( pRedl->GetType() )
2004                 {
2005                 case RedlineType::Insert:
2006                 case RedlineType::Delete:
2007                     switch( eCmpPos )
2008                     {
2009                     case SwComparePosition::OverlapBefore:
2010                         pNewRedl->SetEnd( *pRStt, pEnd );
2011                         break;
2012 
2013                     case SwComparePosition::OverlapBehind:
2014                         pNewRedl->SetStart( *pREnd, pStt );
2015                         break;
2016 
2017                     case SwComparePosition::Equal:
2018                     case SwComparePosition::Inside:
2019                         delete pNewRedl;
2020                         pNewRedl = nullptr;
2021                         break;
2022 
2023                     case SwComparePosition::Outside:
2024                         // Overlaps the current one completely,
2025                         // split or shorten the new one
2026                         if (*pEnd == *pREnd)
2027                         {
2028                             pNewRedl->SetEnd(*pRStt, pEnd);
2029                         }
2030                         else if (*pStt == *pRStt)
2031                         {
2032                             pNewRedl->SetStart(*pREnd, pStt);
2033                         }
2034                         else
2035                         {
2036                             SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
2037                             pNew->SetStart( *pREnd );
2038                             pNewRedl->SetEnd( *pRStt, pEnd );
2039                             AppendRedline( pNew, bCallDelete );
2040                             n = 0;      // re-initialize
2041                             bDec = true;
2042                         }
2043                         break;
2044                     default:
2045                         break;
2046                     }
2047                     break;
2048                 case RedlineType::Format:
2049                     switch( eCmpPos )
2050                     {
2051                     case SwComparePosition::Outside:
2052                     case SwComparePosition::Equal:
2053                         {
2054                             // Overlaps the current one completely or has the
2055                             // same dimension, delete the old one
2056                             mpRedlineTable->DeleteAndDestroy( n );
2057                             bDec = true;
2058                         }
2059                         break;
2060 
2061                     case SwComparePosition::Inside:
2062                         if( pRedl->IsOwnRedline( *pNewRedl ) &&
2063                             pRedl->CanCombine( *pNewRedl ))
2064                         {
2065                             // own one can be ignored completely
2066                             delete pNewRedl;
2067                             pNewRedl = nullptr;
2068                         }
2069                         else if( *pREnd == *pEnd )
2070                             // or else only shorten the current one
2071                             pRedl->SetEnd( *pStt, pREnd );
2072                         else if( *pRStt == *pStt )
2073                         {
2074                             // or else only shorten the current one
2075                             pRedl->SetStart( *pEnd, pRStt );
2076                             // re-insert
2077                             mpRedlineTable->Remove( n );
2078                             mpRedlineTable->Insert( pRedl, n );
2079                             bDec = true;
2080                         }
2081                         else
2082                         {
2083                             // If it lies completely within the current one
2084                             // we need to split it
2085                             SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
2086                             pNew->SetStart( *pEnd );
2087                             pRedl->SetEnd( *pStt, pREnd );
2088                             AppendRedline( pNew, bCallDelete );
2089                             n = 0;      // re-initialize
2090                             bDec = true;
2091                         }
2092                         break;
2093 
2094                     case SwComparePosition::OverlapBefore:
2095                     case SwComparePosition::OverlapBehind:
2096                         if( pRedl->IsOwnRedline( *pNewRedl ) &&
2097                             pRedl->CanCombine( *pNewRedl ))
2098                         {
2099                             // If that's the case we can merge it, meaning
2100                             // the new one covers this well
2101                             if( SwComparePosition::OverlapBehind == eCmpPos )
2102                                 pNewRedl->SetStart( *pRStt, pStt );
2103                             else
2104                                 pNewRedl->SetEnd( *pREnd, pEnd );
2105                             mpRedlineTable->DeleteAndDestroy( n );
2106                             bDec = false;
2107                         }
2108                         else if( SwComparePosition::OverlapBehind == eCmpPos )
2109                             pNewRedl->SetStart( *pREnd, pStt );
2110                         else
2111                             pNewRedl->SetEnd( *pRStt, pEnd );
2112                         break;
2113 
2114                     case SwComparePosition::CollideEnd:
2115                         if( pRedl->IsOwnRedline( *pNewRedl ) &&
2116                             pRedl->CanCombine( *pNewRedl ) && n &&
2117                             *(*mpRedlineTable)[ n-1 ]->End() < *pStt )
2118                         {
2119                             // If that's the case we can merge it, meaning
2120                             // the new one covers this well
2121                             pNewRedl->SetEnd( *pREnd, pEnd );
2122                             mpRedlineTable->DeleteAndDestroy( n );
2123                             bDec = true;
2124                         }
2125                         break;
2126                     case SwComparePosition::CollideStart:
2127                         if( pRedl->IsOwnRedline( *pNewRedl ) &&
2128                             pRedl->CanCombine( *pNewRedl ) &&
2129                             n+1 < mpRedlineTable->size() &&
2130                             *(*mpRedlineTable)[ n+1 ]->Start() < *pEnd )
2131                         {
2132                             // If that's the case we can merge it, meaning
2133                             // the new one covers this well
2134                             pNewRedl->SetStart( *pRStt, pStt );
2135                             mpRedlineTable->DeleteAndDestroy( n );
2136                             bDec = true;
2137                         }
2138                         break;
2139                     default:
2140                         break;
2141                     }
2142                     break;
2143                 default:
2144                     break;
2145                 }
2146                 break;
2147 
2148             case RedlineType::FmtColl:
2149                 // How should we behave here?
2150                 // insert as is
2151                 break;
2152             default:
2153                 break;
2154             }
2155         }
2156 
2157         if( pNewRedl )
2158         {
2159             if( ( *pStt == *pEnd ) &&
2160                 ( pNewRedl->GetContentIdx() == nullptr ) )
2161             {   // Do not insert empty redlines
2162                 delete pNewRedl;
2163                 pNewRedl = nullptr;
2164             }
2165             else
2166             {
2167                 if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2168                 {
2169                     if ( pStt->nContent != 0 )
2170                     {
2171                         // tdf#119571 update the style of the joined paragraph
2172                         // after a partially deleted paragraph to show its correct style
2173                         // in "Show changes" mode, too. All removed paragraphs
2174                         // get the style of the first (partially deleted) paragraph
2175                         // to avoid text insertion with bad style in the deleted
2176                         // area later.
2177 
2178                         SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
2179                         SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
2180                         SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
2181                         SwTextNode* pTextNode;
2182                         SwNodeIndex aIdx( pEnd->nNode.GetNode() );
2183                         bool bFirst = true;
2184 
2185                         while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
2186                         {
2187                             pTextNode = pTextNd->GetTextNode();
2188                             if (pTextNode && pDelNode != pTextNode )
2189                             {
2190                                 SwPosition aPos(aIdx);
2191 
2192                                 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2193                                 {
2194                                     bCompress = true;
2195 
2196                                     // split redline to store ExtraData per paragraphs
2197                                     SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
2198                                     pPar->SetStart( aPos );
2199                                     pNewRedl->SetEnd( aPos );
2200 
2201                                     // get extradata for reset formatting of the modified paragraph
2202                                     SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
2203                                     if (pExtraData)
2204                                     {
2205                                         std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
2206                                         if (!bFirst)
2207                                             pExtraData->SetFormatAll(false);
2208                                         xRedlineExtraData.reset(pExtraData);
2209                                         pPar->SetExtraData( xRedlineExtraData.get() );
2210                                     }
2211 
2212                                     // skip empty redlines without ExtraData
2213                                     // FIXME: maybe checking pExtraData is redundant here
2214                                     if ( pExtraData || *pPar->Start() != *pPar->End() )
2215                                         mpRedlineTable->Insert( pPar );
2216                                     else
2217                                         delete pPar;
2218                                 }
2219 
2220                                 // modify paragraph formatting
2221                                 lcl_CopyStyle(*pStt, aPos);
2222                             }
2223                             pTextNd = SwNodes::GoPrevious( &aIdx );
2224 
2225                             if (bFirst)
2226                                 bFirst = false;
2227                         }
2228                     }
2229                 }
2230                 bool const ret = mpRedlineTable->Insert( pNewRedl );
2231                 assert(ret || !pNewRedl);
2232                 if (ret && !pNewRedl)
2233                 {
2234                     bMerged = true; // treat InsertWithValidRanges as "merge"
2235                 }
2236             }
2237         }
2238 
2239         if( bCompress )
2240             CompressRedlines();
2241     }
2242     else
2243     {
2244         if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2245         {
2246             RedlineFlags eOld = meRedlineFlags;
2247             // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2248             // The ShowMode needs to be retained!
2249             meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
2250             m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
2251             meRedlineFlags = eOld;
2252         }
2253         delete pNewRedl;
2254         pNewRedl = nullptr;
2255     }
2256     CHECK_REDLINE( *this )
2257 
2258     return (nullptr != pNewRedl)
2259         ? AppendResult::APPENDED
2260         : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
2261 }
2262 
AppendTableRowRedline(SwTableRowRedline * pNewRedl)2263 bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
2264 {
2265     // #TODO - equivalent for 'SwTableRowRedline'
2266     /*
2267     CHECK_REDLINE( this )
2268     */
2269 
2270     if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2271     {
2272         // #TODO - equivalent for 'SwTableRowRedline'
2273         /*
2274         pNewRedl->InvalidateRange();
2275         */
2276 
2277         // Make equivalent of 'AppendRedline' checks inside here too
2278 
2279         mpExtraRedlineTable->Insert( pNewRedl );
2280     }
2281     else
2282     {
2283         // TO DO - equivalent for 'SwTableRowRedline'
2284         /*
2285         if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2286         {
2287             RedlineFlags eOld = meRedlineFlags;
2288             // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2289             // The ShowMode needs to be retained!
2290             meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2291             DeleteAndJoin( *pNewRedl );
2292             meRedlineFlags = eOld;
2293         }
2294         delete pNewRedl, pNewRedl = 0;
2295         */
2296     }
2297     // #TODO - equivalent for 'SwTableRowRedline'
2298     /*
2299     CHECK_REDLINE( this )
2300     */
2301 
2302     return nullptr != pNewRedl;
2303 }
2304 
AppendTableCellRedline(SwTableCellRedline * pNewRedl)2305 bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
2306 {
2307     // #TODO - equivalent for 'SwTableCellRedline'
2308     /*
2309     CHECK_REDLINE( this )
2310     */
2311 
2312     if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2313     {
2314         // #TODO - equivalent for 'SwTableCellRedline'
2315         /*
2316         pNewRedl->InvalidateRange();
2317         */
2318 
2319         // Make equivalent of 'AppendRedline' checks inside here too
2320 
2321         mpExtraRedlineTable->Insert( pNewRedl );
2322     }
2323     else
2324     {
2325         // TO DO - equivalent for 'SwTableCellRedline'
2326         /*
2327         if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2328         {
2329             RedlineFlags eOld = meRedlineFlags;
2330             // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2331             // The ShowMode needs to be retained!
2332             meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2333             DeleteAndJoin( *pNewRedl );
2334             meRedlineFlags = eOld;
2335         }
2336         delete pNewRedl, pNewRedl = 0;
2337         */
2338     }
2339     // #TODO - equivalent for 'SwTableCellRedline'
2340     /*
2341     CHECK_REDLINE( this )
2342     */
2343 
2344     return nullptr != pNewRedl;
2345 }
2346 
CompressRedlines()2347 void DocumentRedlineManager::CompressRedlines()
2348 {
2349     CHECK_REDLINE( *this )
2350 
2351     void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
2352     RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
2353     if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
2354         pFnc = &SwRangeRedline::Show;
2355     else if (eShow == RedlineFlags::ShowInsert)
2356         pFnc = &SwRangeRedline::Hide;
2357 
2358     // Try to merge identical ones
2359     for( SwRedlineTable::size_type n = 1; n < mpRedlineTable->size(); ++n )
2360     {
2361         SwRangeRedline* pPrev = (*mpRedlineTable)[ n-1 ],
2362                     * pCur = (*mpRedlineTable)[ n ];
2363         const SwPosition* pPrevStt = pPrev->Start(),
2364                         * pPrevEnd = pPrevStt == pPrev->GetPoint()
2365                             ? pPrev->GetMark() : pPrev->GetPoint();
2366         const SwPosition* pCurStt = pCur->Start(),
2367                         * pCurEnd = pCurStt == pCur->GetPoint()
2368                             ? pCur->GetMark() : pCur->GetPoint();
2369         if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
2370             pPrevStt->nNode.GetNode().StartOfSectionNode() ==
2371             pCurEnd->nNode.GetNode().StartOfSectionNode() &&
2372             !pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() )
2373         {
2374             // we then can merge them
2375             SwRedlineTable::size_type nPrevIndex = n-1;
2376             pPrev->Show(0, nPrevIndex);
2377             pCur->Show(0, n);
2378 
2379             pPrev->SetEnd( *pCur->End() );
2380             mpRedlineTable->DeleteAndDestroy( n );
2381             --n;
2382             if( pFnc )
2383                 (pPrev->*pFnc)(0, nPrevIndex, false);
2384         }
2385     }
2386     CHECK_REDLINE( *this )
2387 
2388     // #TODO - add 'SwExtraRedlineTable' also ?
2389 }
2390 
SplitRedline(const SwPaM & rRange)2391 bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
2392 {
2393     bool bChg = false;
2394     SwRedlineTable::size_type n = 0;
2395     const SwPosition* pStt = rRange.Start();
2396     const SwPosition* pEnd = rRange.End();
2397     GetRedline( *pStt, &n );
2398     for ( ; n < mpRedlineTable->size(); ++n)
2399     {
2400         SwRangeRedline * pRedline = (*mpRedlineTable)[ n ];
2401         SwPosition *const pRedlineStart = pRedline->Start();
2402         SwPosition *const pRedlineEnd = pRedline->End();
2403         if (*pRedlineStart <= *pStt && *pStt <= *pRedlineEnd &&
2404             *pRedlineStart <= *pEnd && *pEnd <= *pRedlineEnd)
2405         {
2406             bChg = true;
2407             int nn = 0;
2408             if (*pStt == *pRedlineStart)
2409                 nn += 1;
2410             if (*pEnd == *pRedlineEnd)
2411                 nn += 2;
2412 
2413             SwRangeRedline* pNew = nullptr;
2414             switch( nn )
2415             {
2416             case 0:
2417                 pNew = new SwRangeRedline( *pRedline );
2418                 pRedline->SetEnd( *pStt, pRedlineEnd );
2419                 pNew->SetStart( *pEnd );
2420                 break;
2421 
2422             case 1:
2423                 *pRedlineStart = *pEnd;
2424                 break;
2425 
2426             case 2:
2427                 *pRedlineEnd = *pStt;
2428                 break;
2429 
2430             case 3:
2431                 pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2432                 mpRedlineTable->DeleteAndDestroy( n-- );
2433                 pRedline = nullptr;
2434                 break;
2435             }
2436             if (pRedline && !pRedline->HasValidRange())
2437             {
2438                 // re-insert
2439                 mpRedlineTable->Remove( n );
2440                 mpRedlineTable->Insert( pRedline, n );
2441             }
2442             if( pNew )
2443                 mpRedlineTable->Insert( pNew, n );
2444         }
2445         else if (*pEnd < *pRedlineStart)
2446             break;
2447     }
2448     return bChg;
2449 
2450     // #TODO - add 'SwExtraRedlineTable' also ?
2451 }
2452 
DeleteRedline(const SwPaM & rRange,bool bSaveInUndo,RedlineType nDelType)2453 bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
2454                             RedlineType nDelType )
2455 {
2456     if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
2457         return false;
2458 
2459     bool bChg = false;
2460 
2461     if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2462     {
2463         std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
2464         if( pUndo->GetRedlSaveCount() )
2465         {
2466             m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2467         }
2468     }
2469 
2470     const SwPosition* pStt = rRange.Start(),
2471                     * pEnd = pStt == rRange.GetPoint() ? rRange.GetMark()
2472                                                        : rRange.GetPoint();
2473     SwRedlineTable::size_type n = 0;
2474     GetRedline( *pStt, &n );
2475     for( ; n < mpRedlineTable->size() ; ++n )
2476     {
2477         SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
2478         if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
2479             continue;
2480 
2481         SwPosition* pRStt = pRedl->Start(),
2482                   * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
2483                                                        : pRedl->GetPoint();
2484         switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
2485         {
2486         case SwComparePosition::Equal:
2487         case SwComparePosition::Outside:
2488             pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2489             mpRedlineTable->DeleteAndDestroy( n-- );
2490             bChg = true;
2491             break;
2492 
2493         case SwComparePosition::OverlapBefore:
2494                 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2495                 pRedl->SetStart( *pEnd, pRStt );
2496                 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2497                 // re-insert
2498                 mpRedlineTable->Remove( n );
2499                 mpRedlineTable->Insert( pRedl );
2500                 --n;
2501             break;
2502 
2503         case SwComparePosition::OverlapBehind:
2504                 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2505                 pRedl->SetEnd( *pStt, pREnd );
2506                 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2507                 if( !pRedl->HasValidRange() )
2508                 {
2509                     // re-insert
2510                     mpRedlineTable->Remove( n );
2511                     mpRedlineTable->Insert( pRedl );
2512                     --n;
2513                 }
2514             break;
2515 
2516         case SwComparePosition::Inside:
2517             {
2518                 // this one needs to be split
2519                 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2520                 if( *pRStt == *pStt )
2521                 {
2522                     pRedl->SetStart( *pEnd, pRStt );
2523                     pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2524                     // re-insert
2525                     mpRedlineTable->Remove( n );
2526                     mpRedlineTable->Insert( pRedl );
2527                     --n;
2528                 }
2529                 else
2530                 {
2531                     SwRangeRedline* pCpy;
2532                     if( *pREnd != *pEnd )
2533                     {
2534                         pCpy = new SwRangeRedline( *pRedl );
2535                         pCpy->SetStart( *pEnd );
2536                         pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
2537                     }
2538                     else
2539                         pCpy = nullptr;
2540                     pRedl->SetEnd( *pStt, pREnd );
2541                     pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2542                     if( !pRedl->HasValidRange() )
2543                     {
2544                         // re-insert
2545                         mpRedlineTable->Remove( n );
2546                         mpRedlineTable->Insert( pRedl );
2547                         --n;
2548                     }
2549                     if( pCpy )
2550                         mpRedlineTable->Insert( pCpy );
2551                 }
2552             }
2553             break;
2554 
2555         case SwComparePosition::CollideEnd:
2556             // remove (not hidden) empty redlines created for fixing tdf#119571
2557             // (Note: hidden redlines are all empty, i.e. start and end are equal.)
2558             if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
2559             {
2560                 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2561                 mpRedlineTable->DeleteAndDestroy( n-- );
2562                 bChg = true;
2563                 break;
2564             }
2565             [[fallthrough]];
2566 
2567         case SwComparePosition::Before:
2568             n = mpRedlineTable->size();
2569             break;
2570         default:
2571             break;
2572         }
2573     }
2574 
2575     if( bChg )
2576         m_rDoc.getIDocumentState().SetModified();
2577 
2578     return bChg;
2579 
2580     // #TODO - add 'SwExtraRedlineTable' also ?
2581 }
2582 
DeleteRedline(const SwStartNode & rNode,bool bSaveInUndo,RedlineType nDelType)2583 bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
2584                             RedlineType nDelType )
2585 {
2586     SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
2587     return DeleteRedline(aTemp, bSaveInUndo, nDelType);
2588 }
2589 
GetRedlinePos(const SwNode & rNd,RedlineType nType) const2590 SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
2591 {
2592     const sal_uLong nNdIdx = rNd.GetIndex();
2593     for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
2594     {
2595         const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
2596         sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
2597               nMk = pTmp->GetMark()->nNode.GetIndex();
2598         if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; }
2599 
2600         if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
2601             nMk <= nNdIdx && nNdIdx <= nPt )
2602             return n;
2603 
2604         if( nMk > nNdIdx )
2605             break;
2606     }
2607     return SwRedlineTable::npos;
2608 
2609     // #TODO - add 'SwExtraRedlineTable' also ?
2610 }
2611 
HasRedline(const SwPaM & rPam,RedlineType nType,bool bStartOrEndInRange) const2612 bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const
2613 {
2614     SwPosition currentStart(*rPam.Start());
2615     SwPosition currentEnd(*rPam.End());
2616     SwNodeIndex pEndNodeIndex(currentEnd.nNode.GetNode());
2617 
2618     for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->nNode.GetNode(), nType );
2619                     n < mpRedlineTable->size(); ++n )
2620     {
2621         const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
2622 
2623         if ( pTmp->Start()->nNode > pEndNodeIndex )
2624             break;
2625 
2626         if( RedlineType::Any != nType && nType != pTmp->GetType() )
2627             continue;
2628 
2629         // redline over the range
2630         if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
2631              // starting or ending within the range
2632              ( !bStartOrEndInRange ||
2633                  ( currentStart < *pTmp->Start() || *pTmp->End() < currentEnd ) ) )
2634         {
2635             return true;
2636         }
2637     }
2638     return false;
2639 }
2640 
GetRedline(const SwPosition & rPos,SwRedlineTable::size_type * pFndPos) const2641 const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
2642                                     SwRedlineTable::size_type* pFndPos ) const
2643 {
2644     SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0;
2645     if( nO > 0 )
2646     {
2647         nO--;
2648         while( nU <= nO )
2649         {
2650             nM = nU + ( nO - nU ) / 2;
2651             const SwRangeRedline* pRedl = (*mpRedlineTable)[ nM ];
2652             const SwPosition* pStt = pRedl->Start();
2653             const SwPosition* pEnd = pStt == pRedl->GetPoint()
2654                                         ? pRedl->GetMark()
2655                                         : pRedl->GetPoint();
2656             if( pEnd == pStt
2657                     ? *pStt == rPos
2658                     : ( *pStt <= rPos && rPos < *pEnd ) )
2659             {
2660                 while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() &&
2661                     rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() )
2662                 {
2663                     --nM;
2664                     pRedl = (*mpRedlineTable)[ nM ];
2665                 }
2666                 // if there are format and insert changes in the same position
2667                 // show insert change first.
2668                 // since the redlines are sorted by position, only check the redline
2669                 // before and after the current redline
2670                 if( RedlineType::Format == pRedl->GetType() )
2671                 {
2672                     if( nM && rPos >= *(*mpRedlineTable)[ nM - 1 ]->Start() &&
2673                         rPos <= *(*mpRedlineTable)[ nM - 1 ]->End() &&
2674                         ( RedlineType::Insert == (*mpRedlineTable)[ nM - 1 ]->GetType() ) )
2675                     {
2676                         --nM;
2677                         pRedl = (*mpRedlineTable)[ nM ];
2678                     }
2679                     else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() &&
2680                         rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() &&
2681                         ( RedlineType::Insert == (*mpRedlineTable)[ nM + 1 ]->GetType() ) )
2682                     {
2683                         ++nM;
2684                         pRedl = (*mpRedlineTable)[ nM ];
2685                     }
2686                 }
2687 
2688                 if( pFndPos )
2689                     *pFndPos = nM;
2690                 return pRedl;
2691             }
2692             else if( *pEnd <= rPos )
2693                 nU = nM + 1;
2694             else if( nM == 0 )
2695             {
2696                 if( pFndPos )
2697                     *pFndPos = nU;
2698                 return nullptr;
2699             }
2700             else
2701                 nO = nM - 1;
2702         }
2703     }
2704     if( pFndPos )
2705         *pFndPos = nU;
2706     return nullptr;
2707 
2708     // #TODO - add 'SwExtraRedlineTable' also ?
2709 }
2710 
AcceptRedline(SwRedlineTable::size_type nPos,bool bCallDelete)2711 bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
2712 {
2713     bool bRet = false;
2714 
2715     // Switch to visible in any case
2716     if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2717         (RedlineFlags::ShowMask & meRedlineFlags) )
2718       SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2719 
2720     SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
2721     pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2722     pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2723     if( pTmp->HasMark() && pTmp->IsVisible() )
2724     {
2725         if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2726         {
2727             SwRewriter aRewriter;
2728 
2729             aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
2730             m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
2731         }
2732 
2733         int nLoopCnt = 2;
2734         sal_uInt16 nSeqNo = pTmp->GetSeqNo();
2735 
2736         do {
2737 
2738             if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2739             {
2740                 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2741                     std::make_unique<SwUndoAcceptRedline>(*pTmp) );
2742             }
2743 
2744             bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete );
2745 
2746             if( nSeqNo )
2747             {
2748                 if( SwRedlineTable::npos == nPos )
2749                     nPos = 0;
2750                 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
2751                                     ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
2752                                     : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
2753                 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
2754                     SwRedlineTable::npos != ( nFndPos =
2755                         mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
2756                 {
2757                     nPos = nFndPos;
2758                     pTmp = (*mpRedlineTable)[ nPos ];
2759                 }
2760                 else
2761                     nLoopCnt = 0;
2762             }
2763             else
2764                 nLoopCnt = 0;
2765 
2766         } while( nLoopCnt );
2767 
2768         if( bRet )
2769         {
2770             CompressRedlines();
2771             m_rDoc.getIDocumentState().SetModified();
2772         }
2773 
2774         if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2775         {
2776             m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
2777         }
2778     }
2779     return bRet;
2780 
2781     // #TODO - add 'SwExtraRedlineTable' also ?
2782 }
2783 
AcceptRedline(const SwPaM & rPam,bool bCallDelete)2784 bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
2785 {
2786     // Switch to visible in any case
2787     if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2788         (RedlineFlags::ShowMask & meRedlineFlags) )
2789       SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2790 
2791     // The Selection is only in the ContentSection. If there are Redlines
2792     // to Non-ContentNodes before or after that, then the Selections
2793     // expand to them.
2794     SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
2795     lcl_AdjustRedlineRange( aPam );
2796 
2797     if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2798     {
2799         m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
2800         m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAcceptRedline>( aPam ));
2801     }
2802 
2803     int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable,
2804                                      bCallDelete, aPam );
2805     if( nRet > 0 )
2806     {
2807         CompressRedlines();
2808         m_rDoc.getIDocumentState().SetModified();
2809     }
2810     if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2811     {
2812         OUString aTmpStr;
2813 
2814         {
2815             SwRewriter aRewriter;
2816             aRewriter.AddRule(UndoArg1, OUString::number(nRet));
2817             aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2818         }
2819 
2820         SwRewriter aRewriter;
2821         aRewriter.AddRule(UndoArg1, aTmpStr);
2822 
2823         m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
2824     }
2825     return nRet != 0;
2826 
2827     // #TODO - add 'SwExtraRedlineTable' also ?
2828 }
2829 
AcceptRedlineParagraphFormatting(const SwPaM & rPam)2830 void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
2831 {
2832     const SwPosition* pStt = rPam.Start(),
2833                     * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
2834                                                      : rPam.GetPoint();
2835 
2836     const sal_uLong nSttIdx = pStt->nNode.GetIndex();
2837     const sal_uLong nEndIdx = pEnd->nNode.GetIndex();
2838 
2839     for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
2840     {
2841         const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
2842         sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
2843               nMk = pTmp->GetMark()->nNode.GetIndex();
2844         if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; }
2845 
2846         if( RedlineType::ParagraphFormat == pTmp->GetType() &&
2847             ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
2848                 AcceptRedline( n, false );
2849 
2850         if( nMk > nEndIdx )
2851             break;
2852     }
2853 }
2854 
RejectRedline(SwRedlineTable::size_type nPos,bool bCallDelete)2855 bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
2856 {
2857     bool bRet = false;
2858 
2859     // Switch to visible in any case
2860     if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2861         (RedlineFlags::ShowMask & meRedlineFlags) )
2862       SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2863 
2864     SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
2865     pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2866     pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2867     if( pTmp->HasMark() && pTmp->IsVisible() )
2868     {
2869         if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2870         {
2871             SwRewriter aRewriter;
2872 
2873             aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
2874             m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
2875         }
2876 
2877         int nLoopCnt = 2;
2878         sal_uInt16 nSeqNo = pTmp->GetSeqNo();
2879 
2880         do {
2881 
2882             if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2883             {
2884                 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2885                     std::make_unique<SwUndoRejectRedline>( *pTmp ) );
2886             }
2887 
2888             bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete );
2889 
2890             if( nSeqNo )
2891             {
2892                 if( SwRedlineTable::npos == nPos )
2893                     nPos = 0;
2894                 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
2895                                     ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
2896                                     : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
2897                 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
2898                     SwRedlineTable::npos != ( nFndPos =
2899                             mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
2900                 {
2901                     nPos = nFndPos;
2902                     pTmp = (*mpRedlineTable)[ nPos ];
2903                 }
2904                 else
2905                     nLoopCnt = 0;
2906             }
2907             else
2908                 nLoopCnt = 0;
2909 
2910         } while( nLoopCnt );
2911 
2912         if( bRet )
2913         {
2914             CompressRedlines();
2915             m_rDoc.getIDocumentState().SetModified();
2916         }
2917 
2918         if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2919         {
2920             m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
2921         }
2922     }
2923     return bRet;
2924 
2925     // #TODO - add 'SwExtraRedlineTable' also ?
2926 }
2927 
RejectRedline(const SwPaM & rPam,bool bCallDelete)2928 bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
2929 {
2930     // Switch to visible in any case
2931     if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2932         (RedlineFlags::ShowMask & meRedlineFlags) )
2933       SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2934 
2935     // The Selection is only in the ContentSection. If there are Redlines
2936     // to Non-ContentNodes before or after that, then the Selections
2937     // expand to them.
2938     SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
2939     lcl_AdjustRedlineRange( aPam );
2940 
2941     if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2942     {
2943         m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
2944         m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) );
2945     }
2946 
2947     int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable,
2948                                         bCallDelete, aPam );
2949     if( nRet > 0 )
2950     {
2951         CompressRedlines();
2952         m_rDoc.getIDocumentState().SetModified();
2953     }
2954     if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2955     {
2956         OUString aTmpStr;
2957 
2958         {
2959             SwRewriter aRewriter;
2960             aRewriter.AddRule(UndoArg1, OUString::number(nRet));
2961             aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2962         }
2963 
2964         SwRewriter aRewriter;
2965         aRewriter.AddRule(UndoArg1, aTmpStr);
2966 
2967         m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
2968     }
2969 
2970     return nRet != 0;
2971 
2972     // #TODO - add 'SwExtraRedlineTable' also ?
2973 }
2974 
AcceptAllRedline(bool bAccept)2975 void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
2976 {
2977     bool bSuccess = true;
2978     OUString sUndoStr;
2979     IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
2980 
2981     if (mpRedlineTable->size() > 1)
2982     {
2983         {
2984             SwRewriter aRewriter;
2985             aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->size()));
2986             sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2987         }
2988 
2989         SwRewriter aRewriter;
2990         aRewriter.AddRule(UndoArg1, sUndoStr);
2991         rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
2992     }
2993 
2994     while (!mpRedlineTable->empty() && bSuccess)
2995     {
2996         if (bAccept)
2997             bSuccess = AcceptRedline(mpRedlineTable->size() - 1, true);
2998         else
2999             bSuccess = RejectRedline(mpRedlineTable->size() - 1, true);
3000     }
3001 
3002     if (!sUndoStr.isEmpty())
3003     {
3004         rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
3005     }
3006 }
3007 
SelNextRedline(SwPaM & rPam) const3008 const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
3009 {
3010     rPam.DeleteMark();
3011     rPam.SetMark();
3012 
3013     SwPosition& rSttPos = *rPam.GetPoint();
3014     SwPosition aSavePos( rSttPos );
3015     bool bRestart;
3016 
3017     // If the starting position points to the last valid ContentNode,
3018     // we take the next Redline in any case.
3019     SwRedlineTable::size_type n = 0;
3020     const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
3021     if( pFnd )
3022     {
3023         const SwPosition* pEnd = pFnd->End();
3024         if( !pEnd->nNode.GetNode().IsContentNode() )
3025         {
3026             SwNodeIndex aTmp( pEnd->nNode );
3027             SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
3028             if( !pCNd || ( aTmp == rSttPos.nNode &&
3029                 pCNd->Len() == rSttPos.nContent.GetIndex() ))
3030                 pFnd = nullptr;
3031         }
3032         if( pFnd )
3033             rSttPos = *pFnd->End();
3034     }
3035 
3036     do {
3037         bRestart = false;
3038 
3039         for( ; !pFnd && n < mpRedlineTable->size(); ++n )
3040         {
3041             pFnd = (*mpRedlineTable)[ n ];
3042             if( pFnd->HasMark() && pFnd->IsVisible() )
3043             {
3044                 *rPam.GetMark() = *pFnd->Start();
3045                 rSttPos = *pFnd->End();
3046                 break;
3047             }
3048             else
3049                 pFnd = nullptr;
3050         }
3051 
3052         if( pFnd )
3053         {
3054             // Merge all of the same type and author that are
3055             // consecutive into one Selection.
3056             const SwPosition* pPrevEnd = pFnd->End();
3057             while( ++n < mpRedlineTable->size() )
3058             {
3059                 const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
3060                 if( pTmp->HasMark() && pTmp->IsVisible() )
3061                 {
3062                     const SwPosition *pRStt;
3063                     if( pFnd->GetType() != pTmp->GetType() ||
3064                         pFnd->GetAuthor() != pTmp->GetAuthor() )
3065                         break;
3066                     pRStt = pTmp->Start();
3067                     if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
3068                     {
3069                         pPrevEnd = pTmp->End();
3070                         rSttPos = *pPrevEnd;
3071                     }
3072                     else
3073                         break;
3074                 }
3075             }
3076         }
3077 
3078         if( pFnd )
3079         {
3080             const SwRangeRedline* pSaveFnd = pFnd;
3081 
3082             SwContentNode* pCNd;
3083             SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
3084             if( !pIdx->GetNode().IsContentNode() )
3085             {
3086                 pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
3087                 if( pCNd )
3088                 {
3089                     if( *pIdx <= rPam.GetPoint()->nNode )
3090                         rPam.GetMark()->nContent.Assign( pCNd, 0 );
3091                     else
3092                         pFnd = nullptr;
3093                 }
3094             }
3095 
3096             if( pFnd )
3097             {
3098                 pIdx = &rPam.GetPoint()->nNode;
3099                 if( !pIdx->GetNode().IsContentNode() )
3100                 {
3101                     pCNd = SwNodes::GoPrevSection( pIdx );
3102                     if( pCNd )
3103                     {
3104                         if( *pIdx >= rPam.GetMark()->nNode )
3105                             rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
3106                         else
3107                             pFnd = nullptr;
3108                     }
3109                 }
3110             }
3111 
3112             if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3113             {
3114                 if( n < mpRedlineTable->size() )
3115                 {
3116                     bRestart = true;
3117                     *rPam.GetPoint() = *pSaveFnd->End();
3118                 }
3119                 else
3120                 {
3121                     rPam.DeleteMark();
3122                     *rPam.GetPoint() = aSavePos;
3123                 }
3124                 pFnd = nullptr;
3125             }
3126         }
3127     } while( bRestart );
3128 
3129     return pFnd;
3130 
3131     // #TODO - add 'SwExtraRedlineTable' also ?
3132 }
3133 
SelPrevRedline(SwPaM & rPam) const3134 const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
3135 {
3136     rPam.DeleteMark();
3137     rPam.SetMark();
3138 
3139     SwPosition& rSttPos = *rPam.GetPoint();
3140     SwPosition aSavePos( rSttPos );
3141     bool bRestart;
3142 
3143     // If the starting position points to the last valid ContentNode,
3144     // we take the previous Redline in any case.
3145     SwRedlineTable::size_type n = 0;
3146     const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
3147     if( pFnd )
3148     {
3149         const SwPosition* pStt = pFnd->Start();
3150         if( !pStt->nNode.GetNode().IsContentNode() )
3151         {
3152             SwNodeIndex aTmp( pStt->nNode );
3153             SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
3154             if( !pCNd || ( aTmp == rSttPos.nNode &&
3155                 !rSttPos.nContent.GetIndex() ))
3156                 pFnd = nullptr;
3157         }
3158         if( pFnd )
3159             rSttPos = *pFnd->Start();
3160     }
3161 
3162     do {
3163         bRestart = false;
3164 
3165         while( !pFnd && 0 < n )
3166         {
3167             pFnd = (*mpRedlineTable)[ --n ];
3168             if( pFnd->HasMark() && pFnd->IsVisible() )
3169             {
3170                 *rPam.GetMark() = *pFnd->End();
3171                 rSttPos = *pFnd->Start();
3172             }
3173             else
3174                 pFnd = nullptr;
3175         }
3176 
3177         if( pFnd )
3178         {
3179             // Merge all of the same type and author that are
3180             // consecutive into one Selection.
3181             const SwPosition* pNextStt = pFnd->Start();
3182             while( 0 < n )
3183             {
3184                 const SwRangeRedline* pTmp = (*mpRedlineTable)[ --n ];
3185                 if( pTmp->HasMark() && pTmp->IsVisible() )
3186                 {
3187                     const SwPosition *pREnd;
3188                     if( pFnd->GetType() == pTmp->GetType() &&
3189                         pFnd->GetAuthor() == pTmp->GetAuthor() &&
3190                         ( *pNextStt == *( pREnd = pTmp->End() ) ||
3191                           IsPrevPos( *pREnd, *pNextStt )) )
3192                     {
3193                         pNextStt = pTmp->Start();
3194                         rSttPos = *pNextStt;
3195                     }
3196                     else
3197                     {
3198                         ++n;
3199                         break;
3200                     }
3201                 }
3202             }
3203         }
3204 
3205         if( pFnd )
3206         {
3207             const SwRangeRedline* pSaveFnd = pFnd;
3208 
3209             SwContentNode* pCNd;
3210             SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
3211             if( !pIdx->GetNode().IsContentNode() )
3212             {
3213                 pCNd = SwNodes::GoPrevSection( pIdx );
3214                 if( pCNd )
3215                 {
3216                     if( *pIdx >= rPam.GetPoint()->nNode )
3217                         rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() );
3218                     else
3219                         pFnd = nullptr;
3220                 }
3221             }
3222 
3223             if( pFnd )
3224             {
3225                 pIdx = &rPam.GetPoint()->nNode;
3226                 if( !pIdx->GetNode().IsContentNode() )
3227                 {
3228                     pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
3229                     if( pCNd )
3230                     {
3231                         if( *pIdx <= rPam.GetMark()->nNode )
3232                             rPam.GetPoint()->nContent.Assign( pCNd, 0 );
3233                         else
3234                             pFnd = nullptr;
3235                     }
3236                 }
3237             }
3238 
3239             if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3240             {
3241                 if( n )
3242                 {
3243                     bRestart = true;
3244                     *rPam.GetPoint() = *pSaveFnd->Start();
3245                 }
3246                 else
3247                 {
3248                     rPam.DeleteMark();
3249                     *rPam.GetPoint() = aSavePos;
3250                 }
3251                 pFnd = nullptr;
3252             }
3253         }
3254     } while( bRestart );
3255 
3256     return pFnd;
3257 
3258     // #TODO - add 'SwExtraRedlineTable' also ?
3259 }
3260 
3261 // Set comment at the Redline
SetRedlineComment(const SwPaM & rPaM,const OUString & rS)3262 bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
3263 {
3264     bool bRet = false;
3265     const SwPosition* pStt = rPaM.Start(),
3266                     * pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
3267                                                      : rPaM.GetPoint();
3268     SwRedlineTable::size_type n = 0;
3269     if( GetRedlineTable().FindAtPosition( *pStt, n ) )
3270     {
3271         for( ; n < mpRedlineTable->size(); ++n )
3272         {
3273             bRet = true;
3274             SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
3275             if( pStt != pEnd && *pTmp->Start() > *pEnd )
3276                 break;
3277 
3278             pTmp->SetComment( rS );
3279             if( *pTmp->End() >= *pEnd )
3280                 break;
3281         }
3282     }
3283     if( bRet )
3284         m_rDoc.getIDocumentState().SetModified();
3285 
3286     return bRet;
3287 
3288     // #TODO - add 'SwExtraRedlineTable' also ?
3289 }
3290 
3291 // Create a new author if necessary
GetRedlineAuthor()3292 std::size_t DocumentRedlineManager::GetRedlineAuthor()
3293 {
3294     return SW_MOD()->GetRedlineAuthor();
3295 }
3296 
3297 /// Insert new author into the Table for the Readers etc.
InsertRedlineAuthor(const OUString & rNew)3298 std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
3299 {
3300     return SW_MOD()->InsertRedlineAuthor(rNew);
3301 }
3302 
UpdateRedlineAttr()3303 void DocumentRedlineManager::UpdateRedlineAttr()
3304 {
3305     const SwRedlineTable& rTable = GetRedlineTable();
3306     for(SwRangeRedline* pRedl : rTable)
3307     {
3308         if( pRedl->IsVisible() )
3309             pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
3310     }
3311 
3312     // #TODO - add 'SwExtraRedlineTable' also ?
3313 }
3314 
GetRedlinePassword() const3315 const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
3316 {
3317     return maRedlinePasswd;
3318 }
3319 
SetRedlinePassword(const uno::Sequence<sal_Int8> & rNewPassword)3320 void DocumentRedlineManager::SetRedlinePassword(
3321             /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
3322 {
3323     maRedlinePasswd = rNewPassword;
3324     m_rDoc.getIDocumentState().SetModified();
3325 }
3326 
3327 /// Set comment text for the Redline, which is inserted later on via
3328 /// AppendRedline. Is used by Autoformat.
3329 /// A null pointer resets the mode. The pointer is not copied, so it
3330 /// needs to stay valid!
SetAutoFormatRedlineComment(const OUString * pText,sal_uInt16 nSeqNo)3331 void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
3332 {
3333     m_rDoc.SetAutoFormatRedline( nullptr != pText );
3334     if( pText )
3335     {
3336         mpAutoFormatRedlnComment.reset( new OUString( *pText ) );
3337     }
3338     else
3339     {
3340         mpAutoFormatRedlnComment.reset();
3341     }
3342 
3343     mnAutoFormatRedlnCommentNo = nSeqNo;
3344 }
3345 
HideAll(bool bDeletion)3346 void DocumentRedlineManager::HideAll( bool bDeletion )
3347 {
3348     const SwRedlineTable& rTable = GetRedlineTable();
3349     for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3350     {
3351         SwRangeRedline* pRedline = rTable[i-1];
3352         if ( pRedline->GetType() == RedlineType::Delete )
3353         {
3354             if ( bDeletion && pRedline->IsVisible() )
3355             {
3356                 pRedline->Hide(0, rTable.GetPos(pRedline), false);
3357                 pRedline->Hide(1, rTable.GetPos(pRedline), false);
3358             }
3359             else if ( !bDeletion && !pRedline->IsVisible() )
3360             {
3361                 pRedline->Show(0, rTable.GetPos(pRedline), true);
3362                 pRedline->Show(1, rTable.GetPos(pRedline), true);
3363             }
3364         }
3365         else if ( pRedline->GetType() == RedlineType::Insert )
3366         {
3367             if ( !bDeletion && pRedline->IsVisible() )
3368             {
3369                 pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
3370                 pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
3371             }
3372             else if ( bDeletion && !pRedline->IsVisible() )
3373             {
3374                 pRedline->Show(0, rTable.GetPos(pRedline), true);
3375                 pRedline->Show(1, rTable.GetPos(pRedline), true);
3376             }
3377         }
3378     }
3379 }
3380 
ShowAll()3381 void DocumentRedlineManager::ShowAll()
3382 {
3383     const SwRedlineTable& rTable = GetRedlineTable();
3384     for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3385     {
3386         SwRangeRedline* pRedline = rTable[i-1];
3387         if ( !pRedline->IsVisible() )
3388         {
3389             pRedline->Show(0, rTable.GetPos(pRedline), true);
3390             pRedline->Show(1, rTable.GetPos(pRedline), true);
3391         }
3392     }
3393 }
3394 
~DocumentRedlineManager()3395 DocumentRedlineManager::~DocumentRedlineManager()
3396 {
3397 }
3398 
3399 }
3400 
3401 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3402