Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / doc / docredln.cxx
blob54d85306562960d49d0239b29a57a1b1a2795565
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <libxml/xmlwriter.h>
21 #include <boost/property_tree/json_parser.hpp>
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 <editeng/prntitem.hxx>
29 #include <comphelper/lok.hxx>
30 #include <comphelper/string.hxx>
31 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
32 #include <unotools/datetime.hxx>
33 #include <sfx2/viewsh.hxx>
34 #include <o3tl/string_view.hxx>
35 #include <swmodule.hxx>
36 #include <doc.hxx>
37 #include <docredln.hxx>
38 #include <IDocumentUndoRedo.hxx>
39 #include <DocumentContentOperationsManager.hxx>
40 #include <IDocumentRedlineAccess.hxx>
41 #include <IDocumentState.hxx>
42 #include <IDocumentLayoutAccess.hxx>
43 #include <IDocumentStylePoolAccess.hxx>
44 #include <docary.hxx>
45 #include <ndtxt.hxx>
46 #include <redline.hxx>
47 #include <UndoCore.hxx>
48 #include <hints.hxx>
49 #include <pamtyp.hxx>
50 #include <poolfmt.hxx>
51 #include <algorithm>
52 #include <limits>
53 #include <utility>
54 #include <view.hxx>
55 #include <viewopt.hxx>
56 #include <usrpref.hxx>
57 #include <viewsh.hxx>
58 #include <viscrs.hxx>
59 #include <rootfrm.hxx>
60 #include <strings.hrc>
61 #include <swtypes.hxx>
62 #include <wrtsh.hxx>
63 #include <txtfld.hxx>
65 #include <flowfrm.hxx>
66 #include <txtfrm.hxx>
68 using namespace com::sun::star;
70 #ifdef DBG_UTIL
72 void sw_DebugRedline( const SwDoc* pDoc )
74 static SwRedlineTable::size_type nWatch = 0; // loplugin:constvars:ignore
75 const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
76 for( SwRedlineTable::size_type n = 0; n < rTable.size(); ++n )
78 volatile SwRedlineTable::size_type nDummy = 0;
79 const SwRangeRedline* pCurrent = rTable[ n ];
80 const SwRangeRedline* pNext = n+1 < rTable.size() ? rTable[ n+1 ] : nullptr;
81 if( pCurrent == pNext )
82 (void) nDummy;
83 if( n == nWatch )
84 (void) nDummy; // Possible debugger breakpoint
88 #endif
91 SwExtraRedlineTable::~SwExtraRedlineTable()
93 DeleteAndDestroyAll();
96 void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
98 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedlineTable"));
99 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
101 for (sal_uInt16 nCurExtraRedlinePos = 0; nCurExtraRedlinePos < GetSize(); ++nCurExtraRedlinePos)
103 const SwExtraRedline* pExtraRedline = GetRedline(nCurExtraRedlinePos);
104 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedline"));
105 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
106 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline).name()));
107 (void)xmlTextWriterEndElement(pWriter);
109 (void)xmlTextWriterEndElement(pWriter);
112 #if OSL_DEBUG_LEVEL > 0
113 static bool CheckPosition( const SwPosition* pStt, const SwPosition* pEnd )
115 int nError = 0;
116 SwNode* pSttNode = &pStt->GetNode();
117 SwNode* pEndNode = &pEnd->GetNode();
118 SwNode* pSttTab = pSttNode->StartOfSectionNode()->FindTableNode();
119 SwNode* pEndTab = pEndNode->StartOfSectionNode()->FindTableNode();
120 SwNode* pSttStart = pSttNode;
121 while( pSttStart && (!pSttStart->IsStartNode() || pSttStart->IsSectionNode() ||
122 pSttStart->IsTableNode() ) )
123 pSttStart = pSttStart->StartOfSectionNode();
124 SwNode* pEndStart = pEndNode;
125 while( pEndStart && (!pEndStart->IsStartNode() || pEndStart->IsSectionNode() ||
126 pEndStart->IsTableNode() ) )
127 pEndStart = pEndStart->StartOfSectionNode();
128 assert(pSttTab == pEndTab);
129 if( pSttTab != pEndTab )
130 nError = 1;
131 assert(pSttTab || pSttStart == pEndStart);
132 if( !pSttTab && pSttStart != pEndStart )
133 nError |= 2;
134 if( nError )
135 nError += 10;
136 return nError != 0;
138 #endif
140 bool SwExtraRedlineTable::DeleteAllTableRedlines( SwDoc& rDoc, const SwTable& rTable, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
142 bool bChg = false;
144 if (bSaveInUndo && rDoc.GetIDocumentUndoRedo().DoesUndo())
146 // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
148 SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
149 if( pUndo->GetRedlSaveCount() )
151 GetIDocumentUndoRedo().AppendUndo(pUndo);
153 else
154 delete pUndo;
158 for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); )
160 SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
161 const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
162 if (pTableCellRedline)
164 const SwTableBox *pRedTabBox = &pTableCellRedline->GetTableBox();
165 const SwTable& rRedTable = pRedTabBox->GetSttNd()->FindTableNode()->GetTable();
166 if ( &rRedTable == &rTable )
168 // Redline for this table
169 const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
170 const RedlineType nRedlineType = aRedlineData.GetType();
172 // Check if this redline object type should be deleted
173 if (RedlineType::Any == nRedlineTypeToDelete || nRedlineTypeToDelete == nRedlineType)
176 DeleteAndDestroy( nCurRedlinePos );
177 bChg = true;
178 continue; // don't increment position after delete
182 ++nCurRedlinePos;
185 if( bChg )
186 rDoc.getIDocumentState().SetModified();
188 return bChg;
191 bool SwExtraRedlineTable::DeleteTableRowRedline( SwDoc* pDoc, const SwTableLine& rTableLine, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
193 bool bChg = false;
195 if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
197 // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
199 SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
200 if( pUndo->GetRedlSaveCount() )
202 GetIDocumentUndoRedo().AppendUndo(pUndo);
204 else
205 delete pUndo;
209 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
211 SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
212 const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
213 const SwTableLine *pRedTabLine = pTableRowRedline ? &pTableRowRedline->GetTableLine() : nullptr;
214 if ( pRedTabLine == &rTableLine )
216 // Redline for this table row
217 const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
218 const RedlineType nRedlineType = aRedlineData.GetType();
220 // Check if this redline object type should be deleted
221 if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
222 continue;
224 DeleteAndDestroy( nCurRedlinePos );
225 bChg = true;
229 if( bChg )
230 pDoc->getIDocumentState().SetModified();
232 return bChg;
235 bool SwExtraRedlineTable::DeleteTableCellRedline( SwDoc* pDoc, const SwTableBox& rTableBox, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
237 bool bChg = false;
239 if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
241 // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
243 SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
244 if( pUndo->GetRedlSaveCount() )
246 GetIDocumentUndoRedo().AppendUndo(pUndo);
248 else
249 delete pUndo;
253 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
255 SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
256 const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
257 const SwTableBox *pRedTabBox = pTableCellRedline ? &pTableCellRedline->GetTableBox() : nullptr;
258 if ( pRedTabBox == &rTableBox )
260 // Redline for this table cell
261 const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
262 const RedlineType nRedlineType = aRedlineData.GetType();
264 // Check if this redline object type should be deleted
265 if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
266 continue;
268 DeleteAndDestroy( nCurRedlinePos );
269 bChg = true;
273 if( bChg )
274 pDoc->getIDocumentState().SetModified();
276 return bChg;
279 namespace
282 void lcl_LOKInvalidateFrames(const sw::BroadcastingModify& rMod, const SwRootFrame* pLayout,
283 SwFrameType const nFrameType, const Point* pPoint)
285 SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
287 for (SwFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
289 if ((pTmpFrame->GetType() & nFrameType) &&
290 (!pLayout || pLayout == pTmpFrame->getRootFrame()) &&
291 (!pTmpFrame->IsFlowFrame() || !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow()))
293 if (pPoint)
295 pTmpFrame->InvalidateSize();
297 // Also empty the text portion cache, so it gets rebuilt, taking the new redlines
298 // into account.
299 if (pTmpFrame->IsTextFrame())
301 auto pTextFrame = static_cast<SwTextFrame*>(pTmpFrame);
302 pTextFrame->ClearPara();
309 void lcl_LOKInvalidateStartEndFrames(SwShellCursor& rCursor)
311 if (!(rCursor.HasMark() &&
312 rCursor.GetPoint()->GetNode().IsContentNode() &&
313 rCursor.GetPoint()->GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout()) &&
314 (rCursor.GetMark()->GetNode() == rCursor.GetPoint()->GetNode() ||
315 (rCursor.GetMark()->GetNode().IsContentNode() &&
316 rCursor.GetMark()->GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout())))))
318 return;
321 auto [pStartPos, pEndPos] = rCursor.StartEnd(); // SwPosition*
323 lcl_LOKInvalidateFrames(*(pStartPos->GetNode().GetContentNode()),
324 rCursor.GetShell()->GetLayout(),
325 FRM_CNTNT, &rCursor.GetSttPos());
327 lcl_LOKInvalidateFrames(*(pEndPos->GetNode().GetContentNode()),
328 rCursor.GetShell()->GetLayout(),
329 FRM_CNTNT, &rCursor.GetEndPos());
332 bool lcl_LOKRedlineNotificationEnabled()
334 static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr;
335 if (comphelper::LibreOfficeKit::isActive() && !bDisableRedlineComments)
336 return true;
338 return false;
341 } // anonymous namespace
343 /// Emits LOK notification about one addition / removal of a redline item.
344 void SwRedlineTable::LOKRedlineNotification(RedlineNotification nType, SwRangeRedline* pRedline)
346 // Disable since usability is very low beyond some small number of changes.
347 if (!lcl_LOKRedlineNotificationEnabled())
348 return;
350 boost::property_tree::ptree aRedline;
351 aRedline.put("action", (nType == RedlineNotification::Add ? "Add" :
352 (nType == RedlineNotification::Remove ? "Remove" :
353 (nType == RedlineNotification::Modify ? "Modify" : "???"))));
354 aRedline.put("index", pRedline->GetId());
355 aRedline.put("author", pRedline->GetAuthorString(1).toUtf8().getStr());
356 aRedline.put("type", SwRedlineTypeToOUString(pRedline->GetRedlineData().GetType()).toUtf8().getStr());
357 aRedline.put("comment", pRedline->GetRedlineData().GetComment().toUtf8().getStr());
358 aRedline.put("description", pRedline->GetDescr().toUtf8().getStr());
359 OUString sDateTime = utl::toISO8601(pRedline->GetRedlineData().GetTimeStamp().GetUNODateTime());
360 aRedline.put("dateTime", sDateTime.toUtf8().getStr());
362 auto [pStartPos, pEndPos] = pRedline->StartEnd(); // SwPosition*
363 SwContentNode* pContentNd = pRedline->GetPointContentNode();
364 SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
365 if (pView && pContentNd)
367 SwShellCursor aCursor(pView->GetWrtShell(), *pStartPos);
368 aCursor.SetMark();
369 *aCursor.GetMark() = *pEndPos;
371 aCursor.FillRects();
373 SwRects* pRects(&aCursor);
374 std::vector<OString> aRects;
375 for(const SwRect& rNextRect : *pRects)
376 aRects.push_back(rNextRect.SVRect().toString());
378 const OString sRects = comphelper::string::join("; ", aRects);
379 aRedline.put("textRange", sRects.getStr());
381 lcl_LOKInvalidateStartEndFrames(aCursor);
383 // When this notify method is called text invalidation is not done yet
384 // Calling FillRects updates the text area so invalidation will not run on the correct rects
385 // So we need to do an own invalidation here. It invalidates text frames containing the redlining
386 SwDoc& rDoc = pRedline->GetDoc();
387 SwViewShell* pSh;
388 if( !rDoc.IsInDtor() )
390 pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
391 if( pSh )
392 for(SwNodeIndex nIdx(pStartPos->GetNode()); nIdx <= pEndPos->GetNode(); ++nIdx)
394 SwContentNode* pContentNode = nIdx.GetNode().GetContentNode();
395 if (pContentNode)
396 pSh->InvalidateWindows(pContentNode->FindLayoutRect());
401 boost::property_tree::ptree aTree;
402 aTree.add_child("redline", aRedline);
403 std::stringstream aStream;
404 boost::property_tree::write_json(aStream, aTree);
405 std::string aPayload = aStream.str();
407 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
408 while (pViewShell)
410 if (pView && pView->GetDocId() == pViewShell->GetDocId())
411 pViewShell->libreOfficeKitViewCallback(nType == RedlineNotification::Modify ? LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED : LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED, OString(aPayload));
412 pViewShell = SfxViewShell::GetNext(*pViewShell);
416 bool SwRedlineTable::Insert(SwRangeRedline*& p)
418 if( p->HasValidRange() )
420 std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
421 size_type nP = rv.first - begin();
422 LOKRedlineNotification(RedlineNotification::Add, p);
424 // detect text moving by checking nearby redlines, except during Undo
425 // (apply isMoved() during OpenDocument and DOCX import, too, to fix
426 // missing text moving handling in ODF and e.g. web version of MSO)
427 if ( p->GetDoc().GetIDocumentUndoRedo().DoesUndo() ||
428 p->GetDoc().IsInWriterfilterImport() ||
429 p->GetDoc().IsInXMLImport() )
431 isMoved(nP);
434 p->CallDisplayFunc(nP);
435 if (rv.second)
436 CheckOverlapping(rv.first);
437 return rv.second;
439 return InsertWithValidRanges( p );
442 void SwRedlineTable::CheckOverlapping(vector_type::const_iterator it)
444 if (m_bHasOverlappingElements)
445 return;
446 if (maVector.size() <= 1) // a single element cannot be overlapping
447 return;
448 auto pCurr = *it;
449 auto itNext = it + 1;
450 if (itNext != maVector.end())
452 auto pNext = *itNext;
453 if (pCurr->End()->GetNodeIndex() >= pNext->Start()->GetNodeIndex())
455 m_bHasOverlappingElements = true;
456 return;
459 if (it != maVector.begin())
461 auto pPrev = *(it - 1);
462 if (pPrev->End()->GetNodeIndex() >= pCurr->Start()->GetNodeIndex())
463 m_bHasOverlappingElements = true;
467 bool SwRedlineTable::Insert(SwRangeRedline*& p, size_type& rP)
469 if( p->HasValidRange() )
471 std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
472 rP = rv.first - begin();
473 p->CallDisplayFunc(rP);
474 if (rv.second)
475 CheckOverlapping(rv.first);
476 return rv.second;
478 return InsertWithValidRanges( p, &rP );
481 namespace sw {
483 std::vector<std::unique_ptr<SwRangeRedline>> GetAllValidRanges(std::unique_ptr<SwRangeRedline> p)
485 std::vector<std::unique_ptr<SwRangeRedline>> ret;
486 // Create valid "sub-ranges" from the Selection
487 auto [pStt, pEnd] = p->StartEnd(); // SwPosition*
488 SwPosition aNewStt( *pStt );
489 SwNodes& rNds = aNewStt.GetNodes();
490 SwContentNode* pC;
492 if( !aNewStt.GetNode().IsContentNode() )
494 pC = rNds.GoNext( &aNewStt );
495 if( !pC )
496 aNewStt.Assign(rNds.GetEndOfContent());
500 if( aNewStt >= *pEnd )
501 return ret;
503 std::unique_ptr<SwRangeRedline> pNew;
504 do {
505 if( !pNew )
506 pNew.reset(new SwRangeRedline( p->GetRedlineData(), aNewStt ));
507 else
509 pNew->DeleteMark();
510 *pNew->GetPoint() = aNewStt;
513 pNew->SetMark();
514 GoEndSection( pNew->GetPoint() );
515 // i60396: If the redlines starts before a table but the table is the last member
516 // of the section, the GoEndSection will end inside the table.
517 // This will result in an incorrect redline, so we've to go back
518 SwNode* pTab = pNew->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
519 // We end in a table when pTab != 0
520 if( pTab && !pNew->GetMark()->GetNode().StartOfSectionNode()->FindTableNode() )
521 { // but our Mark was outside the table => Correction
524 // We want to be before the table
525 pNew->GetPoint()->Assign(*pTab);
526 pC = GoPreviousPos( pNew->GetPoint(), false ); // here we are.
527 if( pC )
528 pNew->GetPoint()->SetContent( 0 );
529 pTab = pNew->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
530 } while( pTab ); // If there is another table we have to repeat our step backwards
533 // insert dummy character to the empty table rows to keep their changes
534 SwNode& rBoxNode = pNew->GetMark()->GetNode();
535 if ( rBoxNode.GetDoc().GetIDocumentUndoRedo().DoesUndo() && rBoxNode.GetTableBox() &&
536 rBoxNode.GetTableBox()->GetUpper()->IsEmpty() && rBoxNode.GetTextNode() )
538 ::sw::UndoGuard const undoGuard(rBoxNode.GetDoc().GetIDocumentUndoRedo());
539 rBoxNode.GetTextNode()->InsertDummy();
540 pNew->GetMark()->SetContent( 1 );
543 if( *pNew->GetPoint() > *pEnd )
545 pC = nullptr;
546 if( aNewStt.GetNode() != pEnd->GetNode() )
547 do {
548 SwNode& rCurNd = aNewStt.GetNode();
549 if( rCurNd.IsStartNode() )
551 if( rCurNd.EndOfSectionIndex() < pEnd->GetNodeIndex() )
552 aNewStt.Assign( *rCurNd.EndOfSectionNode() );
553 else
554 break;
556 else if( rCurNd.IsContentNode() )
557 pC = rCurNd.GetContentNode();
558 aNewStt.Adjust(SwNodeOffset(1));
559 } while( aNewStt.GetNodeIndex() < pEnd->GetNodeIndex() );
561 if( aNewStt.GetNode() == pEnd->GetNode() )
562 aNewStt.SetContent(pEnd->GetContentIndex());
563 else if( pC )
565 aNewStt.Assign(*pC, pC->Len() );
568 if( aNewStt <= *pEnd )
569 *pNew->GetPoint() = aNewStt;
571 else
572 aNewStt = *pNew->GetPoint();
573 #if OSL_DEBUG_LEVEL > 0
574 CheckPosition( pNew->GetPoint(), pNew->GetMark() );
575 #endif
577 if( *pNew->GetPoint() != *pNew->GetMark() &&
578 pNew->HasValidRange())
580 ret.push_back(std::move(pNew));
583 if( aNewStt >= *pEnd )
584 break;
585 pC = rNds.GoNext( &aNewStt );
586 if( !pC )
587 break;
588 } while( aNewStt < *pEnd );
590 return ret;
593 } // namespace sw
595 static void lcl_setRowNotTracked(SwNode& rNode)
597 SwDoc& rDoc = rNode.GetDoc();
598 if ( rDoc.GetIDocumentUndoRedo().DoesUndo() && rNode.GetTableBox() )
600 SvxPrintItem aSetTracking(RES_PRINT, false);
601 SwNodeIndex aInsPos( *(rNode.GetTableBox()->GetSttNd()), 1);
602 SwCursor aCursor( SwPosition(aInsPos), nullptr );
603 ::sw::UndoGuard const undoGuard(rNode.GetDoc().GetIDocumentUndoRedo());
604 rDoc.SetRowNotTracked( aCursor, aSetTracking );
608 bool SwRedlineTable::InsertWithValidRanges(SwRangeRedline*& p, size_type* pInsPos)
610 bool bAnyIns = false;
611 bool bInsert = RedlineType::Insert == p->GetType();
612 SwNode* pSttNode = &p->Start()->GetNode();
614 std::vector<std::unique_ptr<SwRangeRedline>> redlines(
615 GetAllValidRanges(std::unique_ptr<SwRangeRedline>(p)));
617 // tdf#147180 set table change tracking in the empty row with text insertion
618 if ( bInsert )
619 lcl_setRowNotTracked(*pSttNode);
621 for (std::unique_ptr<SwRangeRedline> & pRedline : redlines)
623 assert(pRedline->HasValidRange());
624 size_type nInsPos;
625 auto pTmpRedline = pRedline.release();
626 if (Insert(pTmpRedline, nInsPos))
628 // tdf#147180 set table tracking to the table row
629 lcl_setRowNotTracked(pTmpRedline->GetPointNode());
631 pTmpRedline->CallDisplayFunc(nInsPos);
632 bAnyIns = true;
633 if (pInsPos && *pInsPos < nInsPos)
635 *pInsPos = nInsPos;
639 p = nullptr;
640 return bAnyIns;
643 bool CompareSwRedlineTable::operator()(SwRangeRedline* const &lhs, SwRangeRedline* const &rhs) const
645 return *lhs < *rhs;
648 SwRedlineTable::~SwRedlineTable()
650 maVector.DeleteAndDestroyAll();
653 SwRedlineTable::size_type SwRedlineTable::GetPos(const SwRangeRedline* p) const
655 vector_type::const_iterator it = maVector.find(const_cast<SwRangeRedline*>(p));
656 if( it == maVector.end() )
657 return npos;
658 return it - maVector.begin();
661 void SwRedlineTable::Remove( const SwRangeRedline* p )
663 const size_type nPos = GetPos(p);
664 if (nPos == npos)
665 return;
666 Remove(nPos);
669 void SwRedlineTable::Remove( size_type nP )
671 LOKRedlineNotification(RedlineNotification::Remove, maVector[nP]);
672 SwDoc* pDoc = nullptr;
673 if( !nP && 1 == size() )
674 pDoc = &maVector.front()->GetDoc();
676 maVector.erase( maVector.begin() + nP );
678 if( pDoc && !pDoc->IsInDtor() )
680 SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
681 if( pSh )
682 pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
686 void SwRedlineTable::DeleteAndDestroyAll()
688 while (!maVector.empty())
690 auto const pRedline = maVector.back();
691 maVector.erase_at(maVector.size() - 1);
692 LOKRedlineNotification(RedlineNotification::Remove, pRedline);
693 delete pRedline;
695 m_bHasOverlappingElements = false;
698 void SwRedlineTable::DeleteAndDestroy(size_type const nP)
700 auto const pRedline = maVector[nP];
701 maVector.erase(maVector.begin() + nP);
702 LOKRedlineNotification(RedlineNotification::Remove, pRedline);
703 delete pRedline;
706 SwRedlineTable::size_type SwRedlineTable::FindNextOfSeqNo( size_type nSttPos ) const
708 return nSttPos + 1 < size()
709 ? FindNextSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos+1 )
710 : npos;
713 SwRedlineTable::size_type SwRedlineTable::FindPrevOfSeqNo( size_type nSttPos ) const
715 return nSttPos ? FindPrevSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos-1 )
716 : npos;
719 /// Find the next or preceding Redline with the same seq.no.
720 /// We can limit the search using look ahead (0 searches the whole array).
721 SwRedlineTable::size_type SwRedlineTable::FindNextSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
723 auto constexpr nLookahead = 20;
724 size_type nRet = npos;
725 if( nSeqNo && nSttPos < size() )
727 size_type nEnd = size();
728 const size_type nTmp = nSttPos + nLookahead;
729 if (nTmp < nEnd)
731 nEnd = nTmp;
734 for( ; nSttPos < nEnd; ++nSttPos )
735 if( nSeqNo == operator[]( nSttPos )->GetSeqNo() )
737 nRet = nSttPos;
738 break;
741 return nRet;
744 SwRedlineTable::size_type SwRedlineTable::FindPrevSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
746 auto constexpr nLookahead = 20;
747 size_type nRet = npos;
748 if( nSeqNo && nSttPos < size() )
750 size_type nEnd = 0;
751 if( nSttPos > nLookahead )
752 nEnd = nSttPos - nLookahead;
754 ++nSttPos;
755 while( nSttPos > nEnd )
756 if( nSeqNo == operator[]( --nSttPos )->GetSeqNo() )
758 nRet = nSttPos;
759 break;
762 return nRet;
765 const SwRangeRedline* SwRedlineTable::FindAtPosition( const SwPosition& rSttPos,
766 size_type& rPos,
767 bool bNext ) const
769 const SwRangeRedline* pFnd = nullptr;
770 for( ; rPos < maVector.size() ; ++rPos )
772 const SwRangeRedline* pTmp = (*this)[ rPos ];
773 if( pTmp->HasMark() && pTmp->IsVisible() )
775 auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition*
776 if( bNext ? *pRStt <= rSttPos : *pRStt < rSttPos )
778 if( bNext ? *pREnd > rSttPos : *pREnd >= rSttPos )
780 pFnd = pTmp;
781 break;
784 else
785 break;
788 return pFnd;
791 bool SwRedlineTable::isMoved( size_type rPos ) const
793 bool bRet = false;
794 auto constexpr nLookahead = 20;
795 SwRangeRedline* pRedline = (*this)[ rPos ];
797 // set redline type of the searched pair
798 RedlineType nPairType = pRedline->GetType();
799 if ( RedlineType::Delete == nPairType )
800 nPairType = RedlineType::Insert;
801 else if ( RedlineType::Insert == nPairType )
802 nPairType = RedlineType::Delete;
803 else
804 // only deleted or inserted text can be moved
805 return false;
807 bool bDeletePaM = false;
808 SwPaM* pPaM;
810 // if this redline is visible the content is in this PaM
811 if ( nullptr == pRedline->GetContentIdx() )
813 pPaM = pRedline;
815 else // otherwise it is saved in pContentSect, e.g. during ODT import
817 pPaM = new SwPaM(pRedline->GetContentIdx()->GetNode(), *pRedline->GetContentIdx()->GetNode().EndOfSectionNode() );
818 bDeletePaM = true;
821 const OUString sTrimmed = pPaM->GetText().trim();
822 // detection of move needs at least 6 characters with an inner
823 // space after stripping white spaces of the redline to skip
824 // frequent deleted and inserted articles or other common
825 // word parts, e.g. 'the' and 'of a' to detect as text moving
826 if ( sTrimmed.getLength() < 6 || sTrimmed.indexOf(' ') == -1 )
828 if ( bDeletePaM )
829 delete pPaM;
830 return false;
833 // search pair around the actual redline
834 size_type nEnd = rPos + nLookahead < size()
835 ? rPos + nLookahead
836 : size();
837 rPos = rPos > nLookahead ? rPos - nLookahead : 0;
838 for ( ; rPos < nEnd && !bRet ; ++rPos )
840 SwRangeRedline* pPair = (*this)[ rPos ];
842 // redline must be the requested type and from the same author
843 if ( nPairType != pPair->GetType() ||
844 pRedline->GetAuthor() != pPair->GetAuthor() )
846 continue;
849 bool bDeletePairPaM = false;
850 SwPaM* pPairPaM;
852 // if this redline is visible the content is in this PaM
853 if ( nullptr == pPair->GetContentIdx() )
855 pPairPaM = pPair;
857 else // otherwise it is saved in pContentSect, e.g. during ODT import
859 // saved in pContentSect, e.g. during ODT import
860 pPairPaM = new SwPaM(pPair->GetContentIdx()->GetNode(), *pPair->GetContentIdx()->GetNode().EndOfSectionNode() );
861 bDeletePairPaM = true;
864 // pair at tracked moving: same text by trimming trailing white spaces
865 if ( abs(pPaM->GetText().getLength() - pPairPaM->GetText().getLength()) <= 2 &&
866 sTrimmed == o3tl::trim(pPairPaM->GetText()) )
868 pRedline->SetMoved();
869 pPair->SetMoved();
870 pPair->InvalidateRange(SwRangeRedline::Invalidation::Add);
871 bRet = true;
874 if ( bDeletePairPaM )
875 delete pPairPaM;
878 if ( bDeletePaM )
879 delete pPaM;
881 return bRet;
884 void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
886 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable"));
887 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
889 for (SwRedlineTable::size_type nCurRedlinePos = 0; nCurRedlinePos < size(); ++nCurRedlinePos)
890 operator[](nCurRedlinePos)->dumpAsXml(pWriter);
892 (void)xmlTextWriterEndElement(pWriter);
895 SwRedlineExtraData::~SwRedlineExtraData()
899 void SwRedlineExtraData::Reject( SwPaM& ) const
903 bool SwRedlineExtraData::operator == ( const SwRedlineExtraData& ) const
905 return false;
908 SwRedlineExtraData_FormatColl::SwRedlineExtraData_FormatColl( OUString aColl,
909 sal_uInt16 nPoolFormatId,
910 const SfxItemSet* pItemSet,
911 bool bFormatAll )
912 : m_sFormatNm(std::move(aColl)), m_nPoolId(nPoolFormatId), m_bFormatAll(bFormatAll)
914 if( pItemSet && pItemSet->Count() )
915 m_pSet.reset( new SfxItemSet( *pItemSet ) );
918 SwRedlineExtraData_FormatColl::~SwRedlineExtraData_FormatColl()
922 SwRedlineExtraData* SwRedlineExtraData_FormatColl::CreateNew() const
924 return new SwRedlineExtraData_FormatColl( m_sFormatNm, m_nPoolId, m_pSet.get(), m_bFormatAll );
927 void SwRedlineExtraData_FormatColl::Reject( SwPaM& rPam ) const
929 SwDoc& rDoc = rPam.GetDoc();
931 // What about Undo? Is it turned off?
932 SwTextFormatColl* pColl = USHRT_MAX == m_nPoolId
933 ? rDoc.FindTextFormatCollByName( m_sFormatNm )
934 : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId );
936 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
937 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
939 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
941 const SwPosition* pEnd = rPam.End();
943 if ( !m_bFormatAll || pEnd->GetContentIndex() == 0 )
945 // don't reject the format of the next paragraph (that is handled by the next redline)
946 if (aPam.GetPoint()->GetNode() > aPam.GetMark()->GetNode())
948 aPam.GetPoint()->Adjust(SwNodeOffset(-1));
949 SwContentNode* pNode = aPam.GetPoint()->GetNode().GetContentNode();
950 if ( pNode )
951 aPam.GetPoint()->SetContent( pNode->Len() );
952 else
953 // tdf#147507 set it back to a content node to avoid of crashing
954 aPam.GetPoint()->Adjust(SwNodeOffset(+1));
956 else if (aPam.GetPoint()->GetNode() < aPam.GetMark()->GetNode())
958 aPam.GetMark()->Adjust(SwNodeOffset(-1));
959 SwContentNode* pNode = aPam.GetMark()->GetNode().GetContentNode();
960 aPam.GetMark()->SetContent( pNode->Len() );
964 if( pColl )
965 rDoc.SetTextFormatColl( aPam, pColl, false );
967 if( m_pSet )
968 rDoc.getIDocumentContentOperations().InsertItemSet( aPam, *m_pSet );
970 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
973 bool SwRedlineExtraData_FormatColl::operator == ( const SwRedlineExtraData& r) const
975 const SwRedlineExtraData_FormatColl& rCmp = static_cast<const SwRedlineExtraData_FormatColl&>(r);
976 return m_sFormatNm == rCmp.m_sFormatNm && m_nPoolId == rCmp.m_nPoolId &&
977 m_bFormatAll == rCmp.m_bFormatAll &&
978 ( ( !m_pSet && !rCmp.m_pSet ) ||
979 ( m_pSet && rCmp.m_pSet && *m_pSet == *rCmp.m_pSet ) );
982 void SwRedlineExtraData_FormatColl::SetItemSet( const SfxItemSet& rSet )
984 if( rSet.Count() )
985 m_pSet.reset( new SfxItemSet( rSet ) );
986 else
987 m_pSet.reset();
990 SwRedlineExtraData_Format::SwRedlineExtraData_Format( const SfxItemSet& rSet )
992 SfxItemIter aIter( rSet );
993 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
995 m_aWhichIds.push_back( pItem->Which() );
999 SwRedlineExtraData_Format::SwRedlineExtraData_Format(
1000 const SwRedlineExtraData_Format& rCpy )
1001 : SwRedlineExtraData()
1003 m_aWhichIds.insert( m_aWhichIds.begin(), rCpy.m_aWhichIds.begin(), rCpy.m_aWhichIds.end() );
1006 SwRedlineExtraData_Format::~SwRedlineExtraData_Format()
1010 SwRedlineExtraData* SwRedlineExtraData_Format::CreateNew() const
1012 return new SwRedlineExtraData_Format( *this );
1015 void SwRedlineExtraData_Format::Reject( SwPaM& rPam ) const
1017 SwDoc& rDoc = rPam.GetDoc();
1019 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1020 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
1022 // Actually we need to reset the Attribute here!
1023 for( const auto& rWhichId : m_aWhichIds )
1025 rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, *GetDfltAttr( rWhichId ),
1026 SetAttrMode::DONTEXPAND );
1029 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1032 bool SwRedlineExtraData_Format::operator == ( const SwRedlineExtraData& rCmp ) const
1034 const size_t nEnd = m_aWhichIds.size();
1035 if( nEnd != static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds.size() )
1036 return false;
1038 for( size_t n = 0; n < nEnd; ++n )
1040 if( static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds[n] != m_aWhichIds[n])
1042 return false;
1045 return true;
1048 SwRedlineData::SwRedlineData( RedlineType eT, std::size_t nAut )
1049 : m_pNext( nullptr ), m_pExtraData( nullptr ),
1050 m_aStamp( DateTime::SYSTEM ),
1051 m_nAuthor( nAut ), m_eType( eT ), m_nSeqNo( 0 ), m_bAutoFormat(false), m_bMoved(false)
1053 m_aStamp.SetNanoSec( 0 );
1056 SwRedlineData::SwRedlineData(
1057 const SwRedlineData& rCpy,
1058 bool bCpyNext )
1059 : m_pNext( ( bCpyNext && rCpy.m_pNext ) ? new SwRedlineData( *rCpy.m_pNext ) : nullptr )
1060 , m_pExtraData( rCpy.m_pExtraData ? rCpy.m_pExtraData->CreateNew() : nullptr )
1061 , m_sComment( rCpy.m_sComment )
1062 , m_aStamp( rCpy.m_aStamp )
1063 , m_nAuthor( rCpy.m_nAuthor )
1064 , m_eType( rCpy.m_eType )
1065 , m_nSeqNo( rCpy.m_nSeqNo )
1066 , m_bAutoFormat(false)
1067 , m_bMoved( rCpy.m_bMoved )
1071 // For sw3io: We now own pNext!
1072 SwRedlineData::SwRedlineData(RedlineType eT, std::size_t nAut, const DateTime& rDT,
1073 OUString aCmnt, SwRedlineData *pNxt)
1074 : m_pNext(pNxt), m_pExtraData(nullptr), m_sComment(std::move(aCmnt)), m_aStamp(rDT),
1075 m_nAuthor(nAut), m_eType(eT), m_nSeqNo(0), m_bAutoFormat(false), m_bMoved(false)
1079 SwRedlineData::~SwRedlineData()
1081 delete m_pExtraData;
1082 delete m_pNext;
1085 // Check whether the absolute difference between the two dates is no larger than one minute (can
1086 // give inaccurate results if at least one of the dates is not valid/normalized):
1087 static bool deltaOneMinute(DateTime const & t1, DateTime const & t2) {
1088 auto const & [min, max] = std::minmax(t1, t2);
1089 // Avoid overflow of `min + tools::Time(0, 1)` below when min is close to the maximum valid
1090 // DateTime:
1091 if (min >= DateTime({31, 12, std::numeric_limits<sal_Int16>::max()}, {23, 59})) {
1092 return true;
1094 return max <= min + tools::Time(0, 1);
1097 bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const
1099 return m_nAuthor == rCmp.m_nAuthor &&
1100 m_eType == rCmp.m_eType &&
1101 m_sComment == rCmp.m_sComment &&
1102 deltaOneMinute(GetTimeStamp(), rCmp.GetTimeStamp()) &&
1103 m_bMoved == rCmp.m_bMoved &&
1104 (( !m_pNext && !rCmp.m_pNext ) ||
1105 ( m_pNext && rCmp.m_pNext &&
1106 m_pNext->CanCombine( *rCmp.m_pNext ))) &&
1107 (( !m_pExtraData && !rCmp.m_pExtraData ) ||
1108 ( m_pExtraData && rCmp.m_pExtraData &&
1109 *m_pExtraData == *rCmp.m_pExtraData ));
1112 /// ExtraData is copied. The Pointer's ownership is thus NOT transferred
1113 /// to the Redline Object!
1114 void SwRedlineData::SetExtraData( const SwRedlineExtraData* pData )
1116 delete m_pExtraData;
1118 // Check if there is data - and if so - delete it
1119 if( pData )
1120 m_pExtraData = pData->CreateNew();
1121 else
1122 m_pExtraData = nullptr;
1125 const TranslateId STR_REDLINE_ARY[] =
1127 STR_UNDO_REDLINE_INSERT,
1128 STR_UNDO_REDLINE_DELETE,
1129 STR_UNDO_REDLINE_FORMAT,
1130 STR_UNDO_REDLINE_TABLE,
1131 STR_UNDO_REDLINE_FMTCOLL,
1132 STR_UNDO_REDLINE_PARAGRAPH_FORMAT,
1133 STR_UNDO_REDLINE_TABLE_ROW_INSERT,
1134 STR_UNDO_REDLINE_TABLE_ROW_DELETE,
1135 STR_UNDO_REDLINE_TABLE_CELL_INSERT,
1136 STR_UNDO_REDLINE_TABLE_CELL_DELETE
1139 OUString SwRedlineData::GetDescr() const
1141 return SwResId(STR_REDLINE_ARY[static_cast<int>(GetType())]);
1144 sal_uInt32 SwRangeRedline::s_nLastId = 1;
1146 SwRangeRedline::SwRangeRedline(RedlineType eTyp, const SwPaM& rPam )
1147 : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
1148 m_pRedlineData( new SwRedlineData( eTyp, GetDoc().getIDocumentRedlineAccess().GetRedlineAuthor() ) ),
1149 m_nId( s_nLastId++ )
1151 GetBound().SetRedline(this);
1152 GetBound(false).SetRedline(this);
1154 m_bDelLastPara = false;
1155 m_bIsVisible = true;
1156 if( !rPam.HasMark() )
1157 DeleteMark();
1159 // set default comment for single annotations added or deleted
1160 if ( IsAnnotation() )
1162 SetComment( RedlineType::Delete == eTyp
1163 ? SwResId(STR_REDLINE_COMMENT_DELETED)
1164 : SwResId(STR_REDLINE_COMMENT_ADDED) );
1168 SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPaM& rPam )
1169 : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
1170 m_pRedlineData( new SwRedlineData( rData )),
1171 m_nId( s_nLastId++ )
1173 GetBound().SetRedline(this);
1174 GetBound(false).SetRedline(this);
1176 m_bDelLastPara = false;
1177 m_bIsVisible = true;
1178 if( !rPam.HasMark() )
1179 DeleteMark();
1182 SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPosition& rPos )
1183 : SwPaM( rPos ),
1184 m_pRedlineData( new SwRedlineData( rData )),
1185 m_nId( s_nLastId++ )
1187 GetBound().SetRedline(this);
1188 GetBound(false).SetRedline(this);
1190 m_bDelLastPara = false;
1191 m_bIsVisible = true;
1194 SwRangeRedline::SwRangeRedline( const SwRangeRedline& rCpy )
1195 : SwPaM( *rCpy.GetMark(), *rCpy.GetPoint() ),
1196 m_pRedlineData( new SwRedlineData( *rCpy.m_pRedlineData )),
1197 m_nId( s_nLastId++ )
1199 GetBound().SetRedline(this);
1200 GetBound(false).SetRedline(this);
1202 m_bDelLastPara = false;
1203 m_bIsVisible = true;
1204 if( !rCpy.HasMark() )
1205 DeleteMark();
1208 SwRangeRedline::~SwRangeRedline()
1210 if( m_oContentSect )
1212 // delete the ContentSection
1213 if( !GetDoc().IsInDtor() )
1214 GetDoc().getIDocumentContentOperations().DeleteSection( &m_oContentSect->GetNode() );
1215 m_oContentSect.reset();
1217 delete m_pRedlineData;
1220 void MaybeNotifyRedlineModification(SwRangeRedline& rRedline, SwDoc& rDoc)
1222 if (!lcl_LOKRedlineNotificationEnabled())
1223 return;
1225 const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1226 for (SwRedlineTable::size_type i = 0; i < rRedTable.size(); ++i)
1228 if (rRedTable[i] == &rRedline)
1230 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, &rRedline);
1231 break;
1236 void SwRangeRedline::MaybeNotifyRedlinePositionModification(tools::Long nTop)
1238 if (!lcl_LOKRedlineNotificationEnabled())
1239 return;
1241 if(!m_oLOKLastNodeTop || *m_oLOKLastNodeTop != nTop)
1243 m_oLOKLastNodeTop = nTop;
1244 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, this);
1248 void SwRangeRedline::SetStart( const SwPosition& rPos, SwPosition* pSttPtr )
1250 if( !pSttPtr ) pSttPtr = Start();
1251 *pSttPtr = rPos;
1253 MaybeNotifyRedlineModification(*this, GetDoc());
1256 void SwRangeRedline::SetEnd( const SwPosition& rPos, SwPosition* pEndPtr )
1258 if( !pEndPtr ) pEndPtr = End();
1259 *pEndPtr = rPos;
1261 MaybeNotifyRedlineModification(*this, GetDoc());
1264 /// Do we have a valid Selection?
1265 bool SwRangeRedline::HasValidRange() const
1267 const SwNode* pPtNd = &GetPoint()->GetNode(),
1268 * pMkNd = &GetMark()->GetNode();
1269 if( pPtNd->StartOfSectionNode() == pMkNd->StartOfSectionNode() &&
1270 !pPtNd->StartOfSectionNode()->IsTableNode() &&
1271 // invalid if points on the end of content
1272 // end-of-content only invalid if no content index exists
1273 ( pPtNd != pMkNd || GetContentIdx() != nullptr ||
1274 pPtNd != &pPtNd->GetNodes().GetEndOfContent() )
1276 return true;
1277 return false;
1280 void SwRangeRedline::CallDisplayFunc(size_t nMyPos)
1282 RedlineFlags eShow = RedlineFlags::ShowMask & GetDoc().getIDocumentRedlineAccess().GetRedlineFlags();
1283 if (eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1284 Show(0, nMyPos);
1285 else if (eShow == RedlineFlags::ShowInsert)
1286 Hide(0, nMyPos);
1287 else if (eShow == RedlineFlags::ShowDelete)
1288 ShowOriginal(0, nMyPos);
1291 void SwRangeRedline::Show(sal_uInt16 nLoop, size_t nMyPos, bool bForced)
1293 SwDoc& rDoc = GetDoc();
1295 bool bIsShowChangesInMargin = false;
1296 if ( !bForced )
1298 SwViewShell* pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
1299 if (pSh)
1300 bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
1301 else
1302 bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
1305 if( 1 > nLoop && !bIsShowChangesInMargin )
1306 return;
1308 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1309 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1310 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1312 switch( GetType() )
1314 case RedlineType::Insert: // Content has been inserted
1315 m_bIsVisible = true;
1316 MoveFromSection(nMyPos);
1317 break;
1319 case RedlineType::Delete: // Content has been deleted
1320 m_bIsVisible = !bIsShowChangesInMargin;
1322 if (m_bIsVisible)
1323 MoveFromSection(nMyPos);
1324 else
1326 switch( nLoop )
1328 case 0: MoveToSection(); break;
1329 case 1: CopyToSection(); break;
1330 case 2: DelCopyOfSection(nMyPos); break;
1333 break;
1335 case RedlineType::Format: // Attributes have been applied
1336 case RedlineType::Table: // Table structure has been modified
1337 InvalidateRange(Invalidation::Add);
1338 break;
1339 default:
1340 break;
1342 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1345 void SwRangeRedline::Hide(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
1347 SwDoc& rDoc = GetDoc();
1348 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1349 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1350 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1352 switch( GetType() )
1354 case RedlineType::Insert: // Content has been inserted
1355 m_bIsVisible = true;
1356 if( 1 <= nLoop )
1357 MoveFromSection(nMyPos);
1358 break;
1360 case RedlineType::Delete: // Content has been deleted
1361 m_bIsVisible = false;
1362 switch( nLoop )
1364 case 0: MoveToSection(); break;
1365 case 1: CopyToSection(); break;
1366 case 2: DelCopyOfSection(nMyPos); break;
1368 break;
1370 case RedlineType::Format: // Attributes have been applied
1371 case RedlineType::Table: // Table structure has been modified
1372 if( 1 <= nLoop )
1373 InvalidateRange(Invalidation::Remove);
1374 break;
1375 default:
1376 break;
1378 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1381 void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
1383 SwDoc& rDoc = GetDoc();
1384 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
1385 SwRedlineData* pCur;
1387 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1388 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1390 // Determine the Type, it's the first on Stack
1391 for( pCur = m_pRedlineData; pCur->m_pNext; )
1392 pCur = pCur->m_pNext;
1394 switch( pCur->m_eType )
1396 case RedlineType::Insert: // Content has been inserted
1397 m_bIsVisible = false;
1398 switch( nLoop )
1400 case 0: MoveToSection(); break;
1401 case 1: CopyToSection(); break;
1402 case 2: DelCopyOfSection(nMyPos); break;
1404 break;
1406 case RedlineType::Delete: // Content has been deleted
1407 m_bIsVisible = true;
1408 if( 1 <= nLoop )
1409 MoveFromSection(nMyPos);
1410 break;
1412 case RedlineType::Format: // Attributes have been applied
1413 case RedlineType::Table: // Table structure has been modified
1414 if( 1 <= nLoop )
1415 InvalidateRange(Invalidation::Remove);
1416 break;
1417 default:
1418 break;
1420 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1423 // trigger the Layout
1424 void SwRangeRedline::InvalidateRange(Invalidation const eWhy)
1426 auto [pRStt, pREnd] = StartEnd(); // SwPosition*
1427 SwNodeOffset nSttNd = pRStt->GetNodeIndex(),
1428 nEndNd = pREnd->GetNodeIndex();
1429 sal_Int32 nSttCnt = pRStt->GetContentIndex();
1430 sal_Int32 nEndCnt = pREnd->GetContentIndex();
1432 SwNodes& rNds = GetDoc().GetNodes();
1433 for (SwNodeOffset n(nSttNd); n <= nEndNd; ++n)
1435 SwNode* pNode = rNds[n];
1437 if (pNode && pNode->IsTextNode())
1439 SwTextNode* pNd = pNode->GetTextNode();
1441 SwUpdateAttr aHt(
1442 n == nSttNd ? nSttCnt : 0,
1443 n == nEndNd ? nEndCnt : pNd->GetText().getLength(),
1444 RES_FMT_CHG);
1446 pNd->TriggerNodeUpdate(sw::LegacyModifyHint(&aHt, &aHt));
1448 // SwUpdateAttr must be handled first, otherwise indexes are off
1449 if (GetType() == RedlineType::Delete)
1451 sal_Int32 const nStart(n == nSttNd ? nSttCnt : 0);
1452 sal_Int32 const nLen((n == nEndNd ? nEndCnt : pNd->GetText().getLength()) - nStart);
1453 if (eWhy == Invalidation::Add)
1455 sw::RedlineDelText const hint(nStart, nLen);
1456 pNd->CallSwClientNotify(hint);
1458 else
1460 sw::RedlineUnDelText const hint(nStart, nLen);
1461 pNd->CallSwClientNotify(hint);
1468 /** Calculates the start and end position of the intersection rTmp and
1469 text node nNdIdx */
1470 void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, sal_Int32& rStart, sal_Int32& rEnd ) const
1472 auto [pRStt, pREnd] = StartEnd(); // SwPosition*
1473 if( pRStt->GetNodeIndex() < nNdIdx )
1475 if( pREnd->GetNodeIndex() > nNdIdx )
1477 rStart = 0; // Paragraph is completely enclosed
1478 rEnd = COMPLETE_STRING;
1480 else if (pREnd->GetNodeIndex() == nNdIdx)
1482 rStart = 0; // Paragraph is overlapped in the beginning
1483 rEnd = pREnd->GetContentIndex();
1485 else // redline ends before paragraph
1487 rStart = COMPLETE_STRING;
1488 rEnd = COMPLETE_STRING;
1491 else if( pRStt->GetNodeIndex() == nNdIdx )
1493 rStart = pRStt->GetContentIndex();
1494 if( pREnd->GetNodeIndex() == nNdIdx )
1495 rEnd = pREnd->GetContentIndex(); // Within the Paragraph
1496 else
1497 rEnd = COMPLETE_STRING; // Paragraph is overlapped in the end
1499 else
1501 rStart = COMPLETE_STRING;
1502 rEnd = COMPLETE_STRING;
1506 static void lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStt, const SwPosition* pEnd)
1508 // tdf#115815 keep original start position of collapsed annotation ranges
1509 // as temporary bookmarks (removed after file saving and file loading)
1510 IDocumentMarkAccess& rDMA(*rDoc.getIDocumentMarkAccess());
1511 for (auto iter = rDMA.getAnnotationMarksBegin();
1512 iter != rDMA.getAnnotationMarksEnd(); )
1514 SwPosition const& rStartPos((**iter).GetMarkStart());
1515 if ( *pStt <= rStartPos && rStartPos < *pEnd )
1517 IDocumentMarkAccess::const_iterator_t pOldMark =
1518 rDMA.findAnnotationBookmark((**iter).GetName());
1519 if ( pOldMark == rDMA.getBookmarksEnd() )
1521 // at start of redlines use a 1-character length bookmark range
1522 // instead of a 0-character length bookmark position to avoid its losing
1523 sal_Int32 nLen = (*pStt == rStartPos) ? 1 : 0;
1524 SwPaM aPam( rStartPos.GetNode(), rStartPos.GetContentIndex(),
1525 rStartPos.GetNode(), rStartPos.GetContentIndex() + nLen);
1526 ::sw::mark::IMark* pMark = rDMA.makeAnnotationBookmark(
1527 aPam,
1528 (**iter).GetName(),
1529 IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
1530 ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
1531 if (pBookmark)
1533 pBookmark->SetKeyCode(vcl::KeyCode());
1534 pBookmark->SetShortName(OUString());
1538 ++iter;
1542 void SwRangeRedline::MoveToSection()
1544 if( !m_oContentSect )
1546 auto [pStt, pEnd] = StartEnd(); // SwPosition*
1548 SwDoc& rDoc = GetDoc();
1549 SwPaM aPam( *pStt, *pEnd );
1550 SwContentNode* pCSttNd = pStt->GetNode().GetContentNode();
1551 SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
1553 if( !pCSttNd )
1555 // In order to not move other Redlines' indices, we set them
1556 // to the end (is exclusive)
1557 const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1558 for(SwRangeRedline* pRedl : rTable)
1560 if( pRedl->GetBound() == *pStt )
1561 pRedl->GetBound() = *pEnd;
1562 if( pRedl->GetBound(false) == *pStt )
1563 pRedl->GetBound(false) = *pEnd;
1567 SwStartNode* pSttNd;
1568 SwNodes& rNds = rDoc.GetNodes();
1569 if( pCSttNd || pCEndNd )
1571 SwTextFormatColl* pColl = (pCSttNd && pCSttNd->IsTextNode() )
1572 ? pCSttNd->GetTextNode()->GetTextColl()
1573 : (pCEndNd && pCEndNd->IsTextNode() )
1574 ? pCEndNd->GetTextNode()->GetTextColl()
1575 : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
1577 pSttNd = rNds.MakeTextSection( rNds.GetEndOfRedlines(),
1578 SwNormalStartNode, pColl );
1579 SwTextNode* pTextNd = rNds[ pSttNd->GetIndex() + 1 ]->GetTextNode();
1581 SwPosition aPos( *pTextNd );
1582 if( pCSttNd && pCEndNd )
1584 // tdf#140982 keep annotation ranges in deletions in margin mode
1585 lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
1586 rDoc.getIDocumentContentOperations().MoveAndJoin( aPam, aPos );
1588 else
1590 if( pCSttNd && !pCEndNd )
1591 m_bDelLastPara = true;
1592 rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
1593 SwMoveFlags::DEFAULT );
1596 else
1598 pSttNd = SwNodes::MakeEmptySection( rNds.GetEndOfRedlines() );
1600 SwPosition aPos( *pSttNd->EndOfSectionNode() );
1601 rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
1602 SwMoveFlags::DEFAULT );
1604 m_oContentSect.emplace( *pSttNd );
1606 if( pStt == GetPoint() )
1607 Exchange();
1609 DeleteMark();
1611 else
1612 InvalidateRange(Invalidation::Remove);
1615 void SwRangeRedline::CopyToSection()
1617 if( m_oContentSect )
1618 return;
1620 auto [pStt, pEnd] = StartEnd(); // SwPosition*
1622 SwContentNode* pCSttNd = pStt->GetNode().GetContentNode();
1623 SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
1625 SwStartNode* pSttNd;
1626 SwDoc& rDoc = GetDoc();
1627 SwNodes& rNds = rDoc.GetNodes();
1629 bool bSaveCopyFlag = rDoc.IsCopyIsMove(),
1630 bSaveRdlMoveFlg = rDoc.getIDocumentRedlineAccess().IsRedlineMove();
1631 rDoc.SetCopyIsMove( true );
1633 // The IsRedlineMove() flag causes the behaviour of the
1634 // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change,
1635 // which will eventually be called by the CopyRange() below.
1636 rDoc.getIDocumentRedlineAccess().SetRedlineMove(true);
1638 if( pCSttNd )
1640 SwTextFormatColl* pColl = pCSttNd->IsTextNode()
1641 ? pCSttNd->GetTextNode()->GetTextColl()
1642 : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
1644 pSttNd = rNds.MakeTextSection( rNds.GetEndOfRedlines(),
1645 SwNormalStartNode, pColl );
1647 SwPosition aPos( *pSttNd, SwNodeOffset(1) );
1649 // tdf#115815 keep original start position of collapsed annotation ranges
1650 // as temporary bookmarks (removed after file saving and file loading)
1651 lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
1652 rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
1654 // Take over the style from the EndNode if needed
1655 // We don't want this in Doc::Copy
1656 if( pCEndNd && pCEndNd != pCSttNd )
1658 SwContentNode* pDestNd = aPos.GetNode().GetContentNode();
1659 if( pDestNd )
1661 if( pDestNd->IsTextNode() && pCEndNd->IsTextNode() )
1662 pCEndNd->GetTextNode()->CopyCollFormat(*pDestNd->GetTextNode());
1663 else
1664 pDestNd->ChgFormatColl( pCEndNd->GetFormatColl() );
1668 else
1670 pSttNd = SwNodes::MakeEmptySection( rNds.GetEndOfRedlines() );
1672 if( pCEndNd )
1674 SwPosition aPos( *pSttNd->EndOfSectionNode() );
1675 rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
1677 else
1679 SwNodeRange aRg( pStt->GetNode(), SwNodeOffset(0), pEnd->GetNode(), SwNodeOffset(1) );
1680 rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, *pSttNd->EndOfSectionNode());
1683 m_oContentSect.emplace( *pSttNd );
1685 rDoc.SetCopyIsMove( bSaveCopyFlag );
1686 rDoc.getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg );
1689 void SwRangeRedline::DelCopyOfSection(size_t nMyPos)
1691 if( !m_oContentSect )
1692 return;
1694 auto [pStt, pEnd] = StartEnd(); // SwPosition*
1696 SwDoc& rDoc = GetDoc();
1697 SwPaM aPam( *pStt, *pEnd );
1698 SwContentNode* pCSttNd = pStt->GetNode().GetContentNode();
1699 SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
1701 if( !pCSttNd )
1703 // In order to not move other Redlines' indices, we set them
1704 // to the end (is exclusive)
1705 const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1706 for(SwRangeRedline* pRedl : rTable)
1708 if( pRedl->GetBound() == *pStt )
1709 pRedl->GetBound() = *pEnd;
1710 if( pRedl->GetBound(false) == *pStt )
1711 pRedl->GetBound(false) = *pEnd;
1715 if( pCSttNd && pCEndNd )
1717 // #i100466# - force a <join next> on <delete and join> operation
1718 // tdf#125319 - rather not?
1719 rDoc.getIDocumentContentOperations().DeleteAndJoin(aPam/*, true*/);
1721 else if( pCSttNd || pCEndNd )
1723 if( pCSttNd && !pCEndNd )
1724 m_bDelLastPara = true;
1725 rDoc.getIDocumentContentOperations().DeleteRange( aPam );
1727 if( m_bDelLastPara )
1729 // To prevent dangling references to the paragraph to
1730 // be deleted, redline that point into this paragraph should be
1731 // moved to the new end position. Since redlines in the redline
1732 // table are sorted and the pEnd position is an endnode (see
1733 // bDelLastPara condition above), only redlines before the
1734 // current ones can be affected.
1735 const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1736 size_t n = nMyPos;
1737 for( bool bBreak = false; !bBreak && n > 0; )
1739 --n;
1740 bBreak = true;
1741 if( rTable[ n ]->GetBound() == *aPam.GetPoint() )
1743 rTable[ n ]->GetBound() = *pEnd;
1744 bBreak = false;
1746 if( rTable[ n ]->GetBound(false) == *aPam.GetPoint() )
1748 rTable[ n ]->GetBound(false) = *pEnd;
1749 bBreak = false;
1753 *GetPoint() = *pEnd;
1754 *GetMark() = *pEnd;
1755 DeleteMark();
1757 aPam.DeleteMark();
1758 aPam.GetPoint()->SetContent(0);;
1759 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
1762 else
1764 rDoc.getIDocumentContentOperations().DeleteRange( aPam );
1767 if( pStt == GetPoint() )
1768 Exchange();
1770 DeleteMark();
1773 void SwRangeRedline::MoveFromSection(size_t nMyPos)
1775 if( m_oContentSect )
1777 SwDoc& rDoc = GetDoc();
1778 const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1779 std::vector<SwPosition*> aBeforeArr, aBehindArr;
1780 bool bBreak = false;
1781 SwRedlineTable::size_type n;
1783 for( n = nMyPos+1; !bBreak && n < rTable.size(); ++n )
1785 bBreak = true;
1786 if( rTable[ n ]->GetBound() == *GetPoint() )
1788 SwRangeRedline* pRedl = rTable[n];
1789 aBehindArr.push_back(&pRedl->GetBound());
1790 bBreak = false;
1792 if( rTable[ n ]->GetBound(false) == *GetPoint() )
1794 SwRangeRedline* pRedl = rTable[n];
1795 aBehindArr.push_back(&pRedl->GetBound(false));
1796 bBreak = false;
1799 for( bBreak = false, n = nMyPos; !bBreak && n ; )
1801 --n;
1802 bBreak = true;
1803 if( rTable[ n ]->GetBound() == *GetPoint() )
1805 SwRangeRedline* pRedl = rTable[n];
1806 aBeforeArr.push_back(&pRedl->GetBound());
1807 bBreak = false;
1809 if( rTable[ n ]->GetBound(false) == *GetPoint() )
1811 SwRangeRedline* pRedl = rTable[n];
1812 aBeforeArr.push_back(&pRedl->GetBound(false));
1813 bBreak = false;
1817 const SwNode* pKeptContentSectNode( &m_oContentSect->GetNode() ); // #i95711#
1819 SwPaM aPam( m_oContentSect->GetNode(),
1820 *m_oContentSect->GetNode().EndOfSectionNode(), SwNodeOffset(1),
1821 SwNodeOffset( m_bDelLastPara ? -2 : -1 ) );
1822 SwContentNode* pCNd = aPam.GetPointContentNode();
1823 if( pCNd )
1824 aPam.GetPoint()->SetContent( pCNd->Len() );
1825 else
1826 aPam.GetPoint()->Adjust(SwNodeOffset(+1));
1828 SwFormatColl* pColl = pCNd && pCNd->Len() && aPam.GetPoint()->GetNode() !=
1829 aPam.GetMark()->GetNode()
1830 ? pCNd->GetFormatColl() : nullptr;
1832 SwNodeIndex aNdIdx( GetPoint()->GetNode(), -1 );
1833 const sal_Int32 nPos = GetPoint()->GetContentIndex();
1835 SwPosition aPos( *GetPoint() );
1836 if( m_bDelLastPara && *aPam.GetPoint() == *aPam.GetMark() )
1838 aPos.Adjust(SwNodeOffset(-1));
1840 rDoc.getIDocumentContentOperations().AppendTextNode( aPos );
1842 else
1844 rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
1845 SwMoveFlags::ALLFLYS );
1848 SetMark();
1849 *GetPoint() = aPos;
1850 GetMark()->Assign(aNdIdx.GetIndex() + 1);
1851 pCNd = GetMark()->GetNode().GetContentNode();
1852 if( pCNd )
1853 GetMark()->SetContent( nPos );
1855 if( m_bDelLastPara )
1857 GetPoint()->Adjust(SwNodeOffset(+1));
1858 pCNd = GetPointContentNode();
1859 m_bDelLastPara = false;
1861 else if( pColl )
1862 pCNd = GetPointContentNode();
1864 if( pColl && pCNd )
1865 pCNd->ChgFormatColl( pColl );
1868 // #i95771#
1869 // Under certain conditions the previous <SwDoc::Move(..)> has already
1870 // removed the change tracking section of this <SwRangeRedline> instance from
1871 // the change tracking nodes area.
1872 // Thus, check if <pContentSect> still points to the change tracking section
1873 // by comparing it with the "indexed" <SwNode> instance copied before
1874 // perform the intrinsic move.
1875 // Note: Such condition is e.g. a "delete" change tracking only containing a table.
1876 if ( &m_oContentSect->GetNode() == pKeptContentSectNode )
1878 rDoc.getIDocumentContentOperations().DeleteSection( &m_oContentSect->GetNode() );
1880 m_oContentSect.reset();
1882 // adjustment of redline table positions must take start and
1883 // end into account, not point and mark.
1884 for( auto& pItem : aBeforeArr )
1885 *pItem = *Start();
1886 for( auto& pItem : aBehindArr )
1887 *pItem = *End();
1889 else
1890 InvalidateRange(Invalidation::Add);
1893 // for Undo
1894 void SwRangeRedline::SetContentIdx( const SwNodeIndex& rIdx )
1896 if( !m_oContentSect )
1898 m_oContentSect = rIdx;
1899 m_bIsVisible = false;
1901 else
1903 OSL_FAIL("SwRangeRedline::SetContentIdx: invalid state");
1907 // for Undo
1908 void SwRangeRedline::ClearContentIdx()
1910 if( m_oContentSect )
1912 m_oContentSect.reset();
1914 else
1916 OSL_FAIL("SwRangeRedline::ClearContentIdx: invalid state");
1920 bool SwRangeRedline::CanCombine( const SwRangeRedline& rRedl ) const
1922 return IsVisible() && rRedl.IsVisible() &&
1923 m_pRedlineData->CanCombine( *rRedl.m_pRedlineData );
1926 void SwRangeRedline::PushData( const SwRangeRedline& rRedl, bool bOwnAsNext )
1928 SwRedlineData* pNew = new SwRedlineData( *rRedl.m_pRedlineData, false );
1929 if( bOwnAsNext )
1931 pNew->m_pNext = m_pRedlineData;
1932 m_pRedlineData = pNew;
1934 else
1936 pNew->m_pNext = m_pRedlineData->m_pNext;
1937 m_pRedlineData->m_pNext = pNew;
1941 bool SwRangeRedline::PopData()
1943 if( !m_pRedlineData->m_pNext )
1944 return false;
1945 SwRedlineData* pCur = m_pRedlineData;
1946 m_pRedlineData = pCur->m_pNext;
1947 pCur->m_pNext = nullptr;
1948 delete pCur;
1949 return true;
1952 sal_uInt16 SwRangeRedline::GetStackCount() const
1954 sal_uInt16 nRet = 1;
1955 for( SwRedlineData* pCur = m_pRedlineData; pCur->m_pNext; pCur = pCur->m_pNext )
1956 ++nRet;
1957 return nRet;
1960 std::size_t SwRangeRedline::GetAuthor( sal_uInt16 nPos ) const
1962 return GetRedlineData(nPos).m_nAuthor;
1965 OUString const & SwRangeRedline::GetAuthorString( sal_uInt16 nPos ) const
1967 return SW_MOD()->GetRedlineAuthor(GetRedlineData(nPos).m_nAuthor);
1970 const DateTime& SwRangeRedline::GetTimeStamp( sal_uInt16 nPos ) const
1972 return GetRedlineData(nPos).m_aStamp;
1975 RedlineType SwRangeRedline::GetType( sal_uInt16 nPos ) const
1977 return GetRedlineData(nPos).m_eType;
1980 bool SwRangeRedline::IsAnnotation() const
1982 return GetText().getLength() == 1 && GetText()[0] == CH_TXTATR_INWORD;
1985 const OUString& SwRangeRedline::GetComment( sal_uInt16 nPos ) const
1987 return GetRedlineData(nPos).m_sComment;
1990 bool SwRangeRedline::operator<( const SwRangeRedline& rCmp ) const
1992 if (*Start() < *rCmp.Start())
1993 return true;
1995 return *Start() == *rCmp.Start() && *End() < *rCmp.End();
1998 const SwRedlineData & SwRangeRedline::GetRedlineData(const sal_uInt16 nPos) const
2000 SwRedlineData * pCur = m_pRedlineData;
2002 sal_uInt16 nP = nPos;
2004 while (nP > 0 && nullptr != pCur->m_pNext)
2006 pCur = pCur->m_pNext;
2008 nP--;
2011 SAL_WARN_IF( nP != 0, "sw.core", "Pos " << nPos << " is " << nP << " too big");
2013 return *pCur;
2016 OUString SwRangeRedline::GetDescr(bool bSimplified)
2018 // get description of redline data (e.g.: "insert $1")
2019 OUString aResult = GetRedlineData().GetDescr();
2021 SwPaM * pPaM = nullptr;
2022 bool bDeletePaM = false;
2024 // if this redline is visible the content is in this PaM
2025 if (!m_oContentSect.has_value())
2027 pPaM = this;
2029 else // otherwise it is saved in pContentSect
2031 pPaM = new SwPaM( m_oContentSect->GetNode(), *m_oContentSect->GetNode().EndOfSectionNode() );
2032 bDeletePaM = true;
2035 OUString sDescr = DenoteSpecialCharacters(pPaM->GetText().replace('\n', ' '), /*bQuoted=*/!bSimplified);
2036 if (const SwTextNode *pTextNode = pPaM->GetPointNode().GetTextNode())
2038 if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default))
2040 sDescr = ( bSimplified ? "" : SwResId(STR_START_QUOTE) )
2041 + pTextAttr->GetFormatField().GetField()->GetFieldName()
2042 + ( bSimplified ? "" : SwResId(STR_END_QUOTE) );
2046 // replace $1 in description by description of the redlines text
2047 const OUString aTmpStr = ShortenString(sDescr, nUndoStringLength, SwResId(STR_LDOTS));
2049 if (!bSimplified)
2051 SwRewriter aRewriter;
2052 aRewriter.AddRule(UndoArg1, aTmpStr);
2054 aResult = aRewriter.Apply(aResult);
2056 else
2058 aResult = aTmpStr;
2059 // more shortening
2060 sal_Int32 nPos = aTmpStr.indexOf(SwResId(STR_LDOTS));
2061 if (nPos > 5)
2062 aResult = aTmpStr.copy(0, nPos + SwResId(STR_LDOTS).getLength());
2065 if (bDeletePaM)
2066 delete pPaM;
2068 return aResult;
2071 void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter) const
2073 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRangeRedline"));
2075 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
2076 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr()));
2077 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("author"), BAD_CAST(SW_MOD()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr()));
2078 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr()));
2079 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("descr"), BAD_CAST(const_cast<SwRangeRedline*>(this)->GetDescr().toUtf8().getStr()));
2081 OString sRedlineType;
2082 switch (GetType())
2084 case RedlineType::Insert:
2085 sRedlineType = "REDLINE_INSERT";
2086 break;
2087 case RedlineType::Delete:
2088 sRedlineType = "REDLINE_DELETE";
2089 break;
2090 case RedlineType::Format:
2091 sRedlineType = "REDLINE_FORMAT";
2092 break;
2093 default:
2094 sRedlineType = "UNKNOWN";
2095 break;
2097 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(sRedlineType.getStr()));
2099 SwPaM::dumpAsXml(pWriter);
2101 (void)xmlTextWriterEndElement(pWriter);
2104 void SwExtraRedlineTable::Insert( SwExtraRedline* p )
2106 m_aExtraRedlines.push_back( p );
2107 //p->CallDisplayFunc();
2110 void SwExtraRedlineTable::DeleteAndDestroy(sal_uInt16 const nPos)
2113 SwDoc* pDoc = 0;
2114 if( !nP && nL && nL == size() )
2115 pDoc = front()->GetDoc();
2118 delete m_aExtraRedlines[nPos];
2119 m_aExtraRedlines.erase(m_aExtraRedlines.begin() + nPos);
2122 SwViewShell* pSh;
2123 if( pDoc && !pDoc->IsInDtor() &&
2124 0 != ( pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) )
2125 pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
2129 void SwExtraRedlineTable::DeleteAndDestroyAll()
2131 while (!m_aExtraRedlines.empty())
2133 auto const pRedline = m_aExtraRedlines.back();
2134 m_aExtraRedlines.pop_back();
2135 delete pRedline;
2139 SwExtraRedline::~SwExtraRedline()
2143 SwTableRowRedline::SwTableRowRedline(const SwRedlineData& rData, const SwTableLine& rTableLine)
2144 : m_aRedlineData(rData)
2145 , m_rTableLine(rTableLine)
2149 SwTableRowRedline::~SwTableRowRedline()
2153 SwTableCellRedline::SwTableCellRedline(const SwRedlineData& rData, const SwTableBox& rTableBox)
2154 : m_aRedlineData(rData)
2155 , m_rTableBox(rTableBox)
2159 SwTableCellRedline::~SwTableCellRedline()
2163 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */