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 
20 #include <libxml/xmlwriter.h>
21 #include <boost/property_tree/json_parser.hpp>
22 
23 #include <osl/diagnose.h>
24 #include <sal/log.hxx>
25 #include <tools/datetimeutils.hxx>
26 #include <hintids.hxx>
27 #include <svl/itemiter.hxx>
28 #include <comphelper/lok.hxx>
29 #include <comphelper/string.hxx>
30 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
31 #include <unotools/datetime.hxx>
32 #include <sfx2/viewsh.hxx>
33 #include <swmodule.hxx>
34 #include <doc.hxx>
35 #include <docredln.hxx>
36 #include <IDocumentUndoRedo.hxx>
37 #include <DocumentContentOperationsManager.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <IDocumentState.hxx>
40 #include <IDocumentLayoutAccess.hxx>
41 #include <IDocumentStylePoolAccess.hxx>
42 #include <docary.hxx>
43 #include <ndtxt.hxx>
44 #include <redline.hxx>
45 #include <UndoCore.hxx>
46 #include <hints.hxx>
47 #include <pamtyp.hxx>
48 #include <poolfmt.hxx>
49 #include <view.hxx>
50 #include <viewopt.hxx>
51 #include <usrpref.hxx>
52 #include <viewsh.hxx>
53 #include <viscrs.hxx>
54 #include <rootfrm.hxx>
55 #include <strings.hrc>
56 #include <wrtsh.hxx>
57 #include <txtfld.hxx>
58 
59 #include <flowfrm.hxx>
60 
61 using namespace com::sun::star;
62 
63 #ifdef DBG_UTIL
64 
sw_DebugRedline(const SwDoc * pDoc)65     void sw_DebugRedline( const SwDoc* pDoc )
66     {
67         static SwRedlineTable::size_type nWatch = 0; // loplugin:constvars:ignore
68         const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
69         for( SwRedlineTable::size_type n = 0; n < rTable.size(); ++n )
70         {
71             SwRedlineTable::size_type nDummy = 0;
72             const SwRangeRedline* pCurrent = rTable[ n ];
73             const SwRangeRedline* pNext = n+1 < rTable.size() ? rTable[ n+1 ] : nullptr;
74             if( pCurrent == pNext )
75                 ++nDummy;
76             if( n == nWatch )
77                 ++nDummy; // Possible debugger breakpoint
78         }
79     }
80 
81 #endif
82 
83 
~SwExtraRedlineTable()84 SwExtraRedlineTable::~SwExtraRedlineTable()
85 {
86     DeleteAndDestroyAll();
87 }
88 
dumpAsXml(xmlTextWriterPtr pWriter) const89 void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
90 {
91     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedlineTable"));
92     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
93 
94     for (sal_uInt16 nCurExtraRedlinePos = 0; nCurExtraRedlinePos < GetSize(); ++nCurExtraRedlinePos)
95     {
96         const SwExtraRedline* pExtraRedline = GetRedline(nCurExtraRedlinePos);
97         (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedline"));
98         (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
99         (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline).name()));
100         (void)xmlTextWriterEndElement(pWriter);
101     }
102     (void)xmlTextWriterEndElement(pWriter);
103 }
104 
105 #if OSL_DEBUG_LEVEL > 0
CheckPosition(const SwPosition * pStt,const SwPosition * pEnd)106 static bool CheckPosition( const SwPosition* pStt, const SwPosition* pEnd )
107 {
108     int nError = 0;
109     SwNode* pSttNode = &pStt->nNode.GetNode();
110     SwNode* pEndNode = &pEnd->nNode.GetNode();
111     SwNode* pSttTab = pSttNode->StartOfSectionNode()->FindTableNode();
112     SwNode* pEndTab = pEndNode->StartOfSectionNode()->FindTableNode();
113     SwNode* pSttStart = pSttNode;
114     while( pSttStart && (!pSttStart->IsStartNode() || pSttStart->IsSectionNode() ||
115         pSttStart->IsTableNode() ) )
116         pSttStart = pSttStart->StartOfSectionNode();
117     SwNode* pEndStart = pEndNode;
118     while( pEndStart && (!pEndStart->IsStartNode() || pEndStart->IsSectionNode() ||
119         pEndStart->IsTableNode() ) )
120         pEndStart = pEndStart->StartOfSectionNode();
121     assert(pSttTab == pEndTab);
122     if( pSttTab != pEndTab )
123         nError = 1;
124     assert(pSttTab || pSttStart == pEndStart);
125     if( !pSttTab && pSttStart != pEndStart )
126         nError |= 2;
127     if( nError )
128         nError += 10;
129     return nError != 0;
130 }
131 #endif
132 
DeleteAllTableRedlines(SwDoc & rDoc,const SwTable & rTable,bool bSaveInUndo,RedlineType nRedlineTypeToDelete)133 bool SwExtraRedlineTable::DeleteAllTableRedlines( SwDoc& rDoc, const SwTable& rTable, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
134 {
135     bool bChg = false;
136 
137     if (bSaveInUndo && rDoc.GetIDocumentUndoRedo().DoesUndo())
138     {
139         // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
140         /*
141         SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
142         if( pUndo->GetRedlSaveCount() )
143         {
144             GetIDocumentUndoRedo().AppendUndo(pUndo);
145         }
146         else
147             delete pUndo;
148         */
149     }
150 
151     for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); )
152     {
153         SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
154         const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
155         if (pTableCellRedline)
156         {
157             const SwTableBox *pRedTabBox = &pTableCellRedline->GetTableBox();
158             const SwTable& rRedTable = pRedTabBox->GetSttNd()->FindTableNode()->GetTable();
159             if ( &rRedTable == &rTable )
160             {
161                 // Redline for this table
162                 const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
163                 const RedlineType nRedlineType = aRedlineData.GetType();
164 
165                 // Check if this redline object type should be deleted
166                 if (RedlineType::Any == nRedlineTypeToDelete || nRedlineTypeToDelete == nRedlineType)
167                 {
168 
169                     DeleteAndDestroy( nCurRedlinePos );
170                     bChg = true;
171                     continue; // don't increment position after delete
172                 }
173             }
174         }
175         else
176         {
177             const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
178             if (pTableRowRedline)
179             {
180                 const SwTableLine *pRedTabLine = &pTableRowRedline->GetTableLine();
181                 const SwTableBoxes &rRedTabBoxes = pRedTabLine->GetTabBoxes();
182                 const SwTable& rRedTable = rRedTabBoxes[0]->GetSttNd()->FindTableNode()->GetTable();
183                 if ( &rRedTable == &rTable )
184                 {
185                     // Redline for this table
186                     const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
187                     const RedlineType nRedlineType = aRedlineData.GetType();
188 
189                     // Check if this redline object type should be deleted
190                     if (RedlineType::Any == nRedlineTypeToDelete || nRedlineTypeToDelete == nRedlineType)
191 
192                     {
193                         DeleteAndDestroy( nCurRedlinePos );
194                         bChg = true;
195                         continue; // don't increment position after delete
196                     }
197                 }
198             }
199         }
200         ++nCurRedlinePos;
201     }
202 
203     if( bChg )
204         rDoc.getIDocumentState().SetModified();
205 
206     return bChg;
207 }
208 
DeleteTableRowRedline(SwDoc * pDoc,const SwTableLine & rTableLine,bool bSaveInUndo,RedlineType nRedlineTypeToDelete)209 bool SwExtraRedlineTable::DeleteTableRowRedline( SwDoc* pDoc, const SwTableLine& rTableLine, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
210 {
211     bool bChg = false;
212 
213     if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
214     {
215         // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
216         /*
217         SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
218         if( pUndo->GetRedlSaveCount() )
219         {
220             GetIDocumentUndoRedo().AppendUndo(pUndo);
221         }
222         else
223             delete pUndo;
224         */
225     }
226 
227     for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
228     {
229         SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
230         const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
231         const SwTableLine *pRedTabLine = pTableRowRedline ? &pTableRowRedline->GetTableLine() : nullptr;
232         if ( pRedTabLine == &rTableLine )
233         {
234             // Redline for this table row
235             const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
236             const RedlineType nRedlineType = aRedlineData.GetType();
237 
238             // Check if this redline object type should be deleted
239             if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
240                 continue;
241 
242             DeleteAndDestroy( nCurRedlinePos );
243             bChg = true;
244         }
245     }
246 
247     if( bChg )
248         pDoc->getIDocumentState().SetModified();
249 
250     return bChg;
251 }
252 
DeleteTableCellRedline(SwDoc * pDoc,const SwTableBox & rTableBox,bool bSaveInUndo,RedlineType nRedlineTypeToDelete)253 bool SwExtraRedlineTable::DeleteTableCellRedline( SwDoc* pDoc, const SwTableBox& rTableBox, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
254 {
255     bool bChg = false;
256 
257     if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
258     {
259         // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
260         /*
261         SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
262         if( pUndo->GetRedlSaveCount() )
263         {
264             GetIDocumentUndoRedo().AppendUndo(pUndo);
265         }
266         else
267             delete pUndo;
268         */
269     }
270 
271     for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
272     {
273         SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
274         const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
275         const SwTableBox *pRedTabBox = pTableCellRedline ? &pTableCellRedline->GetTableBox() : nullptr;
276         if ( pRedTabBox == &rTableBox )
277         {
278             // Redline for this table cell
279             const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
280             const RedlineType nRedlineType = aRedlineData.GetType();
281 
282             // Check if this redline object type should be deleted
283             if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
284                 continue;
285 
286             DeleteAndDestroy( nCurRedlinePos );
287             bChg = true;
288         }
289     }
290 
291     if( bChg )
292         pDoc->getIDocumentState().SetModified();
293 
294     return bChg;
295 }
296 
297 namespace
298 {
299 
lcl_LOKInvalidateFrames(const sw::BroadcastingModify & rMod,const SwRootFrame * pLayout,SwFrameType const nFrameType,const Point * pPoint)300 void lcl_LOKInvalidateFrames(const sw::BroadcastingModify& rMod, const SwRootFrame* pLayout,
301         SwFrameType const nFrameType, const Point* pPoint)
302 {
303     SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
304 
305     for (SwFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
306     {
307         if ((pTmpFrame->GetType() & nFrameType) &&
308             (!pLayout || pLayout == pTmpFrame->getRootFrame()) &&
309             (!pTmpFrame->IsFlowFrame() || !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow()))
310         {
311             if (pPoint)
312             {
313                 pTmpFrame->InvalidateSize();
314             }
315         }
316     }
317 }
318 
lcl_LOKInvalidateStartEndFrames(SwShellCursor & rCursor)319 void lcl_LOKInvalidateStartEndFrames(SwShellCursor& rCursor)
320 {
321     if (!(rCursor.HasMark() &&
322         rCursor.GetPoint()->nNode.GetNode().IsContentNode() &&
323         rCursor.GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout()) &&
324         (rCursor.GetMark()->nNode == rCursor.GetPoint()->nNode ||
325         (rCursor.GetMark()->nNode.GetNode().IsContentNode() &&
326          rCursor.GetMark()->nNode.GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout())))))
327     {
328         return;
329     }
330 
331 
332     SwPosition *pStartPos = rCursor.Start(),
333                *pEndPos   = rCursor.GetPoint() == pStartPos ? rCursor.GetMark() : rCursor.GetPoint();
334 
335 
336     lcl_LOKInvalidateFrames(*(pStartPos->nNode.GetNode().GetContentNode()),
337                             rCursor.GetShell()->GetLayout(),
338                             FRM_CNTNT, &rCursor.GetSttPos());
339 
340     lcl_LOKInvalidateFrames(*(pEndPos->nNode.GetNode().GetContentNode()),
341                             rCursor.GetShell()->GetLayout(),
342                             FRM_CNTNT, &rCursor.GetEndPos());
343 }
344 
lcl_LOKRedlineNotificationEnabled()345 bool lcl_LOKRedlineNotificationEnabled()
346 {
347     static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr;
348     if (comphelper::LibreOfficeKit::isActive() && !bDisableRedlineComments)
349         return true;
350 
351     return false;
352 }
353 
354 } // anonymous namespace
355 
356 /// Emits LOK notification about one addition / removal of a redline item.
LOKRedlineNotification(RedlineNotification nType,SwRangeRedline * pRedline)357 void SwRedlineTable::LOKRedlineNotification(RedlineNotification nType, SwRangeRedline* pRedline)
358 {
359     // Disable since usability is very low beyond some small number of changes.
360     if (!lcl_LOKRedlineNotificationEnabled())
361         return;
362 
363     boost::property_tree::ptree aRedline;
364     aRedline.put("action", (nType == RedlineNotification::Add ? "Add" :
365                             (nType == RedlineNotification::Remove ? "Remove" :
366                              (nType == RedlineNotification::Modify ? "Modify" : "???"))));
367     aRedline.put("index", pRedline->GetId());
368     aRedline.put("author", pRedline->GetAuthorString(1).toUtf8().getStr());
369     aRedline.put("type", SwRedlineTypeToOUString(pRedline->GetRedlineData().GetType()).toUtf8().getStr());
370     aRedline.put("comment", pRedline->GetRedlineData().GetComment().toUtf8().getStr());
371     aRedline.put("description", pRedline->GetDescr().toUtf8().getStr());
372     OUString sDateTime = utl::toISO8601(pRedline->GetRedlineData().GetTimeStamp().GetUNODateTime());
373     aRedline.put("dateTime", sDateTime.toUtf8().getStr());
374 
375     SwPosition* pStartPos = pRedline->Start();
376     SwPosition* pEndPos = pRedline->End();
377     SwContentNode* pContentNd = pRedline->GetContentNode();
378     SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
379     if (pView && pContentNd)
380     {
381         SwShellCursor aCursor(pView->GetWrtShell(), *pStartPos);
382         aCursor.SetMark();
383         aCursor.GetMark()->nNode = pEndPos->nNode;
384         aCursor.GetMark()->nContent = pEndPos->nContent;
385 
386         aCursor.FillRects();
387 
388         SwRects* pRects(&aCursor);
389         std::vector<OString> aRects;
390         for(const SwRect& rNextRect : *pRects)
391             aRects.push_back(rNextRect.SVRect().toString());
392 
393         const OString sRects = comphelper::string::join("; ", aRects);
394         aRedline.put("textRange", sRects.getStr());
395 
396         lcl_LOKInvalidateStartEndFrames(aCursor);
397 
398         // When this notify method is called text invalidation is not done yet
399         // Calling FillRects updates the text area so invalidation will not run on the correct rects
400         // So we need to do an own invalidation here. It invalidates text frames containing the redlining
401         SwDoc& rDoc = pRedline->GetDoc();
402         SwViewShell* pSh;
403         if( !rDoc.IsInDtor() )
404         {
405             pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
406             if( pSh )
407                 for(SwNodeIndex nIdx = pStartPos->nNode; nIdx <= pEndPos->nNode; ++nIdx)
408                 {
409                     SwContentNode* pContentNode = nIdx.GetNode().GetContentNode();
410                     if (pContentNode)
411                         pSh->InvalidateWindows(pContentNode->FindLayoutRect());
412                 }
413         }
414     }
415 
416     boost::property_tree::ptree aTree;
417     aTree.add_child("redline", aRedline);
418     std::stringstream aStream;
419     boost::property_tree::write_json(aStream, aTree);
420     std::string aPayload = aStream.str();
421 
422     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
423     while (pViewShell)
424     {
425         if (pView && pView->GetDocId() == pViewShell->GetDocId())
426             pViewShell->libreOfficeKitViewCallback(nType == RedlineNotification::Modify ? LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED : LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED, aPayload.c_str());
427         pViewShell = SfxViewShell::GetNext(*pViewShell);
428     }
429 }
430 
Insert(SwRangeRedline * & p)431 bool SwRedlineTable::Insert(SwRangeRedline*& p)
432 {
433     if( p->HasValidRange() )
434     {
435         std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
436         size_type nP = rv.first - begin();
437         LOKRedlineNotification(RedlineNotification::Add, p);
438         p->CallDisplayFunc(nP);
439         return rv.second;
440     }
441     return InsertWithValidRanges( p );
442 }
443 
Insert(SwRangeRedline * & p,size_type & rP)444 bool SwRedlineTable::Insert(SwRangeRedline*& p, size_type& rP)
445 {
446     if( p->HasValidRange() )
447     {
448         std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
449         rP = rv.first - begin();
450         p->CallDisplayFunc(rP);
451         return rv.second;
452     }
453     return InsertWithValidRanges( p, &rP );
454 }
455 
456 namespace sw {
457 
GetAllValidRanges(std::unique_ptr<SwRangeRedline> p)458 std::vector<SwRangeRedline*> GetAllValidRanges(std::unique_ptr<SwRangeRedline> p)
459 {
460     std::vector<SwRangeRedline*> ret;
461     // Create valid "sub-ranges" from the Selection
462     SwPosition* pStt = p->Start(),
463               * pEnd = pStt == p->GetPoint() ? p->GetMark() : p->GetPoint();
464     SwPosition aNewStt( *pStt );
465     SwNodes& rNds = aNewStt.nNode.GetNodes();
466     SwContentNode* pC;
467 
468     if( !aNewStt.nNode.GetNode().IsContentNode() )
469     {
470         pC = rNds.GoNext( &aNewStt.nNode );
471         if( pC )
472             aNewStt.nContent.Assign( pC, 0 );
473         else
474             aNewStt.nNode = rNds.GetEndOfContent();
475     }
476 
477     SwRangeRedline* pNew = nullptr;
478 
479     if( aNewStt < *pEnd )
480         do {
481             if( !pNew )
482                 pNew = new SwRangeRedline( p->GetRedlineData(), aNewStt );
483             else
484             {
485                 pNew->DeleteMark();
486                 *pNew->GetPoint() = aNewStt;
487             }
488 
489             pNew->SetMark();
490             GoEndSection( pNew->GetPoint() );
491             // i60396: If the redlines starts before a table but the table is the last member
492             // of the section, the GoEndSection will end inside the table.
493             // This will result in an incorrect redline, so we've to go back
494             SwNode* pTab = pNew->GetPoint()->nNode.GetNode().StartOfSectionNode()->FindTableNode();
495             // We end in a table when pTab != 0
496             if( pTab && !pNew->GetMark()->nNode.GetNode().StartOfSectionNode()->FindTableNode() )
497             { // but our Mark was outside the table => Correction
498                 do
499                 {
500                     // We want to be before the table
501                     *pNew->GetPoint() = SwPosition(*pTab);
502                     pC = GoPreviousNds( &pNew->GetPoint()->nNode, false ); // here we are.
503                     if( pC )
504                         pNew->GetPoint()->nContent.Assign( pC, 0 );
505                     pTab = pNew->GetPoint()->nNode.GetNode().StartOfSectionNode()->FindTableNode();
506                 } while( pTab ); // If there is another table we have to repeat our step backwards
507             }
508 
509             if( *pNew->GetPoint() > *pEnd )
510             {
511                 pC = nullptr;
512                 if( aNewStt.nNode != pEnd->nNode )
513                     do {
514                         SwNode& rCurNd = aNewStt.nNode.GetNode();
515                         if( rCurNd.IsStartNode() )
516                         {
517                             if( rCurNd.EndOfSectionIndex() < pEnd->nNode.GetIndex() )
518                                 aNewStt.nNode = *rCurNd.EndOfSectionNode();
519                             else
520                                 break;
521                         }
522                         else if( rCurNd.IsContentNode() )
523                             pC = rCurNd.GetContentNode();
524                         ++aNewStt.nNode;
525                     } while( aNewStt.nNode.GetIndex() < pEnd->nNode.GetIndex() );
526 
527                 if( aNewStt.nNode == pEnd->nNode )
528                     aNewStt.nContent = pEnd->nContent;
529                 else if( pC )
530                 {
531                     aNewStt.nNode = *pC;
532                     aNewStt.nContent.Assign( pC, pC->Len() );
533                 }
534 
535                 if( aNewStt <= *pEnd )
536                     *pNew->GetPoint() = aNewStt;
537             }
538             else
539                 aNewStt = *pNew->GetPoint();
540 #if OSL_DEBUG_LEVEL > 0
541             CheckPosition( pNew->GetPoint(), pNew->GetMark() );
542 #endif
543 
544             if( *pNew->GetPoint() != *pNew->GetMark() &&
545                 pNew->HasValidRange())
546             {
547                 ret.push_back(pNew);
548                 pNew = nullptr;
549             }
550 
551             if( aNewStt >= *pEnd )
552                 break;
553             pC = rNds.GoNext( &aNewStt.nNode );
554             if( !pC )
555                 break;
556 
557             aNewStt.nContent.Assign( pC, 0 );
558 
559         } while( aNewStt < *pEnd );
560 
561     delete pNew;
562     p.reset();
563     return ret;
564 }
565 
566 } // namespace sw
567 
InsertWithValidRanges(SwRangeRedline * & p,size_type * pInsPos)568 bool SwRedlineTable::InsertWithValidRanges(SwRangeRedline*& p, size_type* pInsPos)
569 {
570     bool bAnyIns = false;
571     std::vector<SwRangeRedline*> const redlines(
572             GetAllValidRanges(std::unique_ptr<SwRangeRedline>(p)));
573     for (SwRangeRedline * pRedline : redlines)
574     {
575         assert(pRedline->HasValidRange());
576         size_type nInsPos;
577         if (Insert(pRedline, nInsPos))
578         {
579             pRedline->CallDisplayFunc(nInsPos);
580             bAnyIns = true;
581             if (pInsPos && *pInsPos < nInsPos)
582             {
583                 *pInsPos = nInsPos;
584             }
585         }
586     }
587     p = nullptr;
588     return bAnyIns;
589 }
590 
operator ()(SwRangeRedline * const & lhs,SwRangeRedline * const & rhs) const591 bool CompareSwRedlineTable::operator()(SwRangeRedline* const &lhs, SwRangeRedline* const &rhs) const
592 {
593     return *lhs < *rhs;
594 }
595 
~SwRedlineTable()596 SwRedlineTable::~SwRedlineTable()
597 {
598    maVector.DeleteAndDestroyAll();
599 }
600 
GetPos(const SwRangeRedline * p) const601 SwRedlineTable::size_type SwRedlineTable::GetPos(const SwRangeRedline* p) const
602 {
603     vector_type::const_iterator it = maVector.find(const_cast<SwRangeRedline*>(p));
604     if( it == maVector.end() )
605         return npos;
606     return it - maVector.begin();
607 }
608 
Remove(const SwRangeRedline * p)609 void SwRedlineTable::Remove( const SwRangeRedline* p )
610 {
611     const size_type nPos = GetPos(p);
612     if (nPos == npos)
613         return;
614     Remove(nPos);
615 }
616 
Remove(size_type nP)617 void SwRedlineTable::Remove( size_type nP )
618 {
619     LOKRedlineNotification(RedlineNotification::Remove, maVector[nP]);
620     SwDoc* pDoc = nullptr;
621     if( !nP && 1 == size() )
622         pDoc = &maVector.front()->GetDoc();
623 
624     maVector.erase( maVector.begin() + nP );
625 
626     if( pDoc && !pDoc->IsInDtor() )
627     {
628         SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
629         if( pSh )
630             pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
631     }
632 }
633 
DeleteAndDestroyAll()634 void SwRedlineTable::DeleteAndDestroyAll()
635 {
636     while (!maVector.empty())
637     {
638         auto const pRedline = maVector.back();
639         maVector.erase_at(maVector.size() - 1);
640         LOKRedlineNotification(RedlineNotification::Remove, pRedline);
641         delete pRedline;
642     }
643 }
644 
DeleteAndDestroy(size_type const nP)645 void SwRedlineTable::DeleteAndDestroy(size_type const nP)
646 {
647     auto const pRedline = maVector[nP];
648     maVector.erase(maVector.begin() + nP);
649     LOKRedlineNotification(RedlineNotification::Remove, pRedline);
650     delete pRedline;
651 }
652 
FindNextOfSeqNo(size_type nSttPos) const653 SwRedlineTable::size_type SwRedlineTable::FindNextOfSeqNo( size_type nSttPos ) const
654 {
655     return nSttPos + 1 < size()
656                 ? FindNextSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos+1 )
657                 : npos;
658 }
659 
FindPrevOfSeqNo(size_type nSttPos) const660 SwRedlineTable::size_type SwRedlineTable::FindPrevOfSeqNo( size_type nSttPos ) const
661 {
662     return nSttPos ? FindPrevSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos-1 )
663                    : npos;
664 }
665 
666 /// Find the next or preceding Redline with the same seq.no.
667 /// We can limit the search using look ahead (0 searches the whole array).
FindNextSeqNo(sal_uInt16 nSeqNo,size_type nSttPos) const668 SwRedlineTable::size_type SwRedlineTable::FindNextSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
669 {
670     auto constexpr nLookahead = 20;
671     size_type nRet = npos;
672     if( nSeqNo && nSttPos < size() )
673     {
674         size_type nEnd = size();
675         const size_type nTmp = nSttPos + nLookahead;
676         if (nTmp < nEnd)
677         {
678             nEnd = nTmp;
679         }
680 
681         for( ; nSttPos < nEnd; ++nSttPos )
682             if( nSeqNo == operator[]( nSttPos )->GetSeqNo() )
683             {
684                 nRet = nSttPos;
685                 break;
686             }
687     }
688     return nRet;
689 }
690 
FindPrevSeqNo(sal_uInt16 nSeqNo,size_type nSttPos) const691 SwRedlineTable::size_type SwRedlineTable::FindPrevSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
692 {
693     auto constexpr nLookahead = 20;
694     size_type nRet = npos;
695     if( nSeqNo && nSttPos < size() )
696     {
697         size_type nEnd = 0;
698         if( nSttPos > nLookahead )
699             nEnd = nSttPos - nLookahead;
700 
701         ++nSttPos;
702         while( nSttPos > nEnd )
703             if( nSeqNo == operator[]( --nSttPos )->GetSeqNo() )
704             {
705                 nRet = nSttPos;
706                 break;
707             }
708     }
709     return nRet;
710 }
711 
FindAtPosition(const SwPosition & rSttPos,size_type & rPos,bool bNext) const712 const SwRangeRedline* SwRedlineTable::FindAtPosition( const SwPosition& rSttPos,
713                                         size_type& rPos,
714                                         bool bNext ) const
715 {
716     const SwRangeRedline* pFnd = nullptr;
717     for( ; rPos < maVector.size() ; ++rPos )
718     {
719         const SwRangeRedline* pTmp = (*this)[ rPos ];
720         if( pTmp->HasMark() && pTmp->IsVisible() )
721         {
722             const SwPosition* pRStt = pTmp->Start(),
723                       * pREnd = pRStt == pTmp->GetPoint() ? pTmp->GetMark()
724                                                           : pTmp->GetPoint();
725             if( bNext ? *pRStt <= rSttPos : *pRStt < rSttPos )
726             {
727                 if( bNext ? *pREnd > rSttPos : *pREnd >= rSttPos )
728                 {
729                     pFnd = pTmp;
730                     break;
731                 }
732             }
733             else
734                 break;
735         }
736     }
737     return pFnd;
738 }
739 
dumpAsXml(xmlTextWriterPtr pWriter) const740 void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
741 {
742     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable"));
743     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
744 
745     for (SwRedlineTable::size_type nCurRedlinePos = 0; nCurRedlinePos < size(); ++nCurRedlinePos)
746         operator[](nCurRedlinePos)->dumpAsXml(pWriter);
747 
748     (void)xmlTextWriterEndElement(pWriter);
749 }
750 
~SwRedlineExtraData()751 SwRedlineExtraData::~SwRedlineExtraData()
752 {
753 }
754 
Reject(SwPaM &) const755 void SwRedlineExtraData::Reject( SwPaM& ) const
756 {
757 }
758 
operator ==(const SwRedlineExtraData &) const759 bool SwRedlineExtraData::operator == ( const SwRedlineExtraData& ) const
760 {
761     return false;
762 }
763 
SwRedlineExtraData_FormatColl(const OUString & rColl,sal_uInt16 nPoolFormatId,const SfxItemSet * pItemSet,bool bFormatAll)764 SwRedlineExtraData_FormatColl::SwRedlineExtraData_FormatColl( const OUString& rColl,
765                                                 sal_uInt16 nPoolFormatId,
766                                                 const SfxItemSet* pItemSet,
767                                                 bool bFormatAll )
768     : m_sFormatNm(rColl), m_nPoolId(nPoolFormatId), m_bFormatAll(bFormatAll)
769 {
770     if( pItemSet && pItemSet->Count() )
771         m_pSet.reset( new SfxItemSet( *pItemSet ) );
772 }
773 
~SwRedlineExtraData_FormatColl()774 SwRedlineExtraData_FormatColl::~SwRedlineExtraData_FormatColl()
775 {
776 }
777 
CreateNew() const778 SwRedlineExtraData* SwRedlineExtraData_FormatColl::CreateNew() const
779 {
780     return new SwRedlineExtraData_FormatColl( m_sFormatNm, m_nPoolId, m_pSet.get(), m_bFormatAll );
781 }
782 
Reject(SwPaM & rPam) const783 void SwRedlineExtraData_FormatColl::Reject( SwPaM& rPam ) const
784 {
785     SwDoc& rDoc = rPam.GetDoc();
786 
787     // What about Undo? Is it turned off?
788     SwTextFormatColl* pColl = USHRT_MAX == m_nPoolId
789                             ? rDoc.FindTextFormatCollByName( m_sFormatNm )
790                             : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId );
791 
792     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
793     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
794 
795     SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
796 
797     const SwPosition* pStt = rPam.Start(),
798                     * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
799                                                      : rPam.GetPoint();
800 
801     if ( !m_bFormatAll || pEnd->nContent == 0 )
802     {
803         // don't reject the format of the next paragraph (that is handled by the next redline)
804         if (aPam.GetPoint()->nNode > aPam.GetMark()->nNode)
805         {
806             aPam.GetPoint()->nNode--;
807             SwContentNode* pNode = aPam.GetPoint()->nNode.GetNode().GetContentNode();
808             aPam.GetPoint()->nContent.Assign( pNode, pNode->Len() );
809         }
810         else if (aPam.GetPoint()->nNode < aPam.GetMark()->nNode)
811         {
812             aPam.GetMark()->nNode--;
813             SwContentNode* pNode = aPam.GetMark()->nNode.GetNode().GetContentNode();
814             aPam.GetMark()->nContent.Assign( pNode, pNode->Len() );
815         }
816     }
817 
818     if( pColl )
819         rDoc.SetTextFormatColl( aPam, pColl, false );
820 
821     if( m_pSet )
822         rDoc.getIDocumentContentOperations().InsertItemSet( aPam, *m_pSet );
823 
824     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
825 }
826 
operator ==(const SwRedlineExtraData & r) const827 bool SwRedlineExtraData_FormatColl::operator == ( const SwRedlineExtraData& r) const
828 {
829     const SwRedlineExtraData_FormatColl& rCmp = static_cast<const SwRedlineExtraData_FormatColl&>(r);
830     return m_sFormatNm == rCmp.m_sFormatNm && m_nPoolId == rCmp.m_nPoolId &&
831             m_bFormatAll == rCmp.m_bFormatAll &&
832             ( ( !m_pSet && !rCmp.m_pSet ) ||
833                ( m_pSet && rCmp.m_pSet && *m_pSet == *rCmp.m_pSet ) );
834 }
835 
SetItemSet(const SfxItemSet & rSet)836 void SwRedlineExtraData_FormatColl::SetItemSet( const SfxItemSet& rSet )
837 {
838     if( rSet.Count() )
839         m_pSet.reset( new SfxItemSet( rSet ) );
840     else
841         m_pSet.reset();
842 }
843 
SwRedlineExtraData_Format(const SfxItemSet & rSet)844 SwRedlineExtraData_Format::SwRedlineExtraData_Format( const SfxItemSet& rSet )
845 {
846     SfxItemIter aIter( rSet );
847     for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
848     {
849         m_aWhichIds.push_back( pItem->Which() );
850     }
851 }
852 
SwRedlineExtraData_Format(const SwRedlineExtraData_Format & rCpy)853 SwRedlineExtraData_Format::SwRedlineExtraData_Format(
854         const SwRedlineExtraData_Format& rCpy )
855     : SwRedlineExtraData()
856 {
857     m_aWhichIds.insert( m_aWhichIds.begin(), rCpy.m_aWhichIds.begin(), rCpy.m_aWhichIds.end() );
858 }
859 
~SwRedlineExtraData_Format()860 SwRedlineExtraData_Format::~SwRedlineExtraData_Format()
861 {
862 }
863 
CreateNew() const864 SwRedlineExtraData* SwRedlineExtraData_Format::CreateNew() const
865 {
866     return new SwRedlineExtraData_Format( *this );
867 }
868 
Reject(SwPaM & rPam) const869 void SwRedlineExtraData_Format::Reject( SwPaM& rPam ) const
870 {
871     SwDoc& rDoc = rPam.GetDoc();
872 
873     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
874     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
875 
876     // Actually we need to reset the Attribute here!
877     for( const auto& rWhichId : m_aWhichIds )
878     {
879         rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, *GetDfltAttr( rWhichId ),
880             SetAttrMode::DONTEXPAND );
881     }
882 
883     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
884 }
885 
operator ==(const SwRedlineExtraData & rCmp) const886 bool SwRedlineExtraData_Format::operator == ( const SwRedlineExtraData& rCmp ) const
887 {
888     const size_t nEnd = m_aWhichIds.size();
889     if( nEnd != static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds.size() )
890         return false;
891 
892     for( size_t n = 0; n < nEnd; ++n )
893     {
894         if( static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds[n] != m_aWhichIds[n])
895         {
896             return false;
897         }
898     }
899     return true;
900 }
901 
SwRedlineData(RedlineType eT,std::size_t nAut)902 SwRedlineData::SwRedlineData( RedlineType eT, std::size_t nAut )
903     : m_pNext( nullptr ), m_pExtraData( nullptr ),
904     m_aStamp( DateTime::SYSTEM ),
905     m_nAuthor( nAut ), m_eType( eT ), m_nSeqNo( 0 ), m_bAutoFormat(false)
906 {
907     m_aStamp.SetNanoSec( 0 );
908 }
909 
SwRedlineData(const SwRedlineData & rCpy,bool bCpyNext)910 SwRedlineData::SwRedlineData(
911     const SwRedlineData& rCpy,
912     bool bCpyNext )
913     : m_pNext( ( bCpyNext && rCpy.m_pNext ) ? new SwRedlineData( *rCpy.m_pNext ) : nullptr )
914     , m_pExtraData( rCpy.m_pExtraData ? rCpy.m_pExtraData->CreateNew() : nullptr )
915     , m_sComment( rCpy.m_sComment )
916     , m_aStamp( rCpy.m_aStamp )
917     , m_nAuthor( rCpy.m_nAuthor )
918     , m_eType( rCpy.m_eType )
919     , m_nSeqNo( rCpy.m_nSeqNo )
920     , m_bAutoFormat(false)
921 {
922 }
923 
924 // For sw3io: We now own pNext!
SwRedlineData(RedlineType eT,std::size_t nAut,const DateTime & rDT,const OUString & rCmnt,SwRedlineData * pNxt)925 SwRedlineData::SwRedlineData(RedlineType eT, std::size_t nAut, const DateTime& rDT,
926     const OUString& rCmnt, SwRedlineData *pNxt)
927     : m_pNext(pNxt), m_pExtraData(nullptr), m_sComment(rCmnt), m_aStamp(rDT),
928     m_nAuthor(nAut), m_eType(eT), m_nSeqNo(0), m_bAutoFormat(false)
929 {
930 }
931 
~SwRedlineData()932 SwRedlineData::~SwRedlineData()
933 {
934     delete m_pExtraData;
935     delete m_pNext;
936 }
937 
CanCombine(const SwRedlineData & rCmp) const938 bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const
939 {
940     DateTime aTime = GetTimeStamp();
941     aTime.SetSec(0);
942     DateTime aCompareTime = rCmp.GetTimeStamp();
943     aCompareTime.SetSec(0);
944     return m_nAuthor == rCmp.m_nAuthor &&
945             m_eType == rCmp.m_eType &&
946             m_sComment == rCmp.m_sComment &&
947             aTime == aCompareTime &&
948             (( !m_pNext && !rCmp.m_pNext ) ||
949                 ( m_pNext && rCmp.m_pNext &&
950                 m_pNext->CanCombine( *rCmp.m_pNext ))) &&
951             (( !m_pExtraData && !rCmp.m_pExtraData ) ||
952                 ( m_pExtraData && rCmp.m_pExtraData &&
953                     *m_pExtraData == *rCmp.m_pExtraData ));
954 }
955 
956 /// ExtraData is copied. The Pointer's ownership is thus NOT transferred
957 /// to the Redline Object!
SetExtraData(const SwRedlineExtraData * pData)958 void SwRedlineData::SetExtraData( const SwRedlineExtraData* pData )
959 {
960     delete m_pExtraData;
961 
962     // Check if there is data - and if so - delete it
963     if( pData )
964         m_pExtraData = pData->CreateNew();
965     else
966         m_pExtraData = nullptr;
967 }
968 
969 static const char* STR_REDLINE_ARY[] =
970 {
971     STR_UNDO_REDLINE_INSERT,
972     STR_UNDO_REDLINE_DELETE,
973     STR_UNDO_REDLINE_FORMAT,
974     STR_UNDO_REDLINE_TABLE,
975     STR_UNDO_REDLINE_FMTCOLL,
976     STR_UNDO_REDLINE_PARAGRAPH_FORMAT,
977     STR_UNDO_REDLINE_TABLE_ROW_INSERT,
978     STR_UNDO_REDLINE_TABLE_ROW_DELETE,
979     STR_UNDO_REDLINE_TABLE_CELL_INSERT,
980     STR_UNDO_REDLINE_TABLE_CELL_DELETE
981 };
982 
GetDescr() const983 OUString SwRedlineData::GetDescr() const
984 {
985     return SwResId(STR_REDLINE_ARY[static_cast<int>(GetType())]);
986 }
987 
988 sal_uInt32 SwRangeRedline::s_nLastId = 1;
989 
SwRangeRedline(RedlineType eTyp,const SwPaM & rPam)990 SwRangeRedline::SwRangeRedline(RedlineType eTyp, const SwPaM& rPam )
991     : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
992     m_pRedlineData( new SwRedlineData( eTyp, GetDoc().getIDocumentRedlineAccess().GetRedlineAuthor() ) ),
993     m_pContentSect( nullptr ),
994     m_nId( s_nLastId++ )
995 {
996     m_bDelLastPara = false;
997     m_bIsVisible = true;
998     if( !rPam.HasMark() )
999         DeleteMark();
1000 }
1001 
SwRangeRedline(const SwRedlineData & rData,const SwPaM & rPam)1002 SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPaM& rPam )
1003     : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
1004     m_pRedlineData( new SwRedlineData( rData )),
1005     m_pContentSect( nullptr ),
1006     m_nId( s_nLastId++ )
1007 {
1008     m_bDelLastPara = false;
1009     m_bIsVisible = true;
1010     if( !rPam.HasMark() )
1011         DeleteMark();
1012 }
1013 
SwRangeRedline(const SwRedlineData & rData,const SwPosition & rPos)1014 SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPosition& rPos )
1015     : SwPaM( rPos ),
1016     m_pRedlineData( new SwRedlineData( rData )),
1017     m_pContentSect( nullptr ),
1018     m_nId( s_nLastId++ )
1019 {
1020     m_bDelLastPara = false;
1021     m_bIsVisible = true;
1022 }
1023 
SwRangeRedline(const SwRangeRedline & rCpy)1024 SwRangeRedline::SwRangeRedline( const SwRangeRedline& rCpy )
1025     : SwPaM( *rCpy.GetMark(), *rCpy.GetPoint() ),
1026     m_pRedlineData( new SwRedlineData( *rCpy.m_pRedlineData )),
1027     m_pContentSect( nullptr ),
1028     m_nId( s_nLastId++ )
1029 {
1030     m_bDelLastPara = false;
1031     m_bIsVisible = true;
1032     if( !rCpy.HasMark() )
1033         DeleteMark();
1034 }
1035 
~SwRangeRedline()1036 SwRangeRedline::~SwRangeRedline()
1037 {
1038     if( m_pContentSect )
1039     {
1040         // delete the ContentSection
1041         if( !GetDoc().IsInDtor() )
1042             GetDoc().getIDocumentContentOperations().DeleteSection( &m_pContentSect->GetNode() );
1043         delete m_pContentSect;
1044     }
1045     delete m_pRedlineData;
1046 }
1047 
MaybeNotifyRedlineModification(SwRangeRedline & rRedline,SwDoc & rDoc)1048 void MaybeNotifyRedlineModification(SwRangeRedline& rRedline, SwDoc& rDoc)
1049 {
1050     if (!lcl_LOKRedlineNotificationEnabled())
1051         return;
1052 
1053     const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1054     for (SwRedlineTable::size_type i = 0; i < rRedTable.size(); ++i)
1055     {
1056         if (rRedTable[i] == &rRedline)
1057         {
1058             SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, &rRedline);
1059             break;
1060         }
1061     }
1062 }
1063 
MaybeNotifyRedlinePositionModification(tools::Long nTop)1064 void SwRangeRedline::MaybeNotifyRedlinePositionModification(tools::Long nTop)
1065 {
1066     if (!lcl_LOKRedlineNotificationEnabled())
1067         return;
1068 
1069     if(!m_oLOKLastNodeTop || *m_oLOKLastNodeTop != nTop)
1070     {
1071         m_oLOKLastNodeTop = nTop;
1072         SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, this);
1073     }
1074 }
1075 
SetStart(const SwPosition & rPos,SwPosition * pSttPtr)1076 void SwRangeRedline::SetStart( const SwPosition& rPos, SwPosition* pSttPtr )
1077 {
1078     if( !pSttPtr ) pSttPtr = Start();
1079     *pSttPtr = rPos;
1080 
1081     MaybeNotifyRedlineModification(*this, GetDoc());
1082 }
1083 
SetEnd(const SwPosition & rPos,SwPosition * pEndPtr)1084 void SwRangeRedline::SetEnd( const SwPosition& rPos, SwPosition* pEndPtr )
1085 {
1086     if( !pEndPtr ) pEndPtr = End();
1087     *pEndPtr = rPos;
1088 
1089     MaybeNotifyRedlineModification(*this, GetDoc());
1090 }
1091 
1092 /// Do we have a valid Selection?
HasValidRange() const1093 bool SwRangeRedline::HasValidRange() const
1094 {
1095     const SwNode* pPtNd = &GetPoint()->nNode.GetNode(),
1096                 * pMkNd = &GetMark()->nNode.GetNode();
1097     if( pPtNd->StartOfSectionNode() == pMkNd->StartOfSectionNode() &&
1098         !pPtNd->StartOfSectionNode()->IsTableNode() &&
1099         // invalid if points on the end of content
1100         // end-of-content only invalid if no content index exists
1101         ( pPtNd != pMkNd || GetContentIdx() != nullptr ||
1102           pPtNd != &pPtNd->GetNodes().GetEndOfContent() )
1103         )
1104         return true;
1105     return false;
1106 }
1107 
CallDisplayFunc(size_t nMyPos)1108 void SwRangeRedline::CallDisplayFunc(size_t nMyPos)
1109 {
1110     RedlineFlags eShow = RedlineFlags::ShowMask & GetDoc().getIDocumentRedlineAccess().GetRedlineFlags();
1111     if (eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1112         Show(0, nMyPos);
1113     else if (eShow == RedlineFlags::ShowInsert)
1114         Hide(0, nMyPos);
1115     else if (eShow == RedlineFlags::ShowDelete)
1116         ShowOriginal(0, nMyPos);
1117 }
1118 
Show(sal_uInt16 nLoop,size_t nMyPos,bool bForced)1119 void SwRangeRedline::Show(sal_uInt16 nLoop, size_t nMyPos, bool bForced)
1120 {
1121     SwDoc& rDoc = GetDoc();
1122 
1123     bool bIsShowChangesInMargin = false;
1124     if ( !bForced )
1125     {
1126         SwViewShell* pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
1127         if (pSh)
1128             bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
1129         else
1130             bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
1131     }
1132 
1133     if( 1 > nLoop && !bIsShowChangesInMargin )
1134         return;
1135 
1136     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1137     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1138     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1139 
1140     switch( GetType() )
1141     {
1142     case RedlineType::Insert:           // Content has been inserted
1143         m_bIsVisible = true;
1144         MoveFromSection(nMyPos);
1145         break;
1146 
1147     case RedlineType::Delete:           // Content has been deleted
1148         m_bIsVisible = !bIsShowChangesInMargin;
1149 
1150         if (m_bIsVisible)
1151             MoveFromSection(nMyPos);
1152         else
1153         {
1154             switch( nLoop )
1155             {
1156             case 0: MoveToSection();    break;
1157             case 1: CopyToSection();    break;
1158             case 2: DelCopyOfSection(nMyPos); break;
1159             }
1160         }
1161         break;
1162 
1163     case RedlineType::Format:           // Attributes have been applied
1164     case RedlineType::Table:            // Table structure has been modified
1165         InvalidateRange(Invalidation::Add);
1166         break;
1167     default:
1168         break;
1169     }
1170     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1171 }
1172 
Hide(sal_uInt16 nLoop,size_t nMyPos,bool)1173 void SwRangeRedline::Hide(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
1174 {
1175     SwDoc& rDoc = GetDoc();
1176     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1177     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1178     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1179 
1180     switch( GetType() )
1181     {
1182     case RedlineType::Insert:           // Content has been inserted
1183         m_bIsVisible = true;
1184         if( 1 <= nLoop )
1185             MoveFromSection(nMyPos);
1186         break;
1187 
1188     case RedlineType::Delete:           // Content has been deleted
1189         m_bIsVisible = false;
1190         switch( nLoop )
1191         {
1192         case 0: MoveToSection();    break;
1193         case 1: CopyToSection();    break;
1194         case 2: DelCopyOfSection(nMyPos); break;
1195         }
1196         break;
1197 
1198     case RedlineType::Format:           // Attributes have been applied
1199     case RedlineType::Table:            // Table structure has been modified
1200         if( 1 <= nLoop )
1201             InvalidateRange(Invalidation::Remove);
1202         break;
1203     default:
1204         break;
1205     }
1206     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1207 }
1208 
ShowOriginal(sal_uInt16 nLoop,size_t nMyPos,bool)1209 void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
1210 {
1211     SwDoc& rDoc = GetDoc();
1212     RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1213     SwRedlineData* pCur;
1214 
1215     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1216     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1217 
1218     // Determine the Type, it's the first on Stack
1219     for( pCur = m_pRedlineData; pCur->m_pNext; )
1220         pCur = pCur->m_pNext;
1221 
1222     switch( pCur->m_eType )
1223     {
1224     case RedlineType::Insert:           // Content has been inserted
1225         m_bIsVisible = false;
1226         switch( nLoop )
1227         {
1228         case 0: MoveToSection();    break;
1229         case 1: CopyToSection();    break;
1230         case 2: DelCopyOfSection(nMyPos); break;
1231         }
1232         break;
1233 
1234     case RedlineType::Delete:           // Content has been deleted
1235         m_bIsVisible = true;
1236         if( 1 <= nLoop )
1237             MoveFromSection(nMyPos);
1238         break;
1239 
1240     case RedlineType::Format:           // Attributes have been applied
1241     case RedlineType::Table:            // Table structure has been modified
1242         if( 1 <= nLoop )
1243             InvalidateRange(Invalidation::Remove);
1244         break;
1245     default:
1246         break;
1247     }
1248     rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1249 }
1250 
1251 // trigger the Layout
InvalidateRange(Invalidation const eWhy)1252 void SwRangeRedline::InvalidateRange(Invalidation const eWhy)
1253 {
1254     sal_uLong nSttNd = GetMark()->nNode.GetIndex(),
1255             nEndNd = GetPoint()->nNode.GetIndex();
1256     sal_Int32 nSttCnt = GetMark()->nContent.GetIndex();
1257     sal_Int32 nEndCnt = GetPoint()->nContent.GetIndex();
1258 
1259     if( nSttNd > nEndNd || ( nSttNd == nEndNd && nSttCnt > nEndCnt ))
1260     {
1261         sal_uLong nTmp = nSttNd; nSttNd = nEndNd; nEndNd = nTmp;
1262         sal_Int32 nTmp2 = nSttCnt; nSttCnt = nEndCnt; nEndCnt = nTmp2;
1263     }
1264 
1265     SwNodes& rNds = GetDoc().GetNodes();
1266     for (sal_uLong n(nSttNd); n <= nEndNd; ++n)
1267     {
1268         SwNode* pNode = rNds[n];
1269 
1270         if (pNode && pNode->IsTextNode())
1271         {
1272             SwTextNode* pNd = pNode->GetTextNode();
1273 
1274             SwUpdateAttr aHt(
1275                 n == nSttNd ? nSttCnt : 0,
1276                 n == nEndNd ? nEndCnt : pNd->GetText().getLength(),
1277                 RES_FMT_CHG);
1278 
1279             pNd->TriggerNodeUpdate(sw::LegacyModifyHint(&aHt, &aHt));
1280 
1281             // SwUpdateAttr must be handled first, otherwise indexes are off
1282             if (GetType() == RedlineType::Delete)
1283             {
1284                 sal_Int32 const nStart(n == nSttNd ? nSttCnt : 0);
1285                 sal_Int32 const nLen((n == nEndNd ? nEndCnt : pNd->GetText().getLength()) - nStart);
1286                 if (eWhy == Invalidation::Add)
1287                 {
1288                     sw::RedlineDelText const hint(nStart, nLen);
1289                     pNd->CallSwClientNotify(hint);
1290                 }
1291                 else
1292                 {
1293                     sw::RedlineUnDelText const hint(nStart, nLen);
1294                     pNd->CallSwClientNotify(hint);
1295                 }
1296             }
1297         }
1298     }
1299 }
1300 
1301 /** Calculates the start and end position of the intersection rTmp and
1302     text node nNdIdx */
CalcStartEnd(sal_uLong nNdIdx,sal_Int32 & rStart,sal_Int32 & rEnd) const1303 void SwRangeRedline::CalcStartEnd( sal_uLong nNdIdx, sal_Int32& rStart, sal_Int32& rEnd ) const
1304 {
1305     const SwPosition *pRStt = Start(), *pREnd = End();
1306     if( pRStt->nNode < nNdIdx )
1307     {
1308         if( pREnd->nNode > nNdIdx )
1309         {
1310             rStart = 0;             // Paragraph is completely enclosed
1311             rEnd = COMPLETE_STRING;
1312         }
1313         else if (pREnd->nNode == nNdIdx)
1314         {
1315             rStart = 0;             // Paragraph is overlapped in the beginning
1316             rEnd = pREnd->nContent.GetIndex();
1317         }
1318         else // redline ends before paragraph
1319         {
1320             rStart = COMPLETE_STRING;
1321             rEnd = COMPLETE_STRING;
1322         }
1323     }
1324     else if( pRStt->nNode == nNdIdx )
1325     {
1326         rStart = pRStt->nContent.GetIndex();
1327         if( pREnd->nNode == nNdIdx )
1328             rEnd = pREnd->nContent.GetIndex(); // Within the Paragraph
1329         else
1330             rEnd = COMPLETE_STRING;      // Paragraph is overlapped in the end
1331     }
1332     else
1333     {
1334         rStart = COMPLETE_STRING;
1335         rEnd = COMPLETE_STRING;
1336     }
1337 }
1338 
lcl_storeAnnotationMarks(SwDoc & rDoc,const SwPosition * pStt,const SwPosition * pEnd)1339 static void lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStt, const SwPosition* pEnd)
1340 {
1341     // tdf#115815 keep original start position of collapsed annotation ranges
1342     // as temporary bookmarks (removed after file saving and file loading)
1343     IDocumentMarkAccess& rDMA(*rDoc.getIDocumentMarkAccess());
1344     for (auto iter = rDMA.getAnnotationMarksBegin();
1345           iter != rDMA.getAnnotationMarksEnd(); )
1346     {
1347         SwPosition const& rStartPos((**iter).GetMarkStart());
1348         if ( *pStt <= rStartPos && rStartPos < *pEnd )
1349         {
1350             IDocumentMarkAccess::const_iterator_t pOldMark =
1351                     rDMA.findAnnotationBookmark((**iter).GetName());
1352             if ( pOldMark == rDMA.getBookmarksEnd() )
1353             {
1354                 // at start of redlines use a 1-character length bookmark range
1355                 // instead of a 0-character length bookmark position to avoid its losing
1356                 sal_Int32 nLen = (*pStt == rStartPos) ? 1 : 0;
1357                 SwPaM aPam( rStartPos.nNode, rStartPos.nContent.GetIndex(),
1358                                 rStartPos.nNode, rStartPos.nContent.GetIndex() + nLen);
1359                 ::sw::mark::IMark* pMark = rDMA.makeAnnotationBookmark(
1360                     aPam,
1361                     (**iter).GetName(),
1362                     IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
1363                 ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
1364                 if (pBookmark)
1365                 {
1366                     pBookmark->SetKeyCode(vcl::KeyCode());
1367                     pBookmark->SetShortName(OUString());
1368                 }
1369             }
1370         }
1371         ++iter;
1372     }
1373 }
1374 
MoveToSection()1375 void SwRangeRedline::MoveToSection()
1376 {
1377     if( !m_pContentSect )
1378     {
1379         const SwPosition* pStt = Start(),
1380                         * pEnd = pStt == GetPoint() ? GetMark() : GetPoint();
1381 
1382         SwDoc& rDoc = GetDoc();
1383         SwPaM aPam( *pStt, *pEnd );
1384         SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
1385         SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
1386 
1387         if( !pCSttNd )
1388         {
1389             // In order to not move other Redlines' indices, we set them
1390             // to the end (is exclusive)
1391             const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1392             for(SwRangeRedline* pRedl : rTable)
1393             {
1394                 if( pRedl->GetBound() == *pStt )
1395                     pRedl->GetBound() = *pEnd;
1396                 if( pRedl->GetBound(false) == *pStt )
1397                     pRedl->GetBound(false) = *pEnd;
1398             }
1399         }
1400 
1401         SwStartNode* pSttNd;
1402         SwNodes& rNds = rDoc.GetNodes();
1403         if( pCSttNd || pCEndNd )
1404         {
1405             SwTextFormatColl* pColl = (pCSttNd && pCSttNd->IsTextNode() )
1406                                     ? pCSttNd->GetTextNode()->GetTextColl()
1407                                     : (pCEndNd && pCEndNd->IsTextNode() )
1408                                         ? pCEndNd->GetTextNode()->GetTextColl()
1409                                         : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
1410 
1411             pSttNd = rNds.MakeTextSection( SwNodeIndex( rNds.GetEndOfRedlines() ),
1412                                             SwNormalStartNode, pColl );
1413             SwTextNode* pTextNd = rNds[ pSttNd->GetIndex() + 1 ]->GetTextNode();
1414 
1415             SwNodeIndex aNdIdx( *pTextNd );
1416             SwPosition aPos( aNdIdx, SwIndex( pTextNd ));
1417             if( pCSttNd && pCEndNd )
1418             {
1419                 // tdf#140982 keep annotation ranges in deletions in margin mode
1420                 lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
1421                 rDoc.getIDocumentContentOperations().MoveAndJoin( aPam, aPos );
1422             }
1423             else
1424             {
1425                 if( pCSttNd && !pCEndNd )
1426                     m_bDelLastPara = true;
1427                 rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
1428                     SwMoveFlags::DEFAULT );
1429             }
1430         }
1431         else
1432         {
1433             pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) );
1434 
1435             SwPosition aPos( *pSttNd->EndOfSectionNode() );
1436             rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
1437                 SwMoveFlags::DEFAULT );
1438         }
1439         m_pContentSect = new SwNodeIndex( *pSttNd );
1440 
1441         if( pStt == GetPoint() )
1442             Exchange();
1443 
1444         DeleteMark();
1445     }
1446     else
1447         InvalidateRange(Invalidation::Remove);
1448 }
1449 
CopyToSection()1450 void SwRangeRedline::CopyToSection()
1451 {
1452     if( m_pContentSect )
1453         return;
1454 
1455     const SwPosition* pStt = Start(),
1456                     * pEnd = pStt == GetPoint() ? GetMark() : GetPoint();
1457 
1458     SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
1459     SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
1460 
1461     SwStartNode* pSttNd;
1462     SwDoc& rDoc = GetDoc();
1463     SwNodes& rNds = rDoc.GetNodes();
1464 
1465     bool bSaveCopyFlag = rDoc.IsCopyIsMove(),
1466          bSaveRdlMoveFlg = rDoc.getIDocumentRedlineAccess().IsRedlineMove();
1467     rDoc.SetCopyIsMove( true );
1468 
1469     // The IsRedlineMove() flag causes the behaviour of the
1470     // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change,
1471     // which will eventually be called by the CopyRange() below.
1472     rDoc.getIDocumentRedlineAccess().SetRedlineMove(true);
1473 
1474     if( pCSttNd )
1475     {
1476         SwTextFormatColl* pColl = pCSttNd->IsTextNode()
1477                                 ? pCSttNd->GetTextNode()->GetTextColl()
1478                                 : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
1479 
1480         pSttNd = rNds.MakeTextSection( SwNodeIndex( rNds.GetEndOfRedlines() ),
1481                                         SwNormalStartNode, pColl );
1482 
1483         SwNodeIndex aNdIdx( *pSttNd, 1 );
1484         SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
1485         SwPosition aPos( aNdIdx, SwIndex( pTextNd ));
1486 
1487         // tdf#115815 keep original start position of collapsed annotation ranges
1488         // as temporary bookmarks (removed after file saving and file loading)
1489         lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
1490         rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
1491 
1492         // Take over the style from the EndNode if needed
1493         // We don't want this in Doc::Copy
1494         if( pCEndNd && pCEndNd != pCSttNd )
1495         {
1496             SwContentNode* pDestNd = aPos.nNode.GetNode().GetContentNode();
1497             if( pDestNd )
1498             {
1499                 if( pDestNd->IsTextNode() && pCEndNd->IsTextNode() )
1500                     pCEndNd->GetTextNode()->CopyCollFormat(*pDestNd->GetTextNode());
1501                 else
1502                     pDestNd->ChgFormatColl( pCEndNd->GetFormatColl() );
1503             }
1504         }
1505     }
1506     else
1507     {
1508         pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) );
1509 
1510         if( pCEndNd )
1511         {
1512             SwPosition aPos( *pSttNd->EndOfSectionNode() );
1513             rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
1514         }
1515         else
1516         {
1517             SwNodeIndex aInsPos( *pSttNd->EndOfSectionNode() );
1518             SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 );
1519             rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos);
1520         }
1521     }
1522     m_pContentSect = new SwNodeIndex( *pSttNd );
1523 
1524     rDoc.SetCopyIsMove( bSaveCopyFlag );
1525     rDoc.getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg );
1526 }
1527 
DelCopyOfSection(size_t nMyPos)1528 void SwRangeRedline::DelCopyOfSection(size_t nMyPos)
1529 {
1530     if( !m_pContentSect )
1531         return;
1532 
1533     const SwPosition* pStt = Start(),
1534                     * pEnd = pStt == GetPoint() ? GetMark() : GetPoint();
1535 
1536     SwDoc& rDoc = GetDoc();
1537     SwPaM aPam( *pStt, *pEnd );
1538     SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
1539     SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
1540 
1541     if( !pCSttNd )
1542     {
1543         // In order to not move other Redlines' indices, we set them
1544         // to the end (is exclusive)
1545         const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1546         for(SwRangeRedline* pRedl : rTable)
1547         {
1548             if( pRedl->GetBound() == *pStt )
1549                 pRedl->GetBound() = *pEnd;
1550             if( pRedl->GetBound(false) == *pStt )
1551                 pRedl->GetBound(false) = *pEnd;
1552         }
1553     }
1554 
1555     if( pCSttNd && pCEndNd )
1556     {
1557         // #i100466# - force a <join next> on <delete and join> operation
1558         // tdf#125319 - rather not?
1559         rDoc.getIDocumentContentOperations().DeleteAndJoin(aPam/*, true*/);
1560     }
1561     else if( pCSttNd || pCEndNd )
1562     {
1563         if( pCSttNd && !pCEndNd )
1564             m_bDelLastPara = true;
1565         rDoc.getIDocumentContentOperations().DeleteRange( aPam );
1566 
1567         if( m_bDelLastPara )
1568         {
1569             // To prevent dangling references to the paragraph to
1570             // be deleted, redline that point into this paragraph should be
1571             // moved to the new end position. Since redlines in the redline
1572             // table are sorted and the pEnd position is an endnode (see
1573             // bDelLastPara condition above), only redlines before the
1574             // current ones can be affected.
1575             const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1576             size_t n = nMyPos;
1577             for( bool bBreak = false; !bBreak && n > 0; )
1578             {
1579                 --n;
1580                 bBreak = true;
1581                 if( rTable[ n ]->GetBound() == *aPam.GetPoint() )
1582                 {
1583                     rTable[ n ]->GetBound() = *pEnd;
1584                     bBreak = false;
1585                 }
1586                 if( rTable[ n ]->GetBound(false) == *aPam.GetPoint() )
1587                 {
1588                     rTable[ n ]->GetBound(false) = *pEnd;
1589                     bBreak = false;
1590                 }
1591             }
1592 
1593             *GetPoint() = *pEnd;
1594             *GetMark() = *pEnd;
1595             DeleteMark();
1596 
1597             aPam.GetBound().nContent.Assign( nullptr, 0 );
1598             aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
1599             aPam.DeleteMark();
1600             rDoc.getIDocumentContentOperations().DelFullPara( aPam );
1601         }
1602     }
1603     else
1604     {
1605         rDoc.getIDocumentContentOperations().DeleteRange( aPam );
1606     }
1607 
1608     if( pStt == GetPoint() )
1609         Exchange();
1610 
1611     DeleteMark();
1612 }
1613 
MoveFromSection(size_t nMyPos)1614 void SwRangeRedline::MoveFromSection(size_t nMyPos)
1615 {
1616     if( m_pContentSect )
1617     {
1618         SwDoc& rDoc = GetDoc();
1619         const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1620         std::vector<SwPosition*> aBeforeArr, aBehindArr;
1621         bool bBreak = false;
1622         SwRedlineTable::size_type n;
1623 
1624         for( n = nMyPos+1; !bBreak && n < rTable.size(); ++n )
1625         {
1626             bBreak = true;
1627             if( rTable[ n ]->GetBound() == *GetPoint() )
1628             {
1629                 SwRangeRedline* pRedl = rTable[n];
1630                 aBehindArr.push_back(&pRedl->GetBound());
1631                 bBreak = false;
1632             }
1633             if( rTable[ n ]->GetBound(false) == *GetPoint() )
1634             {
1635                 SwRangeRedline* pRedl = rTable[n];
1636                 aBehindArr.push_back(&pRedl->GetBound(false));
1637                 bBreak = false;
1638             }
1639         }
1640         for( bBreak = false, n = nMyPos; !bBreak && n ; )
1641         {
1642             --n;
1643             bBreak = true;
1644             if( rTable[ n ]->GetBound() == *GetPoint() )
1645             {
1646                 SwRangeRedline* pRedl = rTable[n];
1647                 aBeforeArr.push_back(&pRedl->GetBound());
1648                 bBreak = false;
1649             }
1650             if( rTable[ n ]->GetBound(false) == *GetPoint() )
1651             {
1652                 SwRangeRedline* pRedl = rTable[n];
1653                 aBeforeArr.push_back(&pRedl->GetBound(false));
1654                 bBreak = false;
1655             }
1656         }
1657 
1658         const SwNode* pKeptContentSectNode( &m_pContentSect->GetNode() ); // #i95711#
1659         {
1660             SwPaM aPam( m_pContentSect->GetNode(),
1661                         *m_pContentSect->GetNode().EndOfSectionNode(), 1,
1662                         ( m_bDelLastPara ? -2 : -1 ) );
1663             SwContentNode* pCNd = aPam.GetContentNode();
1664             if( pCNd )
1665                 aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
1666             else
1667                 ++aPam.GetPoint()->nNode;
1668 
1669             SwFormatColl* pColl = pCNd && pCNd->Len() && aPam.GetPoint()->nNode !=
1670                                         aPam.GetMark()->nNode
1671                                 ? pCNd->GetFormatColl() : nullptr;
1672 
1673             SwNodeIndex aNdIdx( GetPoint()->nNode, -1 );
1674             const sal_Int32 nPos = GetPoint()->nContent.GetIndex();
1675 
1676             SwPosition aPos( *GetPoint() );
1677             if( m_bDelLastPara && *aPam.GetPoint() == *aPam.GetMark() )
1678             {
1679                 --aPos.nNode;
1680 
1681                 rDoc.getIDocumentContentOperations().AppendTextNode( aPos );
1682             }
1683             else
1684             {
1685                 rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
1686                     SwMoveFlags::ALLFLYS );
1687             }
1688 
1689             SetMark();
1690             *GetPoint() = aPos;
1691             GetMark()->nNode = aNdIdx.GetIndex() + 1;
1692             pCNd = GetMark()->nNode.GetNode().GetContentNode();
1693             GetMark()->nContent.Assign( pCNd, nPos );
1694 
1695             if( m_bDelLastPara )
1696             {
1697                 ++GetPoint()->nNode;
1698                 pCNd = GetContentNode();
1699                 GetPoint()->nContent.Assign( pCNd, 0 );
1700                 m_bDelLastPara = false;
1701             }
1702             else if( pColl )
1703                 pCNd = GetContentNode();
1704 
1705             if( pColl && pCNd )
1706                 pCNd->ChgFormatColl( pColl );
1707         }
1708 
1709         // #i95771#
1710         // Under certain conditions the previous <SwDoc::Move(..)> has already
1711         // removed the change tracking section of this <SwRangeRedline> instance from
1712         // the change tracking nodes area.
1713         // Thus, check if <pContentSect> still points to the change tracking section
1714         // by comparing it with the "indexed" <SwNode> instance copied before
1715         // perform the intrinsic move.
1716         // Note: Such condition is e.g. a "delete" change tracking only containing a table.
1717         if ( &m_pContentSect->GetNode() == pKeptContentSectNode )
1718         {
1719             rDoc.getIDocumentContentOperations().DeleteSection( &m_pContentSect->GetNode() );
1720         }
1721         delete m_pContentSect;
1722         m_pContentSect = nullptr;
1723 
1724         // adjustment of redline table positions must take start and
1725         // end into account, not point and mark.
1726         for( auto& pItem : aBeforeArr )
1727             *pItem = *Start();
1728         for( auto& pItem : aBehindArr )
1729             *pItem = *End();
1730     }
1731     else
1732         InvalidateRange(Invalidation::Add);
1733 }
1734 
1735 // for Undo
SetContentIdx(const SwNodeIndex * pIdx)1736 void SwRangeRedline::SetContentIdx( const SwNodeIndex* pIdx )
1737 {
1738     if( pIdx && !m_pContentSect )
1739     {
1740         m_pContentSect = new SwNodeIndex( *pIdx );
1741         m_bIsVisible = false;
1742     }
1743     else if( !pIdx && m_pContentSect )
1744     {
1745         delete m_pContentSect;
1746         m_pContentSect = nullptr;
1747         m_bIsVisible = false;
1748     }
1749     else
1750     {
1751         OSL_FAIL("SwRangeRedline::SetContentIdx: invalid state");
1752     }
1753 }
1754 
CanCombine(const SwRangeRedline & rRedl) const1755 bool SwRangeRedline::CanCombine( const SwRangeRedline& rRedl ) const
1756 {
1757     return  IsVisible() && rRedl.IsVisible() &&
1758             m_pRedlineData->CanCombine( *rRedl.m_pRedlineData );
1759 }
1760 
PushData(const SwRangeRedline & rRedl,bool bOwnAsNext)1761 void SwRangeRedline::PushData( const SwRangeRedline& rRedl, bool bOwnAsNext )
1762 {
1763     SwRedlineData* pNew = new SwRedlineData( *rRedl.m_pRedlineData, false );
1764     if( bOwnAsNext )
1765     {
1766         pNew->m_pNext = m_pRedlineData;
1767         m_pRedlineData = pNew;
1768     }
1769     else
1770     {
1771         pNew->m_pNext = m_pRedlineData->m_pNext;
1772         m_pRedlineData->m_pNext = pNew;
1773     }
1774 }
1775 
PopData()1776 bool SwRangeRedline::PopData()
1777 {
1778     if( !m_pRedlineData->m_pNext )
1779         return false;
1780     SwRedlineData* pCur = m_pRedlineData;
1781     m_pRedlineData = pCur->m_pNext;
1782     pCur->m_pNext = nullptr;
1783     delete pCur;
1784     return true;
1785 }
1786 
GetStackCount() const1787 sal_uInt16 SwRangeRedline::GetStackCount() const
1788 {
1789     sal_uInt16 nRet = 1;
1790     for( SwRedlineData* pCur = m_pRedlineData; pCur->m_pNext; pCur = pCur->m_pNext )
1791         ++nRet;
1792     return nRet;
1793 }
1794 
GetAuthor(sal_uInt16 nPos) const1795 std::size_t SwRangeRedline::GetAuthor( sal_uInt16 nPos ) const
1796 {
1797     return GetRedlineData(nPos).m_nAuthor;
1798 }
1799 
GetAuthorString(sal_uInt16 nPos) const1800 OUString const & SwRangeRedline::GetAuthorString( sal_uInt16 nPos ) const
1801 {
1802     return SW_MOD()->GetRedlineAuthor(GetRedlineData(nPos).m_nAuthor);
1803 }
1804 
GetTimeStamp(sal_uInt16 nPos) const1805 const DateTime& SwRangeRedline::GetTimeStamp( sal_uInt16 nPos ) const
1806 {
1807     return GetRedlineData(nPos).m_aStamp;
1808 }
1809 
GetType(sal_uInt16 nPos) const1810 RedlineType SwRangeRedline::GetType( sal_uInt16 nPos ) const
1811 {
1812     return GetRedlineData(nPos).m_eType;
1813 }
1814 
GetComment(sal_uInt16 nPos) const1815 const OUString& SwRangeRedline::GetComment( sal_uInt16 nPos ) const
1816 {
1817     return GetRedlineData(nPos).m_sComment;
1818 }
1819 
operator <(const SwRangeRedline & rCmp) const1820 bool SwRangeRedline::operator<( const SwRangeRedline& rCmp ) const
1821 {
1822     if (*Start() < *rCmp.Start())
1823         return true;
1824 
1825     return *Start() == *rCmp.Start() && *End() < *rCmp.End();
1826 }
1827 
GetRedlineData(const sal_uInt16 nPos) const1828 const SwRedlineData & SwRangeRedline::GetRedlineData(const sal_uInt16 nPos) const
1829 {
1830     SwRedlineData * pCur = m_pRedlineData;
1831 
1832     sal_uInt16 nP = nPos;
1833 
1834     while (nP > 0 && nullptr != pCur->m_pNext)
1835     {
1836         pCur = pCur->m_pNext;
1837 
1838         nP--;
1839     }
1840 
1841     SAL_WARN_IF( nP != 0, "sw.core", "Pos " << nPos << " is " << nP << " too big");
1842 
1843     return *pCur;
1844 }
1845 
GetDescr(bool bSimplified)1846 OUString SwRangeRedline::GetDescr(bool bSimplified)
1847 {
1848     // get description of redline data (e.g.: "insert $1")
1849     OUString aResult = GetRedlineData().GetDescr();
1850 
1851     SwPaM * pPaM = nullptr;
1852     bool bDeletePaM = false;
1853 
1854     // if this redline is visible the content is in this PaM
1855     if (nullptr == m_pContentSect)
1856     {
1857         pPaM = this;
1858     }
1859     else // otherwise it is saved in pContentSect
1860     {
1861         SwNodeIndex aTmpIdx( *m_pContentSect->GetNode().EndOfSectionNode() );
1862         pPaM = new SwPaM(*m_pContentSect, aTmpIdx );
1863         bDeletePaM = true;
1864     }
1865 
1866     OUString sDescr = DenoteSpecialCharacters(pPaM->GetText().replace('\n', ' '), /*bQuoted=*/!bSimplified);
1867     if (const SwTextNode *pTextNode = pPaM->GetNode().GetTextNode())
1868     {
1869         if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->nContent.GetIndex() - 1, true ))
1870         {
1871             sDescr = ( bSimplified ? "" : SwResId(STR_START_QUOTE) )
1872                 + pTextAttr->GetFormatField().GetField()->GetFieldName()
1873                 + ( bSimplified ? "" : SwResId(STR_END_QUOTE) );
1874         }
1875     }
1876 
1877     // replace $1 in description by description of the redlines text
1878     const OUString aTmpStr = ShortenString(sDescr, nUndoStringLength, SwResId(STR_LDOTS));
1879 
1880     if (!bSimplified)
1881     {
1882         SwRewriter aRewriter;
1883         aRewriter.AddRule(UndoArg1, aTmpStr);
1884 
1885         aResult = aRewriter.Apply(aResult);
1886     }
1887     else
1888     {
1889         aResult = aTmpStr;
1890         // more shortening
1891         sal_Int32 nPos = aTmpStr.indexOf(SwResId(STR_LDOTS));
1892         if (nPos > 5)
1893             aResult = aTmpStr.copy(0, nPos + SwResId(STR_LDOTS).getLength());
1894     }
1895 
1896     if (bDeletePaM)
1897         delete pPaM;
1898 
1899     return aResult;
1900 }
1901 
dumpAsXml(xmlTextWriterPtr pWriter) const1902 void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter) const
1903 {
1904     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRangeRedline"));
1905 
1906     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1907     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr()));
1908     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("author"), BAD_CAST(SW_MOD()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr()));
1909     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr()));
1910     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("descr"), BAD_CAST(const_cast<SwRangeRedline*>(this)->GetDescr().toUtf8().getStr()));
1911 
1912     OString sRedlineType;
1913     switch (GetType())
1914     {
1915         case RedlineType::Insert:
1916             sRedlineType = "REDLINE_INSERT";
1917             break;
1918         case RedlineType::Delete:
1919             sRedlineType = "REDLINE_DELETE";
1920             break;
1921         case RedlineType::Format:
1922             sRedlineType = "REDLINE_FORMAT";
1923             break;
1924         default:
1925             sRedlineType = "UNKNOWN";
1926             break;
1927     }
1928     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(sRedlineType.getStr()));
1929 
1930     SwPaM::dumpAsXml(pWriter);
1931 
1932     (void)xmlTextWriterEndElement(pWriter);
1933 }
1934 
Insert(SwExtraRedline * p)1935 void SwExtraRedlineTable::Insert( SwExtraRedline* p )
1936 {
1937     m_aExtraRedlines.push_back( p );
1938     //p->CallDisplayFunc();
1939 }
1940 
DeleteAndDestroy(sal_uInt16 const nPos)1941 void SwExtraRedlineTable::DeleteAndDestroy(sal_uInt16 const nPos)
1942 {
1943     /*
1944     SwDoc* pDoc = 0;
1945     if( !nP && nL && nL == size() )
1946         pDoc = front()->GetDoc();
1947     */
1948 
1949     delete m_aExtraRedlines[nPos];
1950     m_aExtraRedlines.erase(m_aExtraRedlines.begin() + nPos);
1951 
1952     /*
1953     SwViewShell* pSh;
1954     if( pDoc && !pDoc->IsInDtor() &&
1955         0 != ( pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) )
1956         pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
1957     */
1958 }
1959 
DeleteAndDestroyAll()1960 void SwExtraRedlineTable::DeleteAndDestroyAll()
1961 {
1962     while (!m_aExtraRedlines.empty())
1963     {
1964         auto const pRedline = m_aExtraRedlines.back();
1965         m_aExtraRedlines.pop_back();
1966         delete pRedline;
1967     }
1968 }
1969 
~SwExtraRedline()1970 SwExtraRedline::~SwExtraRedline()
1971 {
1972 }
1973 
SwTableRowRedline(const SwRedlineData & rData,const SwTableLine & rTableLine)1974 SwTableRowRedline::SwTableRowRedline(const SwRedlineData& rData, const SwTableLine& rTableLine)
1975     : m_aRedlineData(rData)
1976     , m_rTableLine(rTableLine)
1977 {
1978 }
1979 
~SwTableRowRedline()1980 SwTableRowRedline::~SwTableRowRedline()
1981 {
1982 }
1983 
SwTableCellRedline(const SwRedlineData & rData,const SwTableBox & rTableBox)1984 SwTableCellRedline::SwTableCellRedline(const SwRedlineData& rData, const SwTableBox& rTableBox)
1985     : m_aRedlineData(rData)
1986     , m_rTableBox(rTableBox)
1987 {
1988 }
1989 
~SwTableCellRedline()1990 SwTableCellRedline::~SwTableCellRedline()
1991 {
1992 }
1993 
1994 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1995