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