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