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 <UndoRedline.hxx>
21 #include <hintids.hxx>
22 #include <osl/diagnose.h>
23 #include <unotools/charclass.hxx>
25 #include <IDocumentRedlineAccess.hxx>
32 #include <UndoCore.hxx>
33 #include <UndoDelete.hxx>
34 #include <strings.hrc>
35 #include <redline.hxx>
37 #include <sortopt.hxx>
40 SwUndoRedline::SwUndoRedline( SwUndoId nUsrId
, const SwPaM
& rRange
)
41 : SwUndo( SwUndoId::REDLINE
, &rRange
.GetDoc() ), SwUndRng( rRange
),
43 mbHiddenRedlines( false )
46 SwDoc
& rDoc
= rRange
.GetDoc();
47 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
51 case SwUndoId::DELETE
:
52 case SwUndoId::REPLACE
:
53 mpRedlData
.reset( new SwRedlineData( RedlineType::Delete
, rDoc
.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
58 SetRedlineFlags( rDoc
.getIDocumentRedlineAccess().GetRedlineFlags() );
61 SwNodeOffset nEndExtra
= rDoc
.GetNodes().GetEndOfExtras().GetIndex();
63 mpRedlSaveData
.reset( new SwRedlineSaveDatas
);
64 if( !FillSaveData( rRange
, *mpRedlSaveData
, false, SwUndoId::REJECT_REDLINE
!= mnUserId
))
66 mpRedlSaveData
.reset();
70 mbHiddenRedlines
= HasHiddenRedlines( *mpRedlSaveData
);
71 if( mbHiddenRedlines
) // then the NodeIndices of SwUndRng need to be corrected
73 nEndExtra
-= rDoc
.GetNodes().GetEndOfExtras().GetIndex();
74 m_nSttNode
-= nEndExtra
;
75 m_nEndNode
-= nEndExtra
;
80 SwUndoRedline::~SwUndoRedline()
83 mpRedlSaveData
.reset();
86 sal_uInt16
SwUndoRedline::GetRedlSaveCount() const
88 return mpRedlSaveData
? mpRedlSaveData
->size() : 0;
91 void SwUndoRedline::UndoImpl(::sw::UndoRedoContext
& rContext
)
93 SwDoc
& rDoc
= rContext
.GetDoc();
94 SwPaM
& rPam(AddUndoRedoPaM(rContext
));
96 // fix PaM for deletions shown in margin
97 bool bIsDeletion
= dynamic_cast<SwUndoRedlineDelete
*>(this);
98 const SwRedlineTable
& rTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
99 sal_uInt32 nMaxId
= SAL_MAX_UINT32
;
100 if ( bIsDeletion
&& rTable
.size() > 0 )
102 // Nodes of the deletion range are in the newest invisible redlines.
103 // Set all redlines visible and recover the original deletion range.
104 for (SwNodeOffset
nNodes(0); nNodes
< m_nEndNode
- m_nSttNode
+ 1; ++nNodes
)
106 SwRedlineTable::size_type nCurRedlinePos
= 0;
107 SwRangeRedline
* pRedline(rTable
[nCurRedlinePos
]);
109 // search last but nNodes redline by its nth biggest id
110 for( SwRedlineTable::size_type n
= 1; n
< rTable
.size(); ++n
)
112 SwRangeRedline
*pRed(rTable
[n
]);
113 if ( !pRed
->HasMark() && pRedline
->GetId() < pRed
->GetId() && pRed
->GetId() < nMaxId
)
120 nMaxId
= pRedline
->GetId();
122 if ( !pRedline
->IsVisible() && !pRedline
->HasMark() )
125 pRedline
->Show(0, rTable
.GetPos(pRedline
), /*bForced=*/true);
126 pRedline
->Show(1, rTable
.GetPos(pRedline
), /*bForced=*/true);
129 if ( nNodes
== SwNodeOffset(0) )
134 *rPam
.GetMark() = *pRedline
->GetMark();
140 UndoRedlineImpl(rDoc
, rPam
);
144 SwNodeOffset nEndExtra
= rDoc
.GetNodes().GetEndOfExtras().GetIndex();
145 SetSaveData(rDoc
, *mpRedlSaveData
);
146 if( mbHiddenRedlines
)
148 mpRedlSaveData
->clear();
150 nEndExtra
= rDoc
.GetNodes().GetEndOfExtras().GetIndex() - nEndExtra
;
151 m_nSttNode
+= nEndExtra
;
152 m_nEndNode
+= nEndExtra
;
157 // update frames after calling SetSaveData
160 sw::UpdateFramesForRemoveDeleteRedline(rDoc
, rPam
);
162 else if (dynamic_cast<SwUndoAcceptRedline
*>(this)
163 || dynamic_cast<SwUndoRejectRedline
*>(this))
164 { // (can't check here if there's a delete redline being accepted)
165 sw::UpdateFramesForAddDeleteRedline(rDoc
, rPam
);
169 void SwUndoRedline::RedoImpl(::sw::UndoRedoContext
& rContext
)
171 SwDoc
& rDoc
= rContext
.GetDoc();
172 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
173 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
175 SwPaM
& rPam( AddUndoRedoPaM(rContext
) );
176 if( mpRedlSaveData
&& mbHiddenRedlines
)
178 SwNodeOffset nEndExtra
= rDoc
.GetNodes().GetEndOfExtras().GetIndex();
179 FillSaveData(rPam
, *mpRedlSaveData
, false, SwUndoId::REJECT_REDLINE
!= mnUserId
);
181 nEndExtra
-= rDoc
.GetNodes().GetEndOfExtras().GetIndex();
182 m_nSttNode
-= nEndExtra
;
183 m_nEndNode
-= nEndExtra
;
186 RedoRedlineImpl(rDoc
, rPam
);
189 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
192 void SwUndoRedline::UndoRedlineImpl(SwDoc
&, SwPaM
&)
196 // default: remove redlines
197 void SwUndoRedline::RedoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
199 rDoc
.getIDocumentRedlineAccess().DeleteRedline(rPam
, true, RedlineType::Any
);
202 SwUndoRedlineDelete::SwUndoRedlineDelete(
203 const SwPaM
& rRange
, SwUndoId
const nUsrId
, SwDeleteFlags
const flags
)
204 : SwUndoRedline( nUsrId
!= SwUndoId::EMPTY
? nUsrId
: SwUndoId::DELETE
, rRange
),
205 m_bCanGroup( false ), m_bIsDelim( false ), m_bIsBackspace( false )
207 const SwTextNode
* pTNd
;
208 SetRedlineText(rRange
.GetText());
209 if( SwUndoId::DELETE
== mnUserId
&&
210 m_nSttNode
== m_nEndNode
&& m_nSttContent
+ 1 == m_nEndContent
&&
211 nullptr != (pTNd
= rRange
.GetPointNode().GetTextNode()) )
213 sal_Unicode
const cCh
= pTNd
->GetText()[m_nSttContent
];
214 if( CH_TXTATR_BREAKWORD
!= cCh
&& CH_TXTATR_INWORD
!= cCh
)
217 m_bIsDelim
= !GetAppCharClass().isLetterNumeric( pTNd
->GetText(),
219 m_bIsBackspace
= m_nSttContent
== rRange
.GetPoint()->GetContentIndex();
223 m_bCacheComment
= false;
224 if (flags
& SwDeleteFlags::ArtificialSelection
)
230 void SwUndoRedlineDelete::InitHistory(SwPaM
const& rRedline
)
232 m_pHistory
.reset(new SwHistory
);
233 // try to rely on direction of rPam here so it works for
234 // backspacing/deleting consecutive characters
236 SaveFlyInRange(rRedline
, *rRedline
.GetMark(), flys
, false, m_pHistory
.get());
237 RestFlyInRange(flys
, *rRedline
.GetPoint(), &rRedline
.GetPoint()->GetNode(), true);
238 if (m_pHistory
->Count())
240 m_bCanGroup
= false; // how to group history?
244 // bit of a hack, replace everything...
245 SwRewriter
SwUndoRedlineDelete::GetRewriter() const
248 OUString aStr
= DenoteSpecialCharacters(m_sRedlineText
);
249 aStr
= ShortenString(aStr
, nUndoStringLength
, SwResId(STR_LDOTS
));
250 SwRewriter aRewriter
;
251 aRewriter
.AddRule(UndoArg1
, aStr
);
252 OUString ret
= aRewriter
.Apply(SwResId(STR_UNDO_REDLINE_DELETE
));
253 aResult
.AddRule(UndoArg1
, ret
);
257 void SwUndoRedlineDelete::SetRedlineText(const OUString
& rText
)
259 m_sRedlineText
= rText
;
262 void SwUndoRedlineDelete::UndoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
264 rDoc
.getIDocumentRedlineAccess().DeleteRedline(rPam
, true, RedlineType::Any
);
267 m_pHistory
->TmpRollback(&rDoc
, 0);
271 void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
273 if (rPam
.GetPoint() != rPam
.GetMark())
275 if (m_pHistory
) // if it was created before, it must be recreated now
277 rPam
.Normalize(m_bIsBackspace
); // to check the correct edge
280 rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(*mpRedlData
, rPam
), false );
282 sw::UpdateFramesForAddDeleteRedline(rDoc
, rPam
);
285 bool SwUndoRedlineDelete::CanGrouping( const SwUndoRedlineDelete
& rNext
)
288 if( SwUndoId::DELETE
== mnUserId
&& mnUserId
== rNext
.mnUserId
&&
289 m_bCanGroup
&& rNext
.m_bCanGroup
&&
290 m_bIsDelim
== rNext
.m_bIsDelim
&&
291 m_bIsBackspace
== rNext
.m_bIsBackspace
&&
292 m_nSttNode
== m_nEndNode
&&
293 rNext
.m_nSttNode
== m_nSttNode
&&
294 rNext
.m_nEndNode
== m_nEndNode
)
297 if( rNext
.m_nSttContent
== m_nEndContent
)
299 else if( rNext
.m_nEndContent
== m_nSttContent
)
303 (( !mpRedlSaveData
&& !rNext
.mpRedlSaveData
) ||
304 ( mpRedlSaveData
&& rNext
.mpRedlSaveData
&&
305 SwUndo::CanRedlineGroup( *mpRedlSaveData
,
306 *rNext
.mpRedlSaveData
, 1 != bIsEnd
)
310 m_nEndContent
= rNext
.m_nEndContent
;
312 m_nSttContent
= rNext
.m_nSttContent
;
319 SwUndoRedlineSort::SwUndoRedlineSort( const SwPaM
& rRange
,
320 const SwSortOptions
& rOpt
)
321 : SwUndoRedline( SwUndoId::SORT_TXT
, rRange
),
322 m_pOpt( new SwSortOptions( rOpt
) ),
323 m_nSaveEndNode( m_nEndNode
), m_nSaveEndContent( m_nEndContent
)
327 SwUndoRedlineSort::~SwUndoRedlineSort()
331 void SwUndoRedlineSort::UndoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
333 // rPam contains the sorted range
334 // aSaveRange contains copied (i.e. original) range
336 auto [pStart
, pEnd
] = rPam
.StartEnd(); // SwPosition*
338 SwNodeIndex
aPrevIdx( pStart
->GetNode(), -1 );
339 SwNodeOffset nOffsetTemp
= pEnd
->GetNodeIndex() - pStart
->GetNodeIndex();
341 if( !( RedlineFlags::ShowDelete
& rDoc
.getIDocumentRedlineAccess().GetRedlineFlags()) )
343 // Search both Redline objects and make them visible to make the nodes
344 // consistent again. The 'delete' one is hidden, thus search for the
345 // 'insert' Redline object. The former is located directly after the latter.
346 SwRedlineTable::size_type nFnd
= rDoc
.getIDocumentRedlineAccess().GetRedlinePos(
347 *rDoc
.GetNodes()[ m_nSttNode
+ 1 ],
348 RedlineType::Insert
);
349 OSL_ENSURE( SwRedlineTable::npos
!= nFnd
&& nFnd
+1 < rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size(),
350 "could not find an Insert object" );
352 rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[nFnd
]->Show(1, nFnd
);
356 SwPaM
aTmp( rPam
.GetMark()->GetNode() );
358 aTmp
.GetPoint()->Assign( m_nSaveEndNode
, m_nSaveEndContent
);
359 rDoc
.getIDocumentRedlineAccess().DeleteRedline( aTmp
, true, RedlineType::Any
);
362 rDoc
.getIDocumentContentOperations().DelFullPara(rPam
);
364 SwPaM
*const pPam
= & rPam
;
366 pPam
->GetPoint()->Assign( aPrevIdx
.GetNode(), SwNodeOffset(+1) );
369 pPam
->GetPoint()->Adjust(nOffsetTemp
);
370 SwContentNode
* pCNd
= pPam
->GetPointContentNode();
371 pPam
->GetPoint()->SetContent( pCNd
->Len() );
378 void SwUndoRedlineSort::RedoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
381 auto [pStart
, pEnd
] = pPam
->StartEnd(); // SwPosition*
383 SwNodeIndex
aPrevIdx( pStart
->GetNode(), -1 );
384 SwNodeOffset nOffsetTemp
= pEnd
->GetNodeIndex() - pStart
->GetNodeIndex();
385 const sal_Int32 nCntStt
= pStart
->GetContentIndex();
387 rDoc
.SortText(rPam
, *m_pOpt
);
390 pPam
->GetPoint()->Assign( aPrevIdx
.GetNode(), SwNodeOffset(+1) );
391 SwContentNode
* pCNd
= pPam
->GetPointContentNode();
392 sal_Int32 nLen
= pCNd
->Len();
395 pPam
->GetPoint()->SetContent( nLen
);
398 pPam
->GetPoint()->Adjust(nOffsetTemp
);
399 pCNd
= pPam
->GetPointContentNode();
400 pPam
->GetPoint()->SetContent( pCNd
->Len() );
405 rPam
.GetPoint()->Assign( m_nSaveEndNode
, m_nSaveEndContent
);
408 void SwUndoRedlineSort::RepeatImpl(::sw::RepeatContext
& rContext
)
410 rContext
.GetDoc().SortText( rContext
.GetRepeatPaM(), *m_pOpt
);
413 void SwUndoRedlineSort::SetSaveRange( const SwPaM
& rRange
)
415 const SwPosition
& rPos
= *rRange
.End();
416 m_nSaveEndNode
= rPos
.GetNodeIndex();
417 m_nSaveEndContent
= rPos
.GetContentIndex();
420 SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM
& rRange
)
421 : SwUndoRedline( SwUndoId::ACCEPT_REDLINE
, rRange
)
425 void SwUndoAcceptRedline::RedoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
427 rDoc
.getIDocumentRedlineAccess().AcceptRedline(rPam
, false);
430 void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext
& rContext
)
432 rContext
.GetDoc().getIDocumentRedlineAccess().AcceptRedline(rContext
.GetRepeatPaM(), true);
435 SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM
& rRange
)
436 : SwUndoRedline( SwUndoId::REJECT_REDLINE
, rRange
)
440 void SwUndoRejectRedline::RedoRedlineImpl(SwDoc
& rDoc
, SwPaM
& rPam
)
442 rDoc
.getIDocumentRedlineAccess().RejectRedline(rPam
, false);
445 void SwUndoRejectRedline::RepeatImpl(::sw::RepeatContext
& rContext
)
447 rContext
.GetDoc().getIDocumentRedlineAccess().RejectRedline(rContext
.GetRepeatPaM(), true);
450 SwUndoCompDoc::SwUndoCompDoc( const SwPaM
& rRg
, bool bIns
)
451 : SwUndo( SwUndoId::COMPAREDOC
, &rRg
.GetDoc() ), SwUndRng( rRg
),
454 SwDoc
& rDoc
= rRg
.GetDoc();
455 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
457 RedlineType eTyp
= m_bInsert
? RedlineType::Insert
: RedlineType::Delete
;
458 m_pRedlineData
.reset( new SwRedlineData( eTyp
, rDoc
.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
459 SetRedlineFlags( rDoc
.getIDocumentRedlineAccess().GetRedlineFlags() );
463 SwUndoCompDoc::SwUndoCompDoc( const SwRangeRedline
& rRedl
)
464 : SwUndo( SwUndoId::COMPAREDOC
, &rRedl
.GetDoc() ), SwUndRng( rRedl
),
465 // for MergeDoc the corresponding inverse is needed
466 m_bInsert( RedlineType::Delete
== rRedl
.GetType() )
468 SwDoc
& rDoc
= rRedl
.GetDoc();
469 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
471 m_pRedlineData
.reset( new SwRedlineData( rRedl
.GetRedlineData() ) );
472 SetRedlineFlags( rDoc
.getIDocumentRedlineAccess().GetRedlineFlags() );
475 m_pRedlineSaveDatas
.reset( new SwRedlineSaveDatas
);
476 if( !FillSaveData( rRedl
, *m_pRedlineSaveDatas
, false ))
478 m_pRedlineSaveDatas
.reset();
482 SwUndoCompDoc::~SwUndoCompDoc()
484 m_pRedlineData
.reset();
485 m_pUndoDelete
.reset();
486 m_pUndoDelete2
.reset();
487 m_pRedlineSaveDatas
.reset();
490 void SwUndoCompDoc::UndoImpl(::sw::UndoRedoContext
& rContext
)
492 SwDoc
& rDoc
= rContext
.GetDoc();
493 SwPaM
& rPam(AddUndoRedoPaM(rContext
));
498 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
499 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
501 rDoc
.getIDocumentRedlineAccess().DeleteRedline(rPam
, true, RedlineType::Any
);
503 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
505 // per definition Point is end (in SwUndRng!)
506 SwContentNode
* pCSttNd
= rPam
.GetMarkContentNode();
507 SwContentNode
* pCEndNd
= rPam
.GetPointContentNode();
509 // if start- and end-content is zero, then the doc-compare moves
510 // complete nodes into the current doc. And then the selection
511 // must be from end to start, so the delete join into the right
513 if( !m_nSttContent
&& !m_nEndContent
)
516 bool bJoinText
, bJoinPrev
;
517 sw_GetJoinFlags(rPam
, bJoinText
, bJoinPrev
);
519 m_pUndoDelete
.reset(new SwUndoDelete(rPam
, SwDeleteFlags::Default
, false));
522 sw_JoinText(rPam
, bJoinPrev
);
524 if( pCSttNd
&& !pCEndNd
)
526 // #112139# Do not step behind the end of content.
527 SwNode
& rTmp
= rPam
.GetPointNode();
528 SwNode
* pEnd
= rDoc
.GetNodes().DocumentSectionEndNode(&rTmp
);
533 rPam
.GetPoint()->Adjust(SwNodeOffset(1));
534 m_pUndoDelete2
.reset(new SwUndoDelete(rPam
, SwDeleteFlags::Default
, true));
541 if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
543 rDoc
.getIDocumentRedlineAccess().DeleteRedline(rPam
, true, RedlineType::Any
);
545 if( m_pRedlineSaveDatas
)
546 SetSaveData(rDoc
, *m_pRedlineSaveDatas
);
552 void SwUndoCompDoc::RedoImpl(::sw::UndoRedoContext
& rContext
)
554 SwDoc
& rDoc
= rContext
.GetDoc();
558 SwPaM
& rPam(AddUndoRedoPaM(rContext
));
559 if( m_pRedlineData
&& IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
561 SwRangeRedline
* pTmp
= new SwRangeRedline(*m_pRedlineData
, rPam
);
562 rDoc
.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp
);
563 pTmp
->InvalidateRange(SwRangeRedline::Invalidation::Add
);
565 else if( !( RedlineFlags::Ignore
& GetRedlineFlags() ) &&
566 !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
568 rDoc
.getIDocumentRedlineAccess().SplitRedline(rPam
);
576 m_pUndoDelete2
->UndoImpl(rContext
);
577 m_pUndoDelete2
.reset();
579 m_pUndoDelete
->UndoImpl(rContext
);
580 m_pUndoDelete
.reset();
582 // note: don't call SetPaM before executing Undo of members
583 SwPaM
& rPam(AddUndoRedoPaM(rContext
));
585 SwRangeRedline
* pTmp
= new SwRangeRedline(*m_pRedlineData
, rPam
);
586 rDoc
.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp
);
587 pTmp
->InvalidateRange(SwRangeRedline::Invalidation::Add
);
593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */