1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
46 #include <redline.hxx>
47 #include <UndoCore.hxx>
50 #include <poolfmt.hxx>
55 #include <viewopt.hxx>
56 #include <usrpref.hxx>
59 #include <rootfrm.hxx>
60 #include <strings.hrc>
61 #include <swtypes.hxx>
65 #include <flowfrm.hxx>
67 #include <annotationmark.hxx>
69 using namespace com::sun::star
;
73 void sw_DebugRedline( const SwDoc
* pDoc
)
75 static SwRedlineTable::size_type nWatch
= 0; // loplugin:constvars:ignore
76 const SwRedlineTable
& rTable
= pDoc
->getIDocumentRedlineAccess().GetRedlineTable();
77 for( SwRedlineTable::size_type n
= 0; n
< rTable
.size(); ++n
)
79 volatile SwRedlineTable::size_type nDummy
= 0;
80 const SwRangeRedline
* pCurrent
= rTable
[ n
];
81 const SwRangeRedline
* pNext
= n
+1 < rTable
.size() ? rTable
[ n
+1 ] : nullptr;
82 if( pCurrent
== pNext
)
85 (void) nDummy
; // Possible debugger breakpoint
92 SwExtraRedlineTable::~SwExtraRedlineTable()
94 DeleteAndDestroyAll();
97 void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter
) const
99 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwExtraRedlineTable"));
100 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
102 for (sal_uInt16 nCurExtraRedlinePos
= 0; nCurExtraRedlinePos
< GetSize(); ++nCurExtraRedlinePos
)
104 const SwExtraRedline
* pExtraRedline
= GetRedline(nCurExtraRedlinePos
);
105 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwExtraRedline"));
106 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
107 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline
).name()));
108 (void)xmlTextWriterEndElement(pWriter
);
110 (void)xmlTextWriterEndElement(pWriter
);
113 #if OSL_DEBUG_LEVEL > 0
114 static bool CheckPosition( const SwPosition
* pStart
, const SwPosition
* pEnd
)
117 SwNode
* pSttNode
= &pStart
->GetNode();
118 SwNode
* pEndNode
= &pEnd
->GetNode();
119 SwNode
* pSttTab
= pSttNode
->StartOfSectionNode()->FindTableNode();
120 SwNode
* pEndTab
= pEndNode
->StartOfSectionNode()->FindTableNode();
121 SwNode
* pSttStart
= pSttNode
;
122 while( pSttStart
&& (!pSttStart
->IsStartNode() || pSttStart
->IsSectionNode() ||
123 pSttStart
->IsTableNode() ) )
124 pSttStart
= pSttStart
->StartOfSectionNode();
125 SwNode
* pEndStart
= pEndNode
;
126 while( pEndStart
&& (!pEndStart
->IsStartNode() || pEndStart
->IsSectionNode() ||
127 pEndStart
->IsTableNode() ) )
128 pEndStart
= pEndStart
->StartOfSectionNode();
129 assert(pSttTab
== pEndTab
);
130 if( pSttTab
!= pEndTab
)
132 assert(pSttTab
|| pSttStart
== pEndStart
);
133 if( !pSttTab
&& pSttStart
!= pEndStart
)
141 bool SwExtraRedlineTable::DeleteAllTableRedlines( SwDoc
& rDoc
, const SwTable
& rTable
, bool bSaveInUndo
, RedlineType nRedlineTypeToDelete
)
145 if (bSaveInUndo
&& rDoc
.GetIDocumentUndoRedo().DoesUndo())
147 // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
149 SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
150 if( pUndo->GetRedlSaveCount() )
152 GetIDocumentUndoRedo().AppendUndo(pUndo);
159 for (sal_uInt16 nCurRedlinePos
= 0; nCurRedlinePos
< GetSize(); )
161 SwExtraRedline
* pExtraRedline
= GetRedline(nCurRedlinePos
);
162 const SwTableCellRedline
* pTableCellRedline
= dynamic_cast<const SwTableCellRedline
*>(pExtraRedline
);
163 if (pTableCellRedline
)
165 const SwTableBox
*pRedTabBox
= &pTableCellRedline
->GetTableBox();
166 const SwTable
& rRedTable
= pRedTabBox
->GetSttNd()->FindTableNode()->GetTable();
167 if ( &rRedTable
== &rTable
)
169 // Redline for this table
170 const SwRedlineData
& aRedlineData
= pTableCellRedline
->GetRedlineData();
171 const RedlineType nRedlineType
= aRedlineData
.GetType();
173 // Check if this redline object type should be deleted
174 if (RedlineType::Any
== nRedlineTypeToDelete
|| nRedlineTypeToDelete
== nRedlineType
)
177 DeleteAndDestroy( nCurRedlinePos
);
179 continue; // don't increment position after delete
187 rDoc
.getIDocumentState().SetModified();
192 bool SwExtraRedlineTable::DeleteTableRowRedline( SwDoc
* pDoc
, const SwTableLine
& rTableLine
, bool bSaveInUndo
, RedlineType nRedlineTypeToDelete
)
196 if (bSaveInUndo
&& pDoc
->GetIDocumentUndoRedo().DoesUndo())
198 // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
200 SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
201 if( pUndo->GetRedlSaveCount() )
203 GetIDocumentUndoRedo().AppendUndo(pUndo);
210 for(sal_uInt16 nCurRedlinePos
= 0; nCurRedlinePos
< GetSize(); ++nCurRedlinePos
)
212 SwExtraRedline
* pExtraRedline
= GetRedline(nCurRedlinePos
);
213 const SwTableRowRedline
* pTableRowRedline
= dynamic_cast<const SwTableRowRedline
*>(pExtraRedline
);
214 if (!pTableRowRedline
)
216 const SwTableLine
& rRedTabLine
= pTableRowRedline
->GetTableLine();
217 if ( &rRedTabLine
== &rTableLine
)
219 // Redline for this table row
220 const SwRedlineData
& aRedlineData
= pTableRowRedline
->GetRedlineData();
221 const RedlineType nRedlineType
= aRedlineData
.GetType();
223 // Check if this redline object type should be deleted
224 if( RedlineType::Any
!= nRedlineTypeToDelete
&& nRedlineTypeToDelete
!= nRedlineType
)
227 DeleteAndDestroy( nCurRedlinePos
);
233 pDoc
->getIDocumentState().SetModified();
238 bool SwExtraRedlineTable::DeleteTableCellRedline( SwDoc
* pDoc
, const SwTableBox
& rTableBox
, bool bSaveInUndo
, RedlineType nRedlineTypeToDelete
)
242 if (bSaveInUndo
&& pDoc
->GetIDocumentUndoRedo().DoesUndo())
244 // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
246 SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
247 if( pUndo->GetRedlSaveCount() )
249 GetIDocumentUndoRedo().AppendUndo(pUndo);
256 for(sal_uInt16 nCurRedlinePos
= 0; nCurRedlinePos
< GetSize(); ++nCurRedlinePos
)
258 SwExtraRedline
* pExtraRedline
= GetRedline(nCurRedlinePos
);
259 const SwTableCellRedline
* pTableCellRedline
= dynamic_cast<const SwTableCellRedline
*>(pExtraRedline
);
260 if (!pTableCellRedline
)
262 const SwTableBox
& rRedTabBox
= pTableCellRedline
->GetTableBox();
263 if (&rRedTabBox
== &rTableBox
)
265 // Redline for this table cell
266 const SwRedlineData
& aRedlineData
= pTableCellRedline
->GetRedlineData();
267 const RedlineType nRedlineType
= aRedlineData
.GetType();
269 // Check if this redline object type should be deleted
270 if( RedlineType::Any
!= nRedlineTypeToDelete
&& nRedlineTypeToDelete
!= nRedlineType
)
273 DeleteAndDestroy( nCurRedlinePos
);
279 pDoc
->getIDocumentState().SetModified();
287 void lcl_LOKInvalidateFrames(const sw::BroadcastingModify
& rMod
, const SwRootFrame
* pLayout
,
288 SwFrameType
const nFrameType
, const Point
* pPoint
)
290 SwIterator
<SwFrame
, sw::BroadcastingModify
, sw::IteratorMode::UnwrapMulti
> aIter(rMod
);
292 for (SwFrame
* pTmpFrame
= aIter
.First(); pTmpFrame
; pTmpFrame
= aIter
.Next() )
294 if ((pTmpFrame
->GetType() & nFrameType
) &&
295 (!pLayout
|| pLayout
== pTmpFrame
->getRootFrame()) &&
296 (!pTmpFrame
->IsFlowFrame() || !SwFlowFrame::CastFlowFrame( pTmpFrame
)->IsFollow()))
300 pTmpFrame
->InvalidateSize();
302 // Also empty the text portion cache, so it gets rebuilt, taking the new redlines
304 if (pTmpFrame
->IsTextFrame())
306 auto pTextFrame
= static_cast<SwTextFrame
*>(pTmpFrame
);
307 pTextFrame
->ClearPara();
314 void lcl_LOKInvalidateStartEndFrames(SwShellCursor
& rCursor
)
316 if (!(rCursor
.HasMark() &&
317 rCursor
.GetPoint()->GetNode().IsContentNode() &&
318 rCursor
.GetPoint()->GetNode().GetContentNode()->getLayoutFrame(rCursor
.GetShell()->GetLayout()) &&
319 (rCursor
.GetMark()->GetNode() == rCursor
.GetPoint()->GetNode() ||
320 (rCursor
.GetMark()->GetNode().IsContentNode() &&
321 rCursor
.GetMark()->GetNode().GetContentNode()->getLayoutFrame(rCursor
.GetShell()->GetLayout())))))
326 auto [pStartPos
, pEndPos
] = rCursor
.StartEnd(); // SwPosition*
328 lcl_LOKInvalidateFrames(*(pStartPos
->GetNode().GetContentNode()),
329 rCursor
.GetShell()->GetLayout(),
330 FRM_CNTNT
, &rCursor
.GetSttPos());
332 lcl_LOKInvalidateFrames(*(pEndPos
->GetNode().GetContentNode()),
333 rCursor
.GetShell()->GetLayout(),
334 FRM_CNTNT
, &rCursor
.GetEndPos());
337 bool lcl_LOKRedlineNotificationEnabled()
339 static bool bDisableRedlineComments
= getenv("DISABLE_REDLINE") != nullptr;
340 if (comphelper::LibreOfficeKit::isActive() && !bDisableRedlineComments
)
346 } // anonymous namespace
348 void SwRedlineTable::setMovedIDIfNeeded(sal_uInt32 nMax
)
350 if (nMax
> m_nMaxMovedID
)
351 m_nMaxMovedID
= nMax
;
354 /// Emits LOK notification about one addition / removal of a redline item.
355 void SwRedlineTable::LOKRedlineNotification(RedlineNotification nType
, SwRangeRedline
* pRedline
)
357 // Disable since usability is very low beyond some small number of changes.
358 if (!lcl_LOKRedlineNotificationEnabled())
361 boost::property_tree::ptree aRedline
;
362 aRedline
.put("action", (nType
== RedlineNotification::Add
? "Add" :
363 (nType
== RedlineNotification::Remove
? "Remove" :
364 (nType
== RedlineNotification::Modify
? "Modify" : "???"))));
365 aRedline
.put("index", pRedline
->GetId());
366 aRedline
.put("author", pRedline
->GetAuthorString(1).toUtf8().getStr());
367 aRedline
.put("type", SwRedlineTypeToOUString(pRedline
->GetRedlineData().GetType()).toUtf8().getStr());
368 aRedline
.put("comment", pRedline
->GetRedlineData().GetComment().toUtf8().getStr());
369 aRedline
.put("description", pRedline
->GetDescr().toUtf8().getStr());
370 OUString sDateTime
= utl::toISO8601(pRedline
->GetRedlineData().GetTimeStamp().GetUNODateTime());
371 aRedline
.put("dateTime", sDateTime
.toUtf8().getStr());
373 auto [pStartPos
, pEndPos
] = pRedline
->StartEnd(); // SwPosition*
374 SwContentNode
* pContentNd
= pRedline
->GetPointContentNode();
375 SwView
* pView
= dynamic_cast<SwView
*>(SfxViewShell::Current());
376 if (pView
&& pContentNd
)
378 SwShellCursor
aCursor(pView
->GetWrtShell(), *pStartPos
);
380 *aCursor
.GetMark() = *pEndPos
;
384 SwRects
* pRects(&aCursor
);
385 std::vector
<OString
> aRects
;
386 for(const SwRect
& rNextRect
: *pRects
)
387 aRects
.push_back(rNextRect
.SVRect().toString());
389 const OString sRects
= comphelper::string::join("; ", aRects
);
390 aRedline
.put("textRange", sRects
.getStr());
392 lcl_LOKInvalidateStartEndFrames(aCursor
);
394 // When this notify method is called text invalidation is not done yet
395 // Calling FillRects updates the text area so invalidation will not run on the correct rects
396 // So we need to do an own invalidation here. It invalidates text frames containing the redlining
397 SwDoc
& rDoc
= pRedline
->GetDoc();
399 if( !rDoc
.IsInDtor() )
401 pSh
= rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell();
403 for(SwNodeIndex
nIdx(pStartPos
->GetNode()); nIdx
<= pEndPos
->GetNode(); ++nIdx
)
405 SwContentNode
* pContentNode
= nIdx
.GetNode().GetContentNode();
407 pSh
->InvalidateWindows(pContentNode
->FindLayoutRect());
412 boost::property_tree::ptree aTree
;
413 aTree
.add_child("redline", aRedline
);
414 std::stringstream aStream
;
415 boost::property_tree::write_json(aStream
, aTree
);
416 std::string aPayload
= aStream
.str();
418 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
421 if (pView
&& pView
->GetDocId() == pViewShell
->GetDocId())
422 pViewShell
->libreOfficeKitViewCallback(nType
== RedlineNotification::Modify
? LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED
: LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED
, OString(aPayload
));
423 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
427 bool SwRedlineTable::Insert(SwRangeRedline
*& p
)
429 if( p
->HasValidRange() )
431 std::pair
<vector_type::const_iterator
, bool> rv
= maVector
.insert( p
);
432 size_type nP
= rv
.first
- begin();
433 LOKRedlineNotification(RedlineNotification::Add
, p
);
435 // detect text moving by checking nearby redlines, except during Undo
436 // (apply isMoved() during OpenDocument and DOCX import, too, to fix
437 // missing text moving handling in ODF and e.g. web version of MSO)
438 if ( p
->GetDoc().GetIDocumentUndoRedo().DoesUndo() ||
439 p
->GetDoc().IsInWriterfilterImport() ||
440 p
->GetDoc().IsInXMLImport() )
445 p
->CallDisplayFunc(nP
);
448 CheckOverlapping(rv
.first
);
449 if (!mpMaxEndPos
|| (*(*rv
.first
)->End()) > *mpMaxEndPos
->End())
450 mpMaxEndPos
= *rv
.first
;
454 return InsertWithValidRanges( p
);
457 void SwRedlineTable::CheckOverlapping(vector_type::const_iterator it
)
459 if (m_bHasOverlappingElements
)
461 if (maVector
.size() <= 1) // a single element cannot be overlapping
464 auto itNext
= it
+ 1;
465 if (itNext
!= maVector
.end())
467 auto pNext
= *itNext
;
468 if (pCurr
->End()->GetNodeIndex() >= pNext
->Start()->GetNodeIndex())
470 m_bHasOverlappingElements
= true;
474 if (it
!= maVector
.begin())
476 auto pPrev
= *(it
- 1);
477 if (pPrev
->End()->GetNodeIndex() >= pCurr
->Start()->GetNodeIndex())
478 m_bHasOverlappingElements
= true;
482 bool SwRedlineTable::Insert(SwRangeRedline
*& p
, size_type
& rP
)
484 if( p
->HasValidRange() )
486 std::pair
<vector_type::const_iterator
, bool> rv
= maVector
.insert( p
);
487 rP
= rv
.first
- begin();
488 p
->CallDisplayFunc(rP
);
491 CheckOverlapping(rv
.first
);
492 if (!mpMaxEndPos
|| (*(*rv
.first
)->End()) > *mpMaxEndPos
->End())
493 mpMaxEndPos
= *rv
.first
;
497 return InsertWithValidRanges( p
, &rP
);
502 std::vector
<std::unique_ptr
<SwRangeRedline
>> GetAllValidRanges(std::unique_ptr
<SwRangeRedline
> p
)
504 std::vector
<std::unique_ptr
<SwRangeRedline
>> ret
;
505 // Create valid "sub-ranges" from the Selection
506 auto [pStart
, pEnd
] = p
->StartEnd(); // SwPosition*
507 SwPosition
aNewStt( *pStart
);
508 SwNodes
& rNds
= aNewStt
.GetNodes();
511 if( !aNewStt
.GetNode().IsContentNode() )
513 pC
= SwNodes::GoNext(&aNewStt
);
515 aNewStt
.Assign(rNds
.GetEndOfContent());
519 if( aNewStt
>= *pEnd
)
522 std::unique_ptr
<SwRangeRedline
> pNew
;
525 pNew
.reset(new SwRangeRedline( p
->GetRedlineData(), aNewStt
));
529 *pNew
->GetPoint() = aNewStt
;
533 GoEndSection( pNew
->GetPoint() );
534 // i60396: If the redlines starts before a table but the table is the last member
535 // of the section, the GoEndSection will end inside the table.
536 // This will result in an incorrect redline, so we've to go back
537 SwNode
* pTab
= pNew
->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
538 // We end in a table when pTab != 0
539 if( pTab
&& !pNew
->GetMark()->GetNode().StartOfSectionNode()->FindTableNode() )
540 { // but our Mark was outside the table => Correction
543 // We want to be before the table
544 pNew
->GetPoint()->Assign(*pTab
);
545 pC
= GoPreviousPos( pNew
->GetPoint(), false ); // here we are.
547 pNew
->GetPoint()->SetContent( 0 );
548 pTab
= pNew
->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
549 } while( pTab
); // If there is another table we have to repeat our step backwards
552 // insert dummy character to the empty table rows to keep their changes
553 SwNode
& rBoxNode
= pNew
->GetMark()->GetNode();
554 if ( rBoxNode
.GetDoc().GetIDocumentUndoRedo().DoesUndo() && rBoxNode
.GetTableBox() &&
555 rBoxNode
.GetTableBox()->GetUpper()->IsEmpty() && rBoxNode
.GetTextNode() )
557 ::sw::UndoGuard
const undoGuard(rBoxNode
.GetDoc().GetIDocumentUndoRedo());
558 rBoxNode
.GetTextNode()->InsertDummy();
559 pNew
->GetMark()->SetContent( 1 );
562 if( *pNew
->GetPoint() > *pEnd
)
565 if( aNewStt
.GetNode() != pEnd
->GetNode() )
567 SwNode
& rCurNd
= aNewStt
.GetNode();
568 if( rCurNd
.IsStartNode() )
570 if( rCurNd
.EndOfSectionIndex() < pEnd
->GetNodeIndex() )
571 aNewStt
.Assign( *rCurNd
.EndOfSectionNode() );
575 else if( rCurNd
.IsContentNode() )
576 pC
= rCurNd
.GetContentNode();
577 aNewStt
.Adjust(SwNodeOffset(1));
578 } while( aNewStt
.GetNodeIndex() < pEnd
->GetNodeIndex() );
580 if( aNewStt
.GetNode() == pEnd
->GetNode() )
581 aNewStt
.SetContent(pEnd
->GetContentIndex());
584 aNewStt
.Assign(*pC
, pC
->Len() );
587 if( aNewStt
<= *pEnd
)
588 *pNew
->GetPoint() = aNewStt
;
591 aNewStt
= *pNew
->GetPoint();
592 #if OSL_DEBUG_LEVEL > 0
593 CheckPosition( pNew
->GetPoint(), pNew
->GetMark() );
596 if( *pNew
->GetPoint() != *pNew
->GetMark() &&
597 pNew
->HasValidRange())
599 ret
.push_back(std::move(pNew
));
602 if( aNewStt
>= *pEnd
)
604 pC
= SwNodes::GoNext(&aNewStt
);
607 } while( aNewStt
< *pEnd
);
614 static void lcl_setRowNotTracked(SwNode
& rNode
)
616 SwDoc
& rDoc
= rNode
.GetDoc();
617 const SwTableBox
* pTableBox
= rNode
.GetTableBox();
618 if ( rDoc
.GetIDocumentUndoRedo().DoesUndo() && pTableBox
)
620 SvxPrintItem
aSetTracking(RES_PRINT
, false);
621 SwNodeIndex
aInsPos( *(pTableBox
->GetSttNd()), 1);
622 SwCursor
aCursor( SwPosition(aInsPos
), nullptr );
623 ::sw::UndoGuard
const undoGuard(rNode
.GetDoc().GetIDocumentUndoRedo());
624 rDoc
.SetRowNotTracked( aCursor
, aSetTracking
);
628 bool SwRedlineTable::InsertWithValidRanges(SwRangeRedline
*& p
, size_type
* pInsPos
)
630 bool bAnyIns
= false;
631 bool bInsert
= RedlineType::Insert
== p
->GetType();
632 SwNode
* pSttNode
= &p
->Start()->GetNode();
634 std::vector
<std::unique_ptr
<SwRangeRedline
>> redlines(
635 GetAllValidRanges(std::unique_ptr
<SwRangeRedline
>(p
)));
637 // tdf#147180 set table change tracking in the empty row with text insertion
639 lcl_setRowNotTracked(*pSttNode
);
641 for (std::unique_ptr
<SwRangeRedline
> & pRedline
: redlines
)
643 assert(pRedline
->HasValidRange());
645 auto pTmpRedline
= pRedline
.release();
646 if (Insert(pTmpRedline
, nInsPos
))
648 // tdf#147180 set table tracking to the table row
649 lcl_setRowNotTracked(pTmpRedline
->GetPointNode());
651 pTmpRedline
->CallDisplayFunc(nInsPos
);
653 if (pInsPos
&& *pInsPos
< nInsPos
)
663 bool CompareSwRedlineTable::operator()(const SwRangeRedline
* lhs
, const SwRangeRedline
* rhs
) const
668 SwRedlineTable::~SwRedlineTable()
670 maVector
.DeleteAndDestroyAll();
673 SwRedlineTable::size_type
SwRedlineTable::GetPos(const SwRangeRedline
* p
) const
675 vector_type::const_iterator it
= maVector
.find(p
);
676 if( it
== maVector
.end() )
678 return it
- maVector
.begin();
681 void SwRedlineTable::Remove( const SwRangeRedline
* p
)
683 const size_type nPos
= GetPos(p
);
689 void SwRedlineTable::Remove( size_type nP
)
691 LOKRedlineNotification(RedlineNotification::Remove
, maVector
[nP
]);
692 SwDoc
* pDoc
= nullptr;
693 if( !nP
&& 1 == size() )
694 pDoc
= &maVector
.front()->GetDoc();
696 if (mpMaxEndPos
== maVector
[nP
])
697 mpMaxEndPos
= nullptr;
698 maVector
.erase( maVector
.begin() + nP
);
700 if( pDoc
&& !pDoc
->IsInDtor() )
702 SwViewShell
* pSh
= pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell();
704 pSh
->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32
, SAL_MAX_INT32
) );
708 void SwRedlineTable::DeleteAndDestroyAll()
710 while (!maVector
.empty())
712 auto const pRedline
= maVector
.back();
713 maVector
.erase_at(maVector
.size() - 1);
714 LOKRedlineNotification(RedlineNotification::Remove
, pRedline
);
717 m_bHasOverlappingElements
= false;
718 mpMaxEndPos
= nullptr;
721 void SwRedlineTable::DeleteAndDestroy(size_type
const nP
)
723 auto const pRedline
= maVector
[nP
];
724 if (pRedline
== mpMaxEndPos
)
725 mpMaxEndPos
= nullptr;
726 maVector
.erase(maVector
.begin() + nP
);
727 LOKRedlineNotification(RedlineNotification::Remove
, pRedline
);
731 SwRedlineTable::size_type
SwRedlineTable::FindNextOfSeqNo( size_type nSttPos
) const
733 return nSttPos
+ 1 < size()
734 ? FindNextSeqNo( operator[]( nSttPos
)->GetSeqNo(), nSttPos
+1 )
738 SwRedlineTable::size_type
SwRedlineTable::FindPrevOfSeqNo( size_type nSttPos
) const
740 return nSttPos
? FindPrevSeqNo( operator[]( nSttPos
)->GetSeqNo(), nSttPos
-1 )
744 /// Find the next or preceding Redline with the same seq.no.
745 /// We can limit the search using look ahead (0 searches the whole array).
746 SwRedlineTable::size_type
SwRedlineTable::FindNextSeqNo( sal_uInt16 nSeqNo
, size_type nSttPos
) const
748 auto constexpr nLookahead
= 20;
749 size_type nRet
= npos
;
750 if( nSeqNo
&& nSttPos
< size() )
752 size_type nEnd
= size();
753 const size_type nTmp
= nSttPos
+ nLookahead
;
759 for( ; nSttPos
< nEnd
; ++nSttPos
)
760 if( nSeqNo
== operator[]( nSttPos
)->GetSeqNo() )
769 SwRedlineTable::size_type
SwRedlineTable::FindPrevSeqNo( sal_uInt16 nSeqNo
, size_type nSttPos
) const
771 auto constexpr nLookahead
= 20;
772 size_type nRet
= npos
;
773 if( nSeqNo
&& nSttPos
< size() )
776 if( nSttPos
> nLookahead
)
777 nEnd
= nSttPos
- nLookahead
;
780 while( nSttPos
> nEnd
)
783 if( nSeqNo
== operator[](nSttPos
)->GetSeqNo() )
793 const SwRangeRedline
* SwRedlineTable::FindAtPosition( const SwPosition
& rSttPos
,
797 const SwRangeRedline
* pFnd
= nullptr;
798 for( ; rPos
< maVector
.size() ; ++rPos
)
800 const SwRangeRedline
* pTmp
= (*this)[ rPos
];
801 if( pTmp
->HasMark() && pTmp
->IsVisible() )
803 auto [pRStt
, pREnd
] = pTmp
->StartEnd(); // SwPosition*
804 if( bNext
? *pRStt
<= rSttPos
: *pRStt
< rSttPos
)
806 if( bNext
? *pREnd
> rSttPos
: *pREnd
>= rSttPos
)
821 bool lcl_CanCombineWithRange(SwRangeRedline
* pOrigin
, SwRangeRedline
* pActual
,
822 SwRangeRedline
* pOther
, bool bReverseDir
, bool bCheckChilds
)
824 if (pOrigin
->IsVisible() != pOther
->IsVisible())
829 if (*(pOther
->End()) != *(pActual
->Start()))
834 if (*(pActual
->End()) != *(pOther
->Start()))
838 if (!pOrigin
->GetRedlineData(0).CanCombineForAcceptReject(pOther
->GetRedlineData(0)))
840 if (!bCheckChilds
|| pOther
->GetStackCount() <= 1
841 || !pOrigin
->GetRedlineData(0).CanCombineForAcceptReject(pOther
->GetRedlineData(1)))
844 if (pOther
->Start()->GetNode().StartOfSectionNode()
845 != pActual
->Start()->GetNode().StartOfSectionNode())
852 const SwPosition
& SwRedlineTable::GetMaxEndPos() const
854 assert(!empty() && "cannot call this when the redline table is empty");
856 return *mpMaxEndPos
->End();
857 for (const SwRangeRedline
* i
: maVector
)
859 if (!mpMaxEndPos
|| *i
->End() > *mpMaxEndPos
->End())
863 return *mpMaxEndPos
->End();
866 void SwRedlineTable::getConnectedArea(size_type nPosOrigin
, size_type
& rPosStart
,
867 size_type
& rPosEnd
, bool bCheckChilds
) const
869 // Keep the original redline .. else we should memorize which children was checked
870 // at the last combined redline.
871 SwRangeRedline
* pOrigin
= (*this)[nPosOrigin
];
872 rPosStart
= nPosOrigin
;
873 rPosEnd
= nPosOrigin
;
874 SwRangeRedline
* pRedline
= pOrigin
;
875 SwRangeRedline
* pOther
;
877 // connection info is already here..only the actual text is missing at import time
878 // so no need to check Redline->GetContentIdx() here yet.
879 while (rPosStart
> 0 && (pOther
= (*this)[rPosStart
- 1])
880 && lcl_CanCombineWithRange(pOrigin
, pRedline
, pOther
, true, bCheckChilds
))
886 while (rPosEnd
+ 1 < size() && (pOther
= (*this)[rPosEnd
+ 1])
887 && lcl_CanCombineWithRange(pOrigin
, pRedline
, pOther
, false, bCheckChilds
))
894 OUString
SwRedlineTable::getTextOfArea(size_type rPosStart
, size_type rPosEnd
) const
896 // Normally a SwPaM::GetText() would be enough with rPosStart-start and rPosEnd-end
897 // But at import time some text is not present there yet
898 // we have to collect them 1 by 1
900 OUString sRet
= u
""_ustr
;
902 for (size_type nIdx
= rPosStart
; nIdx
<= rPosEnd
; ++nIdx
)
904 SwRangeRedline
* pRedline
= (*this)[nIdx
];
905 bool bStartWithNonTextNode
= false;
908 if (nullptr == pRedline
->GetContentIdx())
910 sNew
= pRedline
->GetText();
912 else // otherwise it is saved in pContentSect, e.g. during ODT import
914 SwPaM
aTmpPaM(pRedline
->GetContentIdx()->GetNode(),
915 *pRedline
->GetContentIdx()->GetNode().EndOfSectionNode());
916 if (!aTmpPaM
.Start()->nNode
.GetNode().GetTextNode())
918 bStartWithNonTextNode
= true;
920 sNew
= aTmpPaM
.GetText();
923 if (bStartWithNonTextNode
&&
924 sNew
[0] == CH_TXTATR_NEWLINE
)
926 sRet
+= sNew
.subView(1);
935 bool SwRedlineTable::isMoved(size_type rPos
) const
937 // If it is already a part of a movement, then don't check it.
938 if ((*this)[rPos
]->GetMoved() != 0)
940 // First try with single redline. then try with combined redlines
941 if (isMovedImpl(rPos
, false))
944 return isMovedImpl(rPos
, true);
947 bool SwRedlineTable::isMovedImpl(size_type rPos
, bool bTryCombined
) const
950 auto constexpr nLookahead
= 20;
951 SwRangeRedline
* pRedline
= (*this)[ rPos
];
953 // set redline type of the searched pair
954 RedlineType nPairType
= pRedline
->GetType();
955 if ( RedlineType::Delete
== nPairType
)
956 nPairType
= RedlineType::Insert
;
957 else if ( RedlineType::Insert
== nPairType
)
958 nPairType
= RedlineType::Delete
;
960 // only deleted or inserted text can be moved
964 SwRedlineTable::size_type nPosStart
= rPos
;
965 SwRedlineTable::size_type nPosEnd
= rPos
;
969 getConnectedArea(rPos
, nPosStart
, nPosEnd
, false);
970 if (nPosStart
!= nPosEnd
)
971 sTrimmed
= getTextOfArea(nPosStart
, nPosEnd
).trim();
974 if (sTrimmed
.isEmpty())
976 // if this redline is visible the content is in this PaM
977 if (nullptr == pRedline
->GetContentIdx())
979 sTrimmed
= pRedline
->GetText().trim();
981 else // otherwise it is saved in pContentSect, e.g. during ODT import
983 SwPaM
aTmpPaM(pRedline
->GetContentIdx()->GetNode(),
984 *pRedline
->GetContentIdx()->GetNode().EndOfSectionNode());
985 sTrimmed
= aTmpPaM
.GetText().trim();
989 // detection of move needs at least 6 characters with an inner
990 // space after stripping white spaces of the redline to skip
991 // frequent deleted and inserted articles or other common
992 // word parts, e.g. 'the' and 'of a' to detect as text moving
993 if (sTrimmed
.getLength() < 6 || sTrimmed
.indexOf(' ') == -1)
998 // Todo: lessen the previous condition..:
999 // if the source / destination is a whole node change then maybe space is not needed
1001 // search pair around the actual redline
1002 size_type nEnd
= rPos
+ nLookahead
< size()
1005 size_type nStart
= rPos
> nLookahead
? rPos
- nLookahead
: 0;
1006 // first, try to compare to single redlines
1007 // next, try to compare to combined redlines
1008 for (int nPass
= 0; nPass
< 2 && !bRet
; nPass
++)
1010 for (size_type nPosAct
= nStart
; nPosAct
< nEnd
&& !bRet
; ++nPosAct
)
1012 SwRangeRedline
* pPair
= (*this)[nPosAct
];
1014 // redline must be the requested type and from the same author
1015 if (nPairType
!= pPair
->GetType() || pRedline
->GetAuthor() != pPair
->GetAuthor())
1020 OUString sPairTrimmed
= u
""_ustr
;
1021 SwRedlineTable::size_type nPairStart
= nPosAct
;
1022 SwRedlineTable::size_type nPairEnd
= nPosAct
;
1026 // if this redline is visible the content is in this PaM
1027 if (nullptr == pPair
->GetContentIdx())
1029 sPairTrimmed
= o3tl::trim(pPair
->GetText());
1031 else // otherwise it is saved in pContentSect, e.g. during ODT import
1033 // saved in pContentSect, e.g. during ODT import
1034 SwPaM
aPairPaM(pPair
->GetContentIdx()->GetNode(),
1035 *pPair
->GetContentIdx()->GetNode().EndOfSectionNode());
1036 sPairTrimmed
= o3tl::trim(aPairPaM
.GetText());
1041 getConnectedArea(nPosAct
, nPairStart
, nPairEnd
, false);
1042 if (nPairStart
!= nPairEnd
)
1043 sPairTrimmed
= getTextOfArea(nPairStart
, nPairEnd
).trim();
1046 // pair at tracked moving: same text by trimming trailing white spaces
1047 if (abs(sTrimmed
.getLength() - sPairTrimmed
.getLength()) <= 2
1048 && sTrimmed
== sPairTrimmed
)
1050 sal_uInt32 nMID
= getNewMovedID();
1051 if (nPosStart
!= nPosEnd
)
1053 for (size_type nIdx
= nPosStart
; nIdx
<= nPosEnd
; ++nIdx
)
1055 (*this)[nIdx
]->SetMoved(nMID
);
1057 (*this)[nIdx
]->InvalidateRange(SwRangeRedline::Invalidation::Add
);
1061 pRedline
->SetMoved(nMID
);
1063 //in (nPass == 0) it will only call once .. as nPairStart == nPairEnd == nPosAct
1064 for (size_type nIdx
= nPairStart
; nIdx
<= nPairEnd
; ++nIdx
)
1066 (*this)[nIdx
]->SetMoved(nMID
);
1067 (*this)[nIdx
]->InvalidateRange(SwRangeRedline::Invalidation::Add
);
1073 //we can skip the combined redlines
1082 void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter
) const
1084 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwRedlineTable"));
1085 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
1087 for (SwRedlineTable::size_type nCurRedlinePos
= 0; nCurRedlinePos
< size(); ++nCurRedlinePos
)
1088 operator[](nCurRedlinePos
)->dumpAsXml(pWriter
);
1090 (void)xmlTextWriterEndElement(pWriter
);
1093 SwRedlineExtraData::~SwRedlineExtraData()
1097 void SwRedlineExtraData::Reject( SwPaM
& ) const
1101 bool SwRedlineExtraData::operator == ( const SwRedlineExtraData
& ) const
1106 SwRedlineExtraData_FormatColl::SwRedlineExtraData_FormatColl( OUString aColl
,
1107 sal_uInt16 nPoolFormatId
,
1108 const SfxItemSet
* pItemSet
,
1110 : m_sFormatNm(std::move(aColl
)), m_nPoolId(nPoolFormatId
), m_bFormatAll(bFormatAll
)
1112 if( pItemSet
&& pItemSet
->Count() )
1113 m_pSet
.reset( new SfxItemSet( *pItemSet
) );
1116 SwRedlineExtraData_FormatColl::~SwRedlineExtraData_FormatColl()
1120 SwRedlineExtraData
* SwRedlineExtraData_FormatColl::CreateNew() const
1122 return new SwRedlineExtraData_FormatColl( m_sFormatNm
, m_nPoolId
, m_pSet
.get(), m_bFormatAll
);
1125 void SwRedlineExtraData_FormatColl::Reject( SwPaM
& rPam
) const
1127 SwDoc
& rDoc
= rPam
.GetDoc();
1129 // What about Undo? Is it turned off?
1130 SwTextFormatColl
* pColl
= USHRT_MAX
== m_nPoolId
1131 ? rDoc
.FindTextFormatCollByName( m_sFormatNm
)
1132 : rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId
);
1134 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1135 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
& ~RedlineFlags(RedlineFlags::On
| RedlineFlags::Ignore
));
1137 SwPaM
aPam( *rPam
.GetMark(), *rPam
.GetPoint() );
1139 const SwPosition
* pEnd
= rPam
.End();
1141 if ( !m_bFormatAll
|| pEnd
->GetContentIndex() == 0 )
1143 // don't reject the format of the next paragraph (that is handled by the next redline)
1144 if (aPam
.GetPoint()->GetNode() > aPam
.GetMark()->GetNode())
1146 aPam
.GetPoint()->Adjust(SwNodeOffset(-1));
1147 SwContentNode
* pNode
= aPam
.GetPoint()->GetNode().GetContentNode();
1149 aPam
.GetPoint()->SetContent( pNode
->Len() );
1151 // tdf#147507 set it back to a content node to avoid of crashing
1152 aPam
.GetPoint()->Adjust(SwNodeOffset(+1));
1154 else if (aPam
.GetPoint()->GetNode() < aPam
.GetMark()->GetNode())
1156 aPam
.GetMark()->Adjust(SwNodeOffset(-1));
1157 SwContentNode
* pNode
= aPam
.GetMark()->GetNode().GetContentNode();
1158 aPam
.GetMark()->SetContent( pNode
->Len() );
1163 rDoc
.SetTextFormatColl( aPam
, pColl
, false );
1166 rDoc
.getIDocumentContentOperations().InsertItemSet( aPam
, *m_pSet
);
1168 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1171 bool SwRedlineExtraData_FormatColl::operator == ( const SwRedlineExtraData
& r
) const
1173 const SwRedlineExtraData_FormatColl
& rCmp
= static_cast<const SwRedlineExtraData_FormatColl
&>(r
);
1174 return m_sFormatNm
== rCmp
.m_sFormatNm
&& m_nPoolId
== rCmp
.m_nPoolId
&&
1175 m_bFormatAll
== rCmp
.m_bFormatAll
&&
1176 ( ( !m_pSet
&& !rCmp
.m_pSet
) ||
1177 ( m_pSet
&& rCmp
.m_pSet
&& *m_pSet
== *rCmp
.m_pSet
) );
1180 void SwRedlineExtraData_FormatColl::SetItemSet( const SfxItemSet
& rSet
)
1183 m_pSet
.reset( new SfxItemSet( rSet
) );
1188 SwRedlineExtraData_Format::SwRedlineExtraData_Format( const SfxItemSet
& rSet
)
1190 SfxItemIter
aIter( rSet
);
1191 for (const SfxPoolItem
* pItem
= aIter
.GetCurItem(); pItem
; pItem
= aIter
.NextItem())
1193 m_aWhichIds
.push_back( pItem
->Which() );
1197 SwRedlineExtraData_Format::SwRedlineExtraData_Format(
1198 const SwRedlineExtraData_Format
& rCpy
)
1199 : SwRedlineExtraData()
1201 m_aWhichIds
.insert( m_aWhichIds
.begin(), rCpy
.m_aWhichIds
.begin(), rCpy
.m_aWhichIds
.end() );
1204 SwRedlineExtraData_Format::~SwRedlineExtraData_Format()
1208 SwRedlineExtraData
* SwRedlineExtraData_Format::CreateNew() const
1210 return new SwRedlineExtraData_Format( *this );
1213 void SwRedlineExtraData_Format::Reject( SwPaM
& rPam
) const
1215 SwDoc
& rDoc
= rPam
.GetDoc();
1217 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1218 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
& ~RedlineFlags(RedlineFlags::On
| RedlineFlags::Ignore
));
1220 // Actually we need to reset the Attribute here!
1221 for( const auto& rWhichId
: m_aWhichIds
)
1223 rDoc
.getIDocumentContentOperations().InsertPoolItem( rPam
, *GetDfltAttr( rWhichId
),
1224 SetAttrMode::DONTEXPAND
);
1227 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1230 bool SwRedlineExtraData_Format::operator == ( const SwRedlineExtraData
& rCmp
) const
1232 const size_t nEnd
= m_aWhichIds
.size();
1233 if( nEnd
!= static_cast<const SwRedlineExtraData_Format
&>(rCmp
).m_aWhichIds
.size() )
1236 for( size_t n
= 0; n
< nEnd
; ++n
)
1238 if( static_cast<const SwRedlineExtraData_Format
&>(rCmp
).m_aWhichIds
[n
] != m_aWhichIds
[n
])
1246 SwRedlineData::SwRedlineData( RedlineType eT
, std::size_t nAut
, sal_uInt32 nMovedID
)
1247 : m_pNext( nullptr ), m_pExtraData( nullptr ),
1248 m_aStamp( DateTime::SYSTEM
),
1249 m_nAuthor( nAut
), m_eType( eT
), m_nSeqNo( 0 ), m_bAutoFormat(false), m_nMovedID(nMovedID
)
1251 m_aStamp
.SetNanoSec( 0 );
1254 SwRedlineData::SwRedlineData(
1255 const SwRedlineData
& rCpy
,
1257 : m_pNext( ( bCpyNext
&& rCpy
.m_pNext
) ? new SwRedlineData( *rCpy
.m_pNext
) : nullptr )
1258 , m_pExtraData( rCpy
.m_pExtraData
? rCpy
.m_pExtraData
->CreateNew() : nullptr )
1259 , m_sComment( rCpy
.m_sComment
)
1260 , m_aStamp( rCpy
.m_aStamp
)
1261 , m_nAuthor( rCpy
.m_nAuthor
)
1262 , m_eType( rCpy
.m_eType
)
1263 , m_nSeqNo( rCpy
.m_nSeqNo
)
1264 , m_bAutoFormat(false)
1265 , m_nMovedID( rCpy
.m_nMovedID
)
1269 // For sw3io: We now own pNext!
1270 SwRedlineData::SwRedlineData(RedlineType eT
, std::size_t nAut
, const DateTime
& rDT
,
1271 sal_uInt32 nMovedID
, OUString aCmnt
, SwRedlineData
*pNxt
)
1272 : m_pNext(pNxt
), m_pExtraData(nullptr), m_sComment(std::move(aCmnt
)), m_aStamp(rDT
),
1273 m_nAuthor(nAut
), m_eType(eT
), m_nSeqNo(0), m_bAutoFormat(false), m_nMovedID(nMovedID
)
1277 SwRedlineData::~SwRedlineData()
1279 delete m_pExtraData
;
1283 // Check whether the absolute difference between the two dates is no larger than one minute (can
1284 // give inaccurate results if at least one of the dates is not valid/normalized):
1285 static bool deltaOneMinute(DateTime
const & t1
, DateTime
const & t2
) {
1286 auto const [min
, max
] = std::minmax(t1
, t2
);
1287 // Avoid overflow of `min + tools::Time(0, 1)` below when min is close to the maximum valid
1289 if (min
>= DateTime({31, 12, std::numeric_limits
<sal_Int16
>::max()}, {23, 59})) {
1292 return max
<= min
+ tools::Time(0, 1);
1295 bool SwRedlineData::CanCombine(const SwRedlineData
& rCmp
) const
1297 return m_nAuthor
== rCmp
.m_nAuthor
&&
1298 m_eType
== rCmp
.m_eType
&&
1299 m_sComment
== rCmp
.m_sComment
&&
1300 deltaOneMinute(GetTimeStamp(), rCmp
.GetTimeStamp()) &&
1301 m_nMovedID
== rCmp
.m_nMovedID
&&
1302 (( !m_pNext
&& !rCmp
.m_pNext
) ||
1303 ( m_pNext
&& rCmp
.m_pNext
&&
1304 m_pNext
->CanCombine( *rCmp
.m_pNext
))) &&
1305 (( !m_pExtraData
&& !rCmp
.m_pExtraData
) ||
1306 ( m_pExtraData
&& rCmp
.m_pExtraData
&&
1307 *m_pExtraData
== *rCmp
.m_pExtraData
));
1310 // Check if we could/should accept/reject the 2 redlineData at the same time.
1311 // No need to check its children equality
1312 bool SwRedlineData::CanCombineForAcceptReject(const SwRedlineData
& rCmp
) const
1314 return m_nAuthor
== rCmp
.m_nAuthor
&&
1315 m_eType
== rCmp
.m_eType
&&
1316 m_sComment
== rCmp
.m_sComment
&&
1317 deltaOneMinute(GetTimeStamp(), rCmp
.GetTimeStamp()) &&
1318 m_nMovedID
== rCmp
.m_nMovedID
&&
1319 (( !m_pExtraData
&& !rCmp
.m_pExtraData
) ||
1320 ( m_pExtraData
&& rCmp
.m_pExtraData
&&
1321 *m_pExtraData
== *rCmp
.m_pExtraData
));
1324 /// ExtraData is copied. The Pointer's ownership is thus NOT transferred
1325 /// to the Redline Object!
1326 void SwRedlineData::SetExtraData( const SwRedlineExtraData
* pData
)
1328 delete m_pExtraData
;
1330 // Check if there is data - and if so - delete it
1332 m_pExtraData
= pData
->CreateNew();
1334 m_pExtraData
= nullptr;
1337 const TranslateId STR_REDLINE_ARY
[] =
1339 STR_UNDO_REDLINE_INSERT
,
1340 STR_UNDO_REDLINE_DELETE
,
1341 STR_UNDO_REDLINE_FORMAT
,
1342 STR_UNDO_REDLINE_TABLE
,
1343 STR_UNDO_REDLINE_FMTCOLL
,
1344 STR_UNDO_REDLINE_PARAGRAPH_FORMAT
,
1345 STR_UNDO_REDLINE_TABLE_ROW_INSERT
,
1346 STR_UNDO_REDLINE_TABLE_ROW_DELETE
,
1347 STR_UNDO_REDLINE_TABLE_CELL_INSERT
,
1348 STR_UNDO_REDLINE_TABLE_CELL_DELETE
1351 OUString
SwRedlineData::GetDescr() const
1353 return SwResId(STR_REDLINE_ARY
[static_cast<int>(GetType())]);
1356 void SwRedlineData::dumpAsXml(xmlTextWriterPtr pWriter
) const
1358 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwRedlineData"));
1360 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
1361 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr()));
1362 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("author"), BAD_CAST(SwModule::get()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr()));
1363 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr()));
1364 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("descr"), BAD_CAST(GetDescr().toUtf8().getStr()));
1366 OString sRedlineType
;
1369 case RedlineType::Insert
:
1370 sRedlineType
= "REDLINE_INSERT"_ostr
;
1372 case RedlineType::Delete
:
1373 sRedlineType
= "REDLINE_DELETE"_ostr
;
1375 case RedlineType::Format
:
1376 sRedlineType
= "REDLINE_FORMAT"_ostr
;
1379 sRedlineType
= "UNKNOWN"_ostr
;
1382 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("type"), BAD_CAST(sRedlineType
.getStr()));
1383 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("moved"), BAD_CAST(OString::number(m_nMovedID
).getStr()));
1385 (void)xmlTextWriterEndElement(pWriter
);
1388 sal_uInt32
SwRangeRedline::s_nLastId
= 1;
1392 void lcl_LOKBroadcastCommentOperation(RedlineType type
, const SwPaM
& rPam
)
1394 if (comphelper::LibreOfficeKit::isActive())
1396 auto eHintType
= RedlineType::Delete
== type
? SwFormatFieldHintWhich::REDLINED_DELETION
: SwFormatFieldHintWhich::INSERTED
;
1397 const SwTextNode
*pTextNode
= rPam
.GetPointNode().GetTextNode();
1398 SwTextAttr
* pTextAttr
= pTextNode
? pTextNode
->GetFieldTextAttrAt(rPam
.GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default
) : nullptr;
1399 SwTextField
*const pTextField(static_txtattr_cast
<SwTextField
*>(pTextAttr
));
1401 const_cast<SwFormatField
&>(pTextField
->GetFormatField()).Broadcast(SwFormatFieldHint(&pTextField
->GetFormatField(), eHintType
));
1404 } // anonymous namespace
1406 SwRangeRedline::SwRangeRedline(RedlineType eTyp
, const SwPaM
& rPam
, sal_uInt32 nMovedID
)
1407 : SwPaM( *rPam
.GetMark(), *rPam
.GetPoint() ), m_pRedlineData(
1408 new SwRedlineData(eTyp
, GetDoc().getIDocumentRedlineAccess().GetRedlineAuthor(), nMovedID
) )
1410 m_nId( s_nLastId
++ )
1412 GetBound().SetOwner(this);
1413 GetBound(false).SetOwner(this);
1415 m_bDelLastPara
= false;
1416 m_bIsVisible
= true;
1417 if( !rPam
.HasMark() )
1420 // set default comment for single annotations added or deleted
1421 if ( IsAnnotation() )
1423 SetComment( RedlineType::Delete
== eTyp
1424 ? SwResId(STR_REDLINE_COMMENT_DELETED
)
1425 : SwResId(STR_REDLINE_COMMENT_ADDED
) );
1427 lcl_LOKBroadcastCommentOperation(eTyp
, rPam
);
1431 SwRangeRedline::SwRangeRedline( const SwRedlineData
& rData
, const SwPaM
& rPam
)
1432 : SwPaM( *rPam
.GetMark(), *rPam
.GetPoint() ),
1433 m_pRedlineData( new SwRedlineData( rData
)),
1434 m_nId( s_nLastId
++ )
1436 GetBound().SetOwner(this);
1437 GetBound(false).SetOwner(this);
1439 m_bDelLastPara
= false;
1440 m_bIsVisible
= true;
1441 if( !rPam
.HasMark() )
1444 // set default comment for single annotations added or deleted
1445 if ( IsAnnotation() )
1447 SetComment( RedlineType::Delete
== rData
.m_eType
1448 ? SwResId(STR_REDLINE_COMMENT_DELETED
)
1449 : SwResId(STR_REDLINE_COMMENT_ADDED
) );
1451 lcl_LOKBroadcastCommentOperation(rData
.m_eType
, rPam
);
1455 SwRangeRedline::SwRangeRedline( const SwRedlineData
& rData
, const SwPosition
& rPos
)
1457 m_pRedlineData( new SwRedlineData( rData
)),
1458 m_nId( s_nLastId
++ )
1460 GetBound().SetOwner(this);
1461 GetBound(false).SetOwner(this);
1463 m_bDelLastPara
= false;
1464 m_bIsVisible
= true;
1467 SwRangeRedline::SwRangeRedline( const SwRangeRedline
& rCpy
)
1468 : SwPaM( *rCpy
.GetMark(), *rCpy
.GetPoint() ),
1469 m_pRedlineData( new SwRedlineData( *rCpy
.m_pRedlineData
)),
1470 m_nId( s_nLastId
++ )
1472 GetBound().SetOwner(this);
1473 GetBound(false).SetOwner(this);
1475 m_bDelLastPara
= false;
1476 m_bIsVisible
= true;
1477 if( !rCpy
.HasMark() )
1481 SwRangeRedline::~SwRangeRedline()
1483 if( m_oContentSect
)
1485 // delete the ContentSection
1486 if( !GetDoc().IsInDtor() )
1487 GetDoc().getIDocumentContentOperations().DeleteSection( &m_oContentSect
->GetNode() );
1488 m_oContentSect
.reset();
1490 delete m_pRedlineData
;
1493 void MaybeNotifyRedlineModification(SwRangeRedline
& rRedline
, SwDoc
& rDoc
)
1495 if (!lcl_LOKRedlineNotificationEnabled())
1498 const SwRedlineTable
& rRedTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
1499 for (SwRedlineTable::size_type i
= 0; i
< rRedTable
.size(); ++i
)
1501 if (rRedTable
[i
] == &rRedline
)
1503 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify
, &rRedline
);
1509 void SwRangeRedline::MaybeNotifyRedlinePositionModification(tools::Long nTop
)
1511 if (!lcl_LOKRedlineNotificationEnabled())
1514 if(!m_oLOKLastNodeTop
|| *m_oLOKLastNodeTop
!= nTop
)
1516 m_oLOKLastNodeTop
= nTop
;
1517 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify
, this);
1521 void SwRangeRedline::SetStart( const SwPosition
& rPos
, SwPosition
* pSttPtr
)
1523 if( !pSttPtr
) pSttPtr
= Start();
1526 MaybeNotifyRedlineModification(*this, GetDoc());
1529 void SwRangeRedline::SetEnd( const SwPosition
& rPos
, SwPosition
* pEndPtr
)
1531 if( !pEndPtr
) pEndPtr
= End();
1534 MaybeNotifyRedlineModification(*this, GetDoc());
1537 /// Do we have a valid Selection?
1538 bool SwRangeRedline::HasValidRange() const
1540 const SwNode
* pPtNd
= &GetPoint()->GetNode(),
1541 * pMkNd
= &GetMark()->GetNode();
1542 if( pPtNd
->StartOfSectionNode() == pMkNd
->StartOfSectionNode() &&
1543 !pPtNd
->StartOfSectionNode()->IsTableNode() &&
1544 // invalid if points on the end of content
1545 // end-of-content only invalid if no content index exists
1546 ( pPtNd
!= pMkNd
|| GetContentIdx() != nullptr ||
1547 pPtNd
!= &pPtNd
->GetNodes().GetEndOfContent() )
1553 void SwRangeRedline::CallDisplayFunc(size_t nMyPos
)
1555 RedlineFlags eShow
= RedlineFlags::ShowMask
& GetDoc().getIDocumentRedlineAccess().GetRedlineFlags();
1556 if (eShow
== (RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
))
1558 else if (eShow
== RedlineFlags::ShowInsert
)
1560 else if (eShow
== RedlineFlags::ShowDelete
)
1561 ShowOriginal(0, nMyPos
);
1564 void SwRangeRedline::Show(sal_uInt16 nLoop
, size_t nMyPos
, bool bForced
)
1566 SwDoc
& rDoc
= GetDoc();
1568 bool bIsShowChangesInMargin
= false;
1571 SwViewShell
* pSh
= rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell();
1573 bIsShowChangesInMargin
= pSh
->GetViewOptions()->IsShowChangesInMargin();
1575 bIsShowChangesInMargin
= SwModule::get()->GetUsrPref(false)->IsShowChangesInMargin();
1578 if( 1 > nLoop
&& !bIsShowChangesInMargin
)
1581 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1582 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
1583 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
1587 case RedlineType::Insert
: // Content has been inserted
1588 m_bIsVisible
= true;
1589 MoveFromSection(nMyPos
);
1592 case RedlineType::Delete
: // Content has been deleted
1593 m_bIsVisible
= !bIsShowChangesInMargin
;
1596 MoveFromSection(nMyPos
);
1601 case 0: MoveToSection(); break;
1602 case 1: CopyToSection(); break;
1603 case 2: DelCopyOfSection(nMyPos
); break;
1608 case RedlineType::Format
: // Attributes have been applied
1609 case RedlineType::Table
: // Table structure has been modified
1610 InvalidateRange(Invalidation::Add
);
1615 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1618 void SwRangeRedline::Hide(sal_uInt16 nLoop
, size_t nMyPos
, bool /*bForced*/)
1620 SwDoc
& rDoc
= GetDoc();
1621 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1622 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
1623 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
1627 case RedlineType::Insert
: // Content has been inserted
1628 m_bIsVisible
= true;
1630 MoveFromSection(nMyPos
);
1633 case RedlineType::Delete
: // Content has been deleted
1634 m_bIsVisible
= false;
1637 case 0: MoveToSection(); break;
1638 case 1: CopyToSection(); break;
1639 case 2: DelCopyOfSection(nMyPos
); break;
1643 case RedlineType::Format
: // Attributes have been applied
1644 case RedlineType::Table
: // Table structure has been modified
1646 InvalidateRange(Invalidation::Remove
);
1651 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1654 void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop
, size_t nMyPos
, bool /*bForced*/)
1656 SwDoc
& rDoc
= GetDoc();
1657 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1658 SwRedlineData
* pCur
;
1660 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
1661 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
1663 // Determine the Type, it's the first on Stack
1664 for( pCur
= m_pRedlineData
; pCur
->m_pNext
; )
1665 pCur
= pCur
->m_pNext
;
1667 switch( pCur
->m_eType
)
1669 case RedlineType::Insert
: // Content has been inserted
1670 m_bIsVisible
= false;
1673 case 0: MoveToSection(); break;
1674 case 1: CopyToSection(); break;
1675 case 2: DelCopyOfSection(nMyPos
); break;
1679 case RedlineType::Delete
: // Content has been deleted
1680 m_bIsVisible
= true;
1682 MoveFromSection(nMyPos
);
1685 case RedlineType::Format
: // Attributes have been applied
1686 case RedlineType::Table
: // Table structure has been modified
1688 InvalidateRange(Invalidation::Remove
);
1693 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1696 // trigger the Layout
1697 void SwRangeRedline::InvalidateRange(Invalidation
const eWhy
)
1699 auto [pRStt
, pREnd
] = StartEnd(); // SwPosition*
1700 SwNodeOffset nSttNd
= pRStt
->GetNodeIndex(),
1701 nEndNd
= pREnd
->GetNodeIndex();
1702 sal_Int32 nSttCnt
= pRStt
->GetContentIndex();
1703 sal_Int32 nEndCnt
= pREnd
->GetContentIndex();
1705 SwNodes
& rNds
= GetDoc().GetNodes();
1706 for (SwNodeOffset
n(nSttNd
); n
<= nEndNd
; ++n
)
1708 SwNode
* pNode
= rNds
[n
];
1710 if (pNode
&& pNode
->IsTextNode())
1712 SwTextNode
* pNd
= pNode
->GetTextNode();
1715 n
== nSttNd
? nSttCnt
: 0,
1716 n
== nEndNd
? nEndCnt
: pNd
->GetText().getLength(),
1717 RES_UPDATEATTR_FMT_CHG
);
1719 pNd
->TriggerNodeUpdate(sw::UpdateAttrHint(&aHt
, &aHt
));
1721 // SwUpdateAttr must be handled first, otherwise indexes are off
1722 if (GetType() == RedlineType::Delete
)
1724 sal_Int32
const nStart(n
== nSttNd
? nSttCnt
: 0);
1725 sal_Int32
const nLen((n
== nEndNd
? nEndCnt
: pNd
->GetText().getLength()) - nStart
);
1726 if (eWhy
== Invalidation::Add
)
1728 sw::RedlineDelText
const hint(nStart
, nLen
);
1729 pNd
->CallSwClientNotify(hint
);
1733 sw::RedlineUnDelText
const hint(nStart
, nLen
);
1734 pNd
->CallSwClientNotify(hint
);
1737 if (comphelper::LibreOfficeKit::isActive() && IsAnnotation())
1739 auto eHintType
= eWhy
== Invalidation::Add
? SwFormatFieldHintWhich::INSERTED
: SwFormatFieldHintWhich::REMOVED
;
1740 const SwTextNode
*pTextNode
= this->GetPointNode().GetTextNode();
1741 SwTextAttr
* pTextAttr
= pTextNode
? pTextNode
->GetFieldTextAttrAt(this->GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default
) : nullptr;
1742 SwTextField
*const pTextField(static_txtattr_cast
<SwTextField
*>(pTextAttr
));
1744 const_cast<SwFormatField
&>(pTextField
->GetFormatField()).Broadcast(SwFormatFieldHint(&pTextField
->GetFormatField(), eHintType
));
1751 /** Calculates the start and end position of the intersection rTmp and
1753 void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx
, sal_Int32
& rStart
, sal_Int32
& rEnd
) const
1755 auto [pRStt
, pREnd
] = StartEnd(); // SwPosition*
1756 if( pRStt
->GetNodeIndex() < nNdIdx
)
1758 if( pREnd
->GetNodeIndex() > nNdIdx
)
1760 rStart
= 0; // Paragraph is completely enclosed
1761 rEnd
= COMPLETE_STRING
;
1763 else if (pREnd
->GetNodeIndex() == nNdIdx
)
1765 rStart
= 0; // Paragraph is overlapped in the beginning
1766 rEnd
= pREnd
->GetContentIndex();
1768 else // redline ends before paragraph
1770 rStart
= COMPLETE_STRING
;
1771 rEnd
= COMPLETE_STRING
;
1774 else if( pRStt
->GetNodeIndex() == nNdIdx
)
1776 rStart
= pRStt
->GetContentIndex();
1777 if( pREnd
->GetNodeIndex() == nNdIdx
)
1778 rEnd
= pREnd
->GetContentIndex(); // Within the Paragraph
1780 rEnd
= COMPLETE_STRING
; // Paragraph is overlapped in the end
1784 rStart
= COMPLETE_STRING
;
1785 rEnd
= COMPLETE_STRING
;
1789 static void lcl_storeAnnotationMarks(SwDoc
& rDoc
, const SwPosition
* pStart
, const SwPosition
* pEnd
)
1791 // tdf#115815 keep original start position of collapsed annotation ranges
1792 // as temporary bookmarks (removed after file saving and file loading)
1793 IDocumentMarkAccess
& rDMA(*rDoc
.getIDocumentMarkAccess());
1794 for (auto iter
= rDMA
.findFirstAnnotationMarkNotStartsBefore(*pStart
);
1795 iter
!= rDMA
.getAnnotationMarksEnd(); ++iter
)
1797 SwPosition
const& rStartPos((**iter
).GetMarkStart());
1798 // vector is sorted by start pos, so we can exit early
1799 if ( rStartPos
> *pEnd
)
1801 if ( *pStart
<= rStartPos
&& rStartPos
< *pEnd
)
1803 auto pOldMark
= rDMA
.findAnnotationBookmark((**iter
).GetName());
1804 if ( pOldMark
== rDMA
.getBookmarksEnd() )
1806 // at start of redlines use a 1-character length bookmark range
1807 // instead of a 0-character length bookmark position to avoid its losing
1808 sal_Int32 nLen
= (*pStart
== rStartPos
) ? 1 : 0;
1809 SwPaM
aPam( rStartPos
.GetNode(), rStartPos
.GetContentIndex(),
1810 rStartPos
.GetNode(), rStartPos
.GetContentIndex() + nLen
);
1811 ::sw::mark::Bookmark
* pBookmark
= rDMA
.makeAnnotationBookmark(
1814 sw::mark::InsertMode::New
);
1817 pBookmark
->SetKeyCode(vcl::KeyCode());
1818 pBookmark
->SetShortName(OUString());
1825 void SwRangeRedline::MoveToSection()
1827 if( !m_oContentSect
)
1829 auto [pStart
, pEnd
] = StartEnd(); // SwPosition*
1831 SwDoc
& rDoc
= GetDoc();
1832 SwPaM
aPam( *pStart
, *pEnd
);
1833 SwContentNode
* pCSttNd
= pStart
->GetNode().GetContentNode();
1834 SwContentNode
* pCEndNd
= pEnd
->GetNode().GetContentNode();
1838 // In order to not move other Redlines' indices, we set them
1839 // to the end (is exclusive)
1840 const SwRedlineTable
& rTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
1841 for(SwRangeRedline
* pRedl
: rTable
)
1843 if( pRedl
->GetBound() == *pStart
)
1844 pRedl
->GetBound() = *pEnd
;
1845 if( pRedl
->GetBound(false) == *pStart
)
1846 pRedl
->GetBound(false) = *pEnd
;
1850 SwStartNode
* pSttNd
;
1851 SwNodes
& rNds
= rDoc
.GetNodes();
1852 if( pCSttNd
|| pCEndNd
)
1854 SwTextFormatColl
* pColl
= (pCSttNd
&& pCSttNd
->IsTextNode() )
1855 ? pCSttNd
->GetTextNode()->GetTextColl()
1856 : (pCEndNd
&& pCEndNd
->IsTextNode() )
1857 ? pCEndNd
->GetTextNode()->GetTextColl()
1858 : rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
);
1860 pSttNd
= rNds
.MakeTextSection( rNds
.GetEndOfRedlines(),
1861 SwNormalStartNode
, pColl
);
1862 SwTextNode
* pTextNd
= rNds
[ pSttNd
->GetIndex() + 1 ]->GetTextNode();
1864 SwPosition
aPos( *pTextNd
);
1865 if( pCSttNd
&& pCEndNd
)
1867 // tdf#140982 keep annotation ranges in deletions in margin mode
1868 lcl_storeAnnotationMarks( rDoc
, pStart
, pEnd
);
1869 rDoc
.getIDocumentContentOperations().MoveAndJoin( aPam
, aPos
);
1873 if( pCSttNd
&& !pCEndNd
)
1874 m_bDelLastPara
= true;
1875 rDoc
.getIDocumentContentOperations().MoveRange( aPam
, aPos
,
1876 SwMoveFlags::DEFAULT
);
1881 pSttNd
= SwNodes::MakeEmptySection( rNds
.GetEndOfRedlines() );
1883 SwPosition
aPos( *pSttNd
->EndOfSectionNode() );
1884 rDoc
.getIDocumentContentOperations().MoveRange( aPam
, aPos
,
1885 SwMoveFlags::DEFAULT
);
1887 m_oContentSect
.emplace( *pSttNd
);
1889 if( pStart
== GetPoint() )
1895 InvalidateRange(Invalidation::Remove
);
1898 void SwRangeRedline::CopyToSection()
1900 if( m_oContentSect
)
1903 auto [pStart
, pEnd
] = StartEnd(); // SwPosition*
1905 SwContentNode
* pCSttNd
= pStart
->GetNode().GetContentNode();
1906 SwContentNode
* pCEndNd
= pEnd
->GetNode().GetContentNode();
1908 SwStartNode
* pSttNd
;
1909 SwDoc
& rDoc
= GetDoc();
1910 SwNodes
& rNds
= rDoc
.GetNodes();
1912 bool bSaveCopyFlag
= rDoc
.IsCopyIsMove(),
1913 bSaveRdlMoveFlg
= rDoc
.getIDocumentRedlineAccess().IsRedlineMove();
1914 rDoc
.SetCopyIsMove( true );
1916 // The IsRedlineMove() flag causes the behaviour of the
1917 // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change,
1918 // which will eventually be called by the CopyRange() below.
1919 rDoc
.getIDocumentRedlineAccess().SetRedlineMove(true);
1923 SwTextFormatColl
* pColl
= pCSttNd
->IsTextNode()
1924 ? pCSttNd
->GetTextNode()->GetTextColl()
1925 : rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
);
1927 pSttNd
= rNds
.MakeTextSection( rNds
.GetEndOfRedlines(),
1928 SwNormalStartNode
, pColl
);
1930 SwPosition
aPos( *pSttNd
, SwNodeOffset(1) );
1932 // tdf#115815 keep original start position of collapsed annotation ranges
1933 // as temporary bookmarks (removed after file saving and file loading)
1934 lcl_storeAnnotationMarks( rDoc
, pStart
, pEnd
);
1935 rDoc
.getIDocumentContentOperations().CopyRange(*this, aPos
, SwCopyFlags::CheckPosInFly
);
1937 // Take over the style from the EndNode if needed
1938 // We don't want this in Doc::Copy
1939 if( pCEndNd
&& pCEndNd
!= pCSttNd
)
1941 SwContentNode
* pDestNd
= aPos
.GetNode().GetContentNode();
1944 if( pDestNd
->IsTextNode() && pCEndNd
->IsTextNode() )
1945 pCEndNd
->GetTextNode()->CopyCollFormat(*pDestNd
->GetTextNode());
1947 pDestNd
->ChgFormatColl( pCEndNd
->GetFormatColl() );
1953 pSttNd
= SwNodes::MakeEmptySection( rNds
.GetEndOfRedlines() );
1957 SwPosition
aPos( *pSttNd
->EndOfSectionNode() );
1958 rDoc
.getIDocumentContentOperations().CopyRange(*this, aPos
, SwCopyFlags::CheckPosInFly
);
1962 SwNodeRange
aRg( pStart
->GetNode(), SwNodeOffset(0), pEnd
->GetNode(), SwNodeOffset(1) );
1963 rDoc
.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg
, *pSttNd
->EndOfSectionNode());
1966 m_oContentSect
.emplace( *pSttNd
);
1968 rDoc
.SetCopyIsMove( bSaveCopyFlag
);
1969 rDoc
.getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg
);
1972 void SwRangeRedline::DelCopyOfSection(size_t nMyPos
)
1974 if( !m_oContentSect
)
1977 auto [pStart
, pEnd
] = StartEnd(); // SwPosition*
1979 SwDoc
& rDoc
= GetDoc();
1980 SwPaM
aPam( *pStart
, *pEnd
);
1981 SwContentNode
* pCSttNd
= pStart
->GetNode().GetContentNode();
1982 SwContentNode
* pCEndNd
= pEnd
->GetNode().GetContentNode();
1986 // In order to not move other Redlines' indices, we set them
1987 // to the end (is exclusive)
1988 const SwRedlineTable
& rTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
1989 for(SwRangeRedline
* pRedl
: rTable
)
1991 if( pRedl
->GetBound() == *pStart
)
1992 pRedl
->GetBound() = *pEnd
;
1993 if( pRedl
->GetBound(false) == *pStart
)
1994 pRedl
->GetBound(false) = *pEnd
;
1998 if( pCSttNd
&& pCEndNd
)
2000 // #i100466# - force a <join next> on <delete and join> operation
2001 // tdf#125319 - rather not?
2002 rDoc
.getIDocumentContentOperations().DeleteAndJoin(aPam
/*, true*/);
2004 else if( pCSttNd
|| pCEndNd
)
2006 if( pCSttNd
&& !pCEndNd
)
2007 m_bDelLastPara
= true;
2008 rDoc
.getIDocumentContentOperations().DeleteRange( aPam
);
2010 if( m_bDelLastPara
)
2012 // To prevent dangling references to the paragraph to
2013 // be deleted, redline that point into this paragraph should be
2014 // moved to the new end position. Since redlines in the redline
2015 // table are sorted and the pEnd position is an endnode (see
2016 // bDelLastPara condition above), only redlines before the
2017 // current ones can be affected.
2018 const SwRedlineTable
& rTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
2020 for( bool bBreak
= false; !bBreak
&& n
> 0; )
2024 if( rTable
[ n
]->GetBound() == *aPam
.GetPoint() )
2026 rTable
[ n
]->GetBound() = *pEnd
;
2029 if( rTable
[ n
]->GetBound(false) == *aPam
.GetPoint() )
2031 rTable
[ n
]->GetBound(false) = *pEnd
;
2036 *GetPoint() = *pEnd
;
2041 aPam
.GetPoint()->SetContent(0);;
2042 rDoc
.getIDocumentContentOperations().DelFullPara( aPam
);
2047 rDoc
.getIDocumentContentOperations().DeleteRange( aPam
);
2050 if( pStart
== GetPoint() )
2056 void SwRangeRedline::MoveFromSection(size_t nMyPos
)
2058 if( m_oContentSect
)
2060 SwDoc
& rDoc
= GetDoc();
2061 const SwRedlineTable
& rTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
2062 std::vector
<SwPosition
*> aBeforeArr
, aBehindArr
;
2063 bool bBreak
= false;
2064 SwRedlineTable::size_type n
;
2066 for( n
= nMyPos
+1; !bBreak
&& n
< rTable
.size(); ++n
)
2069 if( rTable
[ n
]->GetBound() == *GetPoint() )
2071 SwRangeRedline
* pRedl
= rTable
[n
];
2072 aBehindArr
.push_back(&pRedl
->GetBound());
2075 if( rTable
[ n
]->GetBound(false) == *GetPoint() )
2077 SwRangeRedline
* pRedl
= rTable
[n
];
2078 aBehindArr
.push_back(&pRedl
->GetBound(false));
2082 for( bBreak
= false, n
= nMyPos
; !bBreak
&& n
; )
2086 if( rTable
[ n
]->GetBound() == *GetPoint() )
2088 SwRangeRedline
* pRedl
= rTable
[n
];
2089 aBeforeArr
.push_back(&pRedl
->GetBound());
2092 if( rTable
[ n
]->GetBound(false) == *GetPoint() )
2094 SwRangeRedline
* pRedl
= rTable
[n
];
2095 aBeforeArr
.push_back(&pRedl
->GetBound(false));
2100 const SwNode
* pKeptContentSectNode( &m_oContentSect
->GetNode() ); // #i95711#
2102 SwPaM
aPam( m_oContentSect
->GetNode(),
2103 *m_oContentSect
->GetNode().EndOfSectionNode(), SwNodeOffset(1),
2104 SwNodeOffset( m_bDelLastPara
? -2 : -1 ) );
2105 SwContentNode
* pCNd
= aPam
.GetPointContentNode();
2107 aPam
.GetPoint()->SetContent( pCNd
->Len() );
2109 aPam
.GetPoint()->Adjust(SwNodeOffset(+1));
2111 SwFormatColl
* pColl
= pCNd
&& pCNd
->Len() && aPam
.GetPoint()->GetNode() !=
2112 aPam
.GetMark()->GetNode()
2113 ? pCNd
->GetFormatColl() : nullptr;
2115 SwNodeIndex
aNdIdx( GetPoint()->GetNode(), -1 );
2116 const sal_Int32 nPos
= GetPoint()->GetContentIndex();
2118 SwPosition
aPos( *GetPoint() );
2119 if( m_bDelLastPara
&& *aPam
.GetPoint() == *aPam
.GetMark() )
2121 aPos
.Adjust(SwNodeOffset(-1));
2123 rDoc
.getIDocumentContentOperations().AppendTextNode( aPos
);
2127 rDoc
.getIDocumentContentOperations().MoveRange( aPam
, aPos
,
2128 SwMoveFlags::ALLFLYS
);
2132 *GetPoint() = std::move(aPos
);
2133 GetMark()->Assign(aNdIdx
.GetIndex() + 1);
2134 pCNd
= GetMark()->GetNode().GetContentNode();
2136 GetMark()->SetContent( nPos
);
2138 if( m_bDelLastPara
)
2140 GetPoint()->Adjust(SwNodeOffset(+1));
2141 pCNd
= GetPointContentNode();
2142 m_bDelLastPara
= false;
2145 pCNd
= GetPointContentNode();
2148 pCNd
->ChgFormatColl( pColl
);
2152 // Under certain conditions the previous <SwDoc::Move(..)> has already
2153 // removed the change tracking section of this <SwRangeRedline> instance from
2154 // the change tracking nodes area.
2155 // Thus, check if <pContentSect> still points to the change tracking section
2156 // by comparing it with the "indexed" <SwNode> instance copied before
2157 // perform the intrinsic move.
2158 // Note: Such condition is e.g. a "delete" change tracking only containing a table.
2159 if ( &m_oContentSect
->GetNode() == pKeptContentSectNode
)
2161 rDoc
.getIDocumentContentOperations().DeleteSection( &m_oContentSect
->GetNode() );
2163 m_oContentSect
.reset();
2165 // adjustment of redline table positions must take start and
2166 // end into account, not point and mark.
2167 for( auto& pItem
: aBeforeArr
)
2169 for( auto& pItem
: aBehindArr
)
2173 InvalidateRange(Invalidation::Add
);
2177 void SwRangeRedline::SetContentIdx( const SwNodeIndex
& rIdx
)
2179 if( !m_oContentSect
)
2181 m_oContentSect
= rIdx
;
2182 m_bIsVisible
= false;
2186 OSL_FAIL("SwRangeRedline::SetContentIdx: invalid state");
2191 void SwRangeRedline::ClearContentIdx()
2193 if( m_oContentSect
)
2195 m_oContentSect
.reset();
2199 OSL_FAIL("SwRangeRedline::ClearContentIdx: invalid state");
2203 bool SwRangeRedline::CanCombine( const SwRangeRedline
& rRedl
) const
2205 return IsVisible() && rRedl
.IsVisible() &&
2206 m_pRedlineData
->CanCombine( *rRedl
.m_pRedlineData
);
2209 void SwRangeRedline::PushData( const SwRangeRedline
& rRedl
, bool bOwnAsNext
)
2211 SwRedlineData
* pNew
= new SwRedlineData( *rRedl
.m_pRedlineData
, false );
2214 pNew
->m_pNext
= m_pRedlineData
;
2215 m_pRedlineData
= pNew
;
2219 pNew
->m_pNext
= m_pRedlineData
->m_pNext
;
2220 m_pRedlineData
->m_pNext
= pNew
;
2224 bool SwRangeRedline::PopData()
2226 if( !m_pRedlineData
->m_pNext
)
2228 SwRedlineData
* pCur
= m_pRedlineData
;
2229 m_pRedlineData
= pCur
->m_pNext
;
2230 pCur
->m_pNext
= nullptr;
2235 bool SwRangeRedline::PopAllDataAfter(int depth
)
2238 SwRedlineData
* pCur
= m_pRedlineData
;
2241 pCur
= pCur
->m_pNext
;
2247 while (pCur
->m_pNext
)
2249 SwRedlineData
* pToDelete
= pCur
->m_pNext
;
2250 pCur
->m_pNext
= pToDelete
->m_pNext
;
2256 sal_uInt16
SwRangeRedline::GetStackCount() const
2258 sal_uInt16 nRet
= 1;
2259 for( SwRedlineData
* pCur
= m_pRedlineData
; pCur
->m_pNext
; pCur
= pCur
->m_pNext
)
2264 std::size_t SwRangeRedline::GetAuthor( sal_uInt16 nPos
) const
2266 return GetRedlineData(nPos
).m_nAuthor
;
2269 OUString
const & SwRangeRedline::GetAuthorString( sal_uInt16 nPos
) const
2271 return SwModule::get()->GetRedlineAuthor(GetRedlineData(nPos
).m_nAuthor
);
2274 sal_uInt32
SwRangeRedline::GetMovedID(sal_uInt16 nPos
) const
2276 return GetRedlineData(nPos
).m_nMovedID
;
2279 const DateTime
& SwRangeRedline::GetTimeStamp(sal_uInt16 nPos
) const
2281 return GetRedlineData(nPos
).m_aStamp
;
2284 RedlineType
SwRangeRedline::GetType( sal_uInt16 nPos
) const
2286 return GetRedlineData(nPos
).m_eType
;
2289 bool SwRangeRedline::IsAnnotation() const
2291 return GetText().getLength() == 1 && GetText()[0] == CH_TXTATR_INWORD
;
2294 const OUString
& SwRangeRedline::GetComment( sal_uInt16 nPos
) const
2296 return GetRedlineData(nPos
).m_sComment
;
2299 bool SwRangeRedline::operator<( const SwRangeRedline
& rCmp
) const
2301 auto [pStart
, pEnd
] = StartEnd();
2302 auto [pCmpStart
, pCmpEnd
] = rCmp
.StartEnd();
2303 if (*pStart
< *pCmpStart
)
2306 return *pStart
== *pCmpStart
&& *pEnd
< *pCmpEnd
;
2309 const SwRedlineData
& SwRangeRedline::GetRedlineData(const sal_uInt16 nPos
) const
2311 SwRedlineData
* pCur
= m_pRedlineData
;
2313 sal_uInt16 nP
= nPos
;
2315 while (nP
> 0 && nullptr != pCur
->m_pNext
)
2317 pCur
= pCur
->m_pNext
;
2322 SAL_WARN_IF( nP
!= 0, "sw.core", "Pos " << nPos
<< " is " << nP
<< " too big");
2327 OUString
SwRangeRedline::GetDescr(bool bSimplified
)
2329 // get description of redline data (e.g.: "insert $1")
2330 OUString aResult
= GetRedlineData().GetDescr();
2332 SwPaM
* pPaM
= nullptr;
2333 bool bDeletePaM
= false;
2335 // if this redline is visible the content is in this PaM
2336 if (!m_oContentSect
.has_value())
2340 else // otherwise it is saved in pContentSect
2342 pPaM
= new SwPaM( m_oContentSect
->GetNode(), *m_oContentSect
->GetNode().EndOfSectionNode() );
2346 OUString sDescr
= DenoteSpecialCharacters(pPaM
->GetText().replace('\n', ' '), /*bQuoted=*/!bSimplified
);
2347 if (const SwTextNode
*pTextNode
= pPaM
->GetPointNode().GetTextNode())
2349 if (const SwTextAttr
* pTextAttr
= pTextNode
->GetFieldTextAttrAt(pPaM
->GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default
))
2351 sDescr
= ( bSimplified
? u
""_ustr
: SwResId(STR_START_QUOTE
) )
2352 + pTextAttr
->GetFormatField().GetField()->GetFieldName()
2353 + ( bSimplified
? u
""_ustr
: SwResId(STR_END_QUOTE
) );
2357 // replace $1 in description by description of the redlines text
2358 const OUString aTmpStr
= ShortenString(sDescr
, nUndoStringLength
, SwResId(STR_LDOTS
));
2362 SwRewriter aRewriter
;
2363 aRewriter
.AddRule(UndoArg1
, aTmpStr
);
2365 aResult
= aRewriter
.Apply(aResult
);
2371 sal_Int32 nPos
= aTmpStr
.indexOf(SwResId(STR_LDOTS
));
2373 aResult
= aTmpStr
.copy(0, nPos
+ SwResId(STR_LDOTS
).getLength());
2382 void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter
) const
2384 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwRangeRedline"));
2386 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
2388 const SwRedlineData
* pRedlineData
= m_pRedlineData
;
2389 while (pRedlineData
)
2391 pRedlineData
->dumpAsXml(pWriter
);
2392 pRedlineData
= pRedlineData
->Next();
2395 SwPaM::dumpAsXml(pWriter
);
2397 (void)xmlTextWriterEndElement(pWriter
);
2400 void SwExtraRedlineTable::Insert( SwExtraRedline
* p
)
2402 m_aExtraRedlines
.push_back( p
);
2403 //p->CallDisplayFunc();
2406 void SwExtraRedlineTable::DeleteAndDestroy(sal_uInt16
const nPos
)
2410 if( !nP && nL && nL == size() )
2411 pDoc = front()->GetDoc();
2414 delete m_aExtraRedlines
[nPos
];
2415 m_aExtraRedlines
.erase(m_aExtraRedlines
.begin() + nPos
);
2419 if( pDoc && !pDoc->IsInDtor() &&
2420 0 != ( pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) )
2421 pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
2425 void SwExtraRedlineTable::DeleteAndDestroyAll()
2427 while (!m_aExtraRedlines
.empty())
2429 auto const pRedline
= m_aExtraRedlines
.back();
2430 m_aExtraRedlines
.pop_back();
2435 SwExtraRedline::~SwExtraRedline()
2439 SwTableRowRedline::SwTableRowRedline(const SwRedlineData
& rData
, const SwTableLine
& rTableLine
)
2440 : m_aRedlineData(rData
)
2441 , m_rTableLine(rTableLine
)
2445 SwTableRowRedline::~SwTableRowRedline()
2449 SwTableCellRedline::SwTableCellRedline(const SwRedlineData
& rData
, const SwTableBox
& rTableBox
)
2450 : m_aRedlineData(rData
)
2451 , m_rTableBox(rTableBox
)
2455 SwTableCellRedline::~SwTableCellRedline()
2459 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */