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 <UndoDelete.hxx>
22 #include <libxml/xmlwriter.h>
23 #include <editeng/formatbreakitem.hxx>
25 #include <hintids.hxx>
26 #include <osl/diagnose.h>
27 #include <rtl/ustrbuf.hxx>
28 #include <unotools/charclass.hxx>
30 #include <fmtanchr.hxx>
32 #include <UndoManager.hxx>
33 #include <IDocumentRedlineAccess.hxx>
34 #include <IDocumentStylePoolAccess.hxx>
35 #include <swtable.hxx>
39 #include <UndoCore.hxx>
41 #include <poolfmt.hxx>
44 #include <frmtool.hxx>
46 #include <rootfrm.hxx>
47 #include <strings.hrc>
48 #include <frameformats.hxx>
49 #include <fmtpdsc.hxx>
53 /* lcl_MakeAutoFrames has to call MakeFrames for objects bounded "AtChar"
54 ( == AUTO ), if the anchor frame has be moved via MoveNodes(..) and
57 static void lcl_MakeAutoFrames(const sw::FrameFormats
<sw::SpzFrameFormat
*>& rSpzs
, SwNodeOffset nMovedIndex
)
61 const SwFormatAnchor
* pAnchor
= &pSpz
->GetAnchor();
62 if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
64 const SwNode
* pAnchorNode
= pAnchor
->GetAnchorNode();
65 if( pAnchorNode
&& nMovedIndex
== pAnchorNode
->GetIndex() )
71 static SwTextNode
* FindFirstAndNextNode(SwDoc
& rDoc
, SwUndRng
const& rRange
,
72 SwRedlineSaveDatas
const& rRedlineSaveData
,
73 SwTextNode
*& o_rpFirstMergedDeletedTextNode
)
75 // redlines are corrected now to exclude the deleted node
76 assert(rRange
.m_nEndContent
== 0);
77 SwNodeOffset
nEndOfRedline(0);
78 for (size_t i
= 0; i
< rRedlineSaveData
.size(); ++i
)
80 auto const& rRedline(rRedlineSaveData
[i
]);
81 if (rRedline
.m_nSttNode
<= rRange
.m_nSttNode
82 // coverity[copy_paste_error : FALSE] : m_nEndNode is intentional here
83 && rRedline
.m_nSttNode
< rRange
.m_nEndNode
84 && rRange
.m_nEndNode
<= rRedline
.m_nEndNode
85 && rRedline
.GetType() == RedlineType::Delete
)
87 nEndOfRedline
= rRedline
.m_nEndNode
;
88 o_rpFirstMergedDeletedTextNode
= rDoc
.GetNodes()[rRedline
.m_nSttNode
]->GetTextNode();
89 assert(rRange
.m_nSttNode
== rRange
.m_nEndNode
- 1); // otherwise this needs to iterate more RL to find the first node?
95 assert(o_rpFirstMergedDeletedTextNode
);
96 SwTextNode
* pNextNode(nullptr);
97 for (SwNodeOffset i
= rRange
.m_nEndNode
; /* i <= nEndOfRedline */; ++i
)
99 SwNode
*const pNode(rDoc
.GetNodes()[i
]);
100 assert(!pNode
->IsEndNode()); // cannot be both leaving section here *and* overlapping redline
101 if (pNode
->IsStartNode())
103 i
= pNode
->EndOfSectionIndex(); // will be incremented again
105 else if (pNode
->IsTextNode())
107 pNextNode
= pNode
->GetTextNode();
120 static void DelFullParaMoveFrames(SwDoc
& rDoc
, SwUndRng
const& rRange
,
121 SwRedlineSaveDatas
const& rRedlineSaveData
)
123 SwTextNode
* pFirstMergedDeletedTextNode(nullptr);
124 SwTextNode
*const pNextNode
= FindFirstAndNextNode(rDoc
, rRange
,
125 rRedlineSaveData
, pFirstMergedDeletedTextNode
);
129 std::vector
<SwTextFrame
*> frames
;
130 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pFirstMergedDeletedTextNode
);
131 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
133 if (pFrame
->getRootFrame()->HasMergedParas())
135 assert(pFrame
->GetMergedPara());
136 assert(pFrame
->GetMergedPara()->pFirstNode
== pFirstMergedDeletedTextNode
);
137 assert(pNextNode
->GetIndex() <= pFrame
->GetMergedPara()->pLastNode
->GetIndex());
138 frames
.push_back(pFrame
);
141 for (SwTextFrame
*const pFrame
: frames
)
143 // sw_redlinehide: don't need FrameMode::Existing here
144 // because everything from pNextNode onwards is already
146 pFrame
->RegisterToNode(*pNextNode
, true);
150 // SwUndoDelete has to perform a deletion and to record anything that is needed
151 // to restore the situation before the deletion. Unfortunately a part of the
152 // deletion will be done after calling this Ctor, this has to be kept in mind!
153 // In this Ctor only the complete paragraphs will be deleted, the joining of
154 // the first and last paragraph of the selection will be handled outside this
156 // Here are the main steps of the function:
157 // 1. Deletion/recording of content indices of the selection: footnotes, fly
158 // frames and bookmarks
159 // Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
160 // 2. If the paragraph where the selection ends, is the last content of a
161 // section so that this section becomes empty when the paragraphs will be
162 // joined we have to do some smart actions ;-) The paragraph will be moved
163 // outside the section and replaced by a dummy text node, the complete
164 // section will be deleted in step 3. The difference between replacement
165 // dummy and original is nReplacementDummy.
166 // 3. Moving complete selected nodes into the UndoArray. Before this happens the
167 // selection has to be extended if there are sections which would become
168 // empty otherwise. BTW: sections will be moved into the UndoArray if they
169 // are complete part of the selection. Sections starting or ending outside
170 // of the selection will not be removed from the DocNodeArray even they got
171 // a "dummy"-copy in the UndoArray.
172 // 4. We have to anticipate the joining of the two paragraphs if the start
173 // paragraph is inside a section and the end paragraph not. Then we have to
174 // move the paragraph into this section and to record this in nSectDiff.
175 SwUndoDelete::SwUndoDelete(
177 SwDeleteFlags
const flags
,
179 bool bCalledByTableCpy
)
180 : SwUndo(SwUndoId::DELETE
, &rPam
.GetDoc()),
189 m_bJoinNext( false ),
190 m_bTableDelLastNd( false ),
191 // bFullPara is set e.g. if an empty paragraph before a table is deleted
192 m_bDelFullPara( bFullPara
),
193 m_bResetPgDesc( false ),
194 m_bResetPgBrk( false ),
195 m_bFromTableCopy( bCalledByTableCpy
)
196 , m_DeleteFlags(flags
)
198 assert(!m_bDelFullPara
|| !(m_DeleteFlags
& SwDeleteFlags::ArtificialSelection
));
200 m_bCacheComment
= false;
202 SwDoc
& rDoc
= rPam
.GetDoc();
204 if( !rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
206 m_pRedlSaveData
.reset(new SwRedlineSaveDatas
);
207 if( !FillSaveData( rPam
, *m_pRedlSaveData
))
209 m_pRedlSaveData
.reset();
214 m_pHistory
.reset( new SwHistory
);
216 // delete all footnotes for now
217 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
219 // Step 1. deletion/record of content indices
222 OSL_ENSURE( rPam
.HasMark(), "PaM without Mark" );
223 DelContentIndex( *rPam
.GetMark(), *rPam
.GetPoint(),
224 DelContentType(DelContentType::AllMask
| DelContentType::CheckNoCntnt
) );
226 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
227 DelBookmarks(pStt
->GetNode(), pEnd
->GetNode());
231 DelContentIndex(*rPam
.GetMark(), *rPam
.GetPoint(),
232 DelContentType::AllMask
233 | ((m_DeleteFlags
& SwDeleteFlags::ArtificialSelection
) ? DelContentType::Replace
: DelContentType(0)));
234 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
235 if (m_nEndNode
- m_nSttNode
> SwNodeOffset(1)) // check for fully selected nodes
237 SwNodeIndex
const start(pStt
->GetNode(), +1);
238 DelBookmarks(start
.GetNode(), pEnd
->GetNode());
242 m_nSetPos
= m_pHistory
? m_pHistory
->Count() : 0;
244 // Is already anything deleted?
245 m_nNdDiff
= m_nSttNode
- pStt
->GetNodeIndex();
247 m_bJoinNext
= !bFullPara
&& pEnd
== rPam
.GetPoint();
248 m_bBackSp
= !bFullPara
&& !m_bJoinNext
;
250 SwTextNode
*pSttTextNd
= nullptr, *pEndTextNd
= nullptr;
253 pSttTextNd
= pStt
->GetNode().GetTextNode();
254 pEndTextNd
= m_nSttNode
== m_nEndNode
256 : pEnd
->GetNode().GetTextNode();
258 else if (m_pRedlSaveData
)
260 DelFullParaMoveFrames(rDoc
, *this, *m_pRedlSaveData
);
263 bool bMoveNds
= *pStt
!= *pEnd
// any area still existent?
264 && ( SaveContent( pStt
, pEnd
, pSttTextNd
, pEndTextNd
) || m_bFromTableCopy
);
266 if( pSttTextNd
&& pEndTextNd
&& pSttTextNd
!= pEndTextNd
)
268 // two different TextNodes, thus save also the TextFormatCollection
269 m_pHistory
->Add( pSttTextNd
->GetTextColl(),pStt
->GetNodeIndex(), SwNodeType::Text
);
270 m_pHistory
->Add( pEndTextNd
->GetTextColl(),pEnd
->GetNodeIndex(), SwNodeType::Text
);
272 if( !m_bJoinNext
) // Selection from bottom to top
274 // When using JoinPrev() all AUTO-PageBreak's will be copied
275 // correctly. To restore them with UNDO, Auto-PageBreak of the
276 // EndNode needs to be reset. Same for PageDesc and ColBreak.
277 if( pEndTextNd
->HasSwAttrSet() )
279 SwRegHistory
aRegHist( *pEndTextNd
, m_pHistory
.get() );
280 if( SfxItemState::SET
== pEndTextNd
->GetpSwAttrSet()->GetItemState(
282 pEndTextNd
->ResetAttr( RES_BREAK
);
283 if( pEndTextNd
->HasSwAttrSet() &&
284 SfxItemState::SET
== pEndTextNd
->GetpSwAttrSet()->GetItemState(
285 RES_PAGEDESC
, false ) )
286 pEndTextNd
->ResetAttr( RES_PAGEDESC
);
291 // Move now also the PaM. The SPoint is at the beginning of a SSelection.
292 if( pEnd
== rPam
.GetPoint() && ( !bFullPara
|| pSttTextNd
|| pEndTextNd
) )
295 if( !pSttTextNd
&& !pEndTextNd
)
296 rPam
.GetPoint()->Adjust(SwNodeOffset(-1));
297 rPam
.DeleteMark(); // the SPoint is in the selection
304 if( bMoveNds
) // Do Nodes exist that need to be moved?
306 SwNodes
& rNds
= rDoc
.GetUndoManager().GetUndoNodes();
307 SwNodes
& rDocNds
= rDoc
.GetNodes();
308 SwNodeRange
aRg( rDocNds
, m_nSttNode
- m_nNdDiff
, m_nEndNode
- m_nNdDiff
);
309 if( !bFullPara
&& !pEndTextNd
&&
310 aRg
.aEnd
.GetNode() != rDoc
.GetNodes().GetEndOfContent() )
312 SwNode
* pNode
= aRg
.aEnd
.GetNode().StartOfSectionNode();
313 if( pNode
->GetIndex() >= m_nSttNode
- m_nNdDiff
)
314 ++aRg
.aEnd
; // Deletion of a complete table
317 // Step 2: Expand selection if necessary
318 if( m_bJoinNext
|| bFullPara
)
320 // If all content of a section will be moved into Undo, the section
321 // itself should be moved completely.
322 while( aRg
.aEnd
.GetIndex() + 2 < rDocNds
.Count() &&
323 ( (pTmpNd
= rDocNds
[ aRg
.aEnd
.GetIndex()+1 ])->IsEndNode() &&
324 pTmpNd
->StartOfSectionNode()->IsSectionNode() &&
325 pTmpNd
->StartOfSectionNode()->GetIndex() >= aRg
.aStart
.GetIndex() ) )
327 m_nReplaceDummy
= aRg
.aEnd
.GetIndex() + m_nNdDiff
- m_nEndNode
;
328 if( m_nReplaceDummy
)
329 { // The selection has been expanded, because
333 // The end text node has to leave the (expanded) selection
334 // The dummy is needed because MoveNodes deletes empty
337 SwNodeRange
aMvRg( *pEndTextNd
, SwNodeOffset(0), *pEndTextNd
, SwNodeOffset(1) );
338 SwPosition
aSplitPos( *pEndTextNd
);
339 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
340 rDoc
.getIDocumentContentOperations().SplitNode( aSplitPos
, false );
341 rDocNds
.MoveNodes( aMvRg
, rDocNds
, aRg
.aEnd
.GetNode() );
345 m_nReplaceDummy
= SwNodeOffset(0);
348 if( m_bBackSp
|| bFullPara
)
350 // See above, the selection has to be expanded if there are "nearly
351 // empty" sections and a replacement dummy has to be set if needed.
352 while( SwNodeOffset(1) < aRg
.aStart
.GetIndex() &&
353 ( (pTmpNd
= rDocNds
[ aRg
.aStart
.GetIndex()-1 ])->IsSectionNode() &&
354 pTmpNd
->EndOfSectionIndex() < aRg
.aEnd
.GetIndex() ) )
358 m_nReplaceDummy
= m_nSttNode
- m_nNdDiff
- aRg
.aStart
.GetIndex();
359 if( m_nReplaceDummy
)
361 SwNodeRange
aMvRg( *pSttTextNd
, SwNodeOffset(0), *pSttTextNd
, SwNodeOffset(1) );
362 SwPosition
aSplitPos( *pSttTextNd
);
363 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
364 rDoc
.getIDocumentContentOperations().SplitNode( aSplitPos
, false );
365 rDocNds
.MoveNodes( aMvRg
, rDocNds
, aRg
.aStart
.GetNode() );
371 if( m_bFromTableCopy
)
377 else if( !bFullPara
&& !aRg
.aEnd
.GetNode().IsContentNode() )
381 else if (pSttTextNd
&& (pEndTextNd
|| pSttTextNd
->GetText().getLength()))
384 // Step 3: Moving into UndoArray...
385 m_nNode
= rNds
.GetEndOfContent().GetIndex();
386 rDocNds
.MoveNodes( aRg
, rNds
, rNds
.GetEndOfContent() );
387 m_oMvStt
.emplace( rNds
, m_nNode
);
388 // remember difference!
389 m_nNode
= rNds
.GetEndOfContent().GetIndex() - m_nNode
;
391 if( pSttTextNd
&& pEndTextNd
)
393 //Step 4: Moving around sections
394 m_nSectDiff
= aRg
.aEnd
.GetIndex() - aRg
.aStart
.GetIndex();
395 // nSect is the number of sections which starts(ends) between start
396 // and end node of the selection. The "loser" paragraph has to be
397 // moved into the section(s) of the "winner" paragraph
402 SwNodeRange
aMvRg( *pEndTextNd
, SwNodeOffset(0), *pEndTextNd
, SwNodeOffset(1) );
403 rDocNds
.MoveNodes( aMvRg
, rDocNds
, aRg
.aStart
.GetNode() );
407 SwNodeRange
aMvRg( *pSttTextNd
, SwNodeOffset(0), *pSttTextNd
, SwNodeOffset(1) );
408 rDocNds
.MoveNodes( aMvRg
, rDocNds
, aRg
.aEnd
.GetNode() );
412 if( m_nSectDiff
|| m_nReplaceDummy
)
413 lcl_MakeAutoFrames( *rDoc
.GetSpzFrameFormats(),
414 m_bJoinNext
? pEndTextNd
->GetIndex() : pSttTextNd
->GetIndex() );
417 m_nNode
= SwNodeOffset(0); // moved no node -> no difference at the end
419 // Are there any Nodes that got deleted before that (FootNotes
420 // have ContentNodes)?
421 if( !pSttTextNd
&& !pEndTextNd
)
423 m_nNdDiff
= m_nSttNode
- rPam
.GetPoint()->GetNodeIndex() - (bFullPara
? 0 : 1);
424 rPam
.Move( fnMoveForward
, GoInNode
);
428 m_nNdDiff
= m_nSttNode
;
429 if( m_nSectDiff
&& m_bBackSp
)
430 m_nNdDiff
+= m_nSectDiff
;
431 m_nNdDiff
-= rPam
.GetPoint()->GetNodeIndex();
434 // is a history necessary here at all?
435 if( m_pHistory
&& !m_pHistory
->Count() )
439 bool SwUndoDelete::SaveContent( const SwPosition
* pStt
, const SwPosition
* pEnd
,
440 SwTextNode
* pSttTextNd
, SwTextNode
* pEndTextNd
)
442 SwNodeOffset nNdIdx
= pStt
->GetNodeIndex();
443 // 1 - copy start in Start-String
446 bool bOneNode
= m_nSttNode
== m_nEndNode
;
447 SwRegHistory
aRHst( *pSttTextNd
, m_pHistory
.get() );
448 // always save all text atttibutes because of possibly overlapping
450 m_pHistory
->CopyAttr( pSttTextNd
->GetpSwpHints(), nNdIdx
,
451 0, pSttTextNd
->GetText().getLength(), true );
452 if( !bOneNode
&& pSttTextNd
->HasSwAttrSet() )
453 m_pHistory
->CopyFormatAttr( *pSttTextNd
->GetpSwAttrSet(), nNdIdx
);
455 // the length might have changed (!!Fields!!)
456 sal_Int32 nLen
= (bOneNode
457 ? pEnd
->GetContentIndex()
458 : pSttTextNd
->GetText().getLength())
459 - pStt
->GetContentIndex();
461 // delete now also the text (all attribute changes are added to
463 m_aSttStr
= pSttTextNd
->GetText().copy(m_nSttContent
, nLen
);
464 pSttTextNd
->EraseText( *pStt
, nLen
);
465 if( pSttTextNd
->GetpSwpHints() )
466 pSttTextNd
->GetpSwpHints()->DeRegister();
469 bool emptied( !m_aSttStr
->isEmpty() && !pSttTextNd
->Len() );
470 if (!bOneNode
|| emptied
) // merging may overwrite xmlids...
472 m_pMetadataUndoStart
= emptied
473 ? pSttTextNd
->CreateUndoForDelete()
474 : pSttTextNd
->CreateUndo();
478 return false; // stop moving more nodes
481 // 2 - copy end into End-String
484 SwContentIndex
aEndIdx( pEndTextNd
);
485 nNdIdx
= pEnd
->GetNodeIndex();
486 SwRegHistory
aRHst( *pEndTextNd
, m_pHistory
.get() );
488 // always save all text atttibutes because of possibly overlapping
490 m_pHistory
->CopyAttr( pEndTextNd
->GetpSwpHints(), nNdIdx
, 0,
491 pEndTextNd
->GetText().getLength(), true );
493 if( pEndTextNd
->HasSwAttrSet() )
494 m_pHistory
->CopyFormatAttr( *pEndTextNd
->GetpSwAttrSet(), nNdIdx
);
496 // delete now also the text (all attribute changes are added to
498 m_aEndStr
= pEndTextNd
->GetText().copy( 0, pEnd
->GetContentIndex() );
499 pEndTextNd
->EraseText( aEndIdx
, pEnd
->GetContentIndex() );
500 if( pEndTextNd
->GetpSwpHints() )
501 pEndTextNd
->GetpSwpHints()->DeRegister();
504 bool emptied
= !m_aEndStr
->isEmpty() && !pEndTextNd
->Len();
506 m_pMetadataUndoEnd
= emptied
507 ? pEndTextNd
->CreateUndoForDelete()
508 : pEndTextNd
->CreateUndo();
511 // if there are only two Nodes then we're done
512 if( ( pSttTextNd
|| pEndTextNd
) && m_nSttNode
+ 1 == m_nEndNode
)
513 return false; // do not move any Node
515 return true; // move Nodes lying in between
518 bool SwUndoDelete::CanGrouping( SwDoc
& rDoc
, const SwPaM
& rDelPam
)
520 // Is Undo greater than one Node (that is Start and EndString)?
521 if( !m_aSttStr
|| m_aSttStr
->isEmpty() || m_aEndStr
)
524 // only the deletion of single char's can be condensed
525 if( m_nSttNode
!= m_nEndNode
|| ( !m_bGroup
&& m_nSttContent
+1 != m_nEndContent
))
528 auto [pStt
, pEnd
] = rDelPam
.StartEnd(); // SwPosition*
530 if( pStt
->GetNode() != pEnd
->GetNode() ||
531 pStt
->GetContentIndex()+1 != pEnd
->GetContentIndex() ||
532 pEnd
->GetNodeIndex() != m_nSttNode
)
535 // Distinguish between BackSpace and Delete because the Undo array needs to
536 // be constructed differently!
537 if( pEnd
->GetContentIndex() == m_nSttContent
)
539 if( m_bGroup
&& !m_bBackSp
) return false;
542 else if( pStt
->GetContentIndex() == m_nSttContent
)
544 if( m_bGroup
&& m_bBackSp
) return false;
550 // are both Nodes (Node/Undo array) TextNodes at all?
551 SwTextNode
* pDelTextNd
= pStt
->GetNode().GetTextNode();
552 if( !pDelTextNd
) return false;
554 sal_Int32 nUChrPos
= m_bBackSp
? 0 : m_aSttStr
->getLength()-1;
555 sal_Unicode cDelChar
= pDelTextNd
->GetText()[ pStt
->GetContentIndex() ];
556 CharClass
& rCC
= GetAppCharClass();
557 if( ( CH_TXTATR_BREAKWORD
== cDelChar
|| CH_TXTATR_INWORD
== cDelChar
) ||
558 rCC
.isLetterNumeric( OUString( cDelChar
), 0 ) !=
559 rCC
.isLetterNumeric( *m_aSttStr
, nUChrPos
) )
562 // tdf#132725 - if at-char/at-para flys would be deleted, don't group!
563 // DelContentIndex() would be called at the wrong time here, the indexes
564 // in the stored SwHistoryTextFlyCnt would be wrong when Undo is invoked
565 if (IsFlySelectedByCursor(rDoc
, *pStt
, *pEnd
))
571 SwRedlineSaveDatas aTmpSav
;
572 const bool bSaved
= FillSaveData( rDelPam
, aTmpSav
, false );
574 bool bOk
= ( !m_pRedlSaveData
&& !bSaved
) ||
575 ( m_pRedlSaveData
&& bSaved
&&
576 SwUndo::CanRedlineGroup( *m_pRedlSaveData
, aTmpSav
, m_bBackSp
));
577 // aTmpSav.DeleteAndDestroyAll();
581 rDoc
.getIDocumentRedlineAccess().DeleteRedline( rDelPam
, false, RedlineType::Any
);
584 // Both 'deletes' can be consolidated, so 'move' the related character
586 m_nSttContent
--; // BackSpace: add char to array!
589 m_nEndContent
++; // Delete: attach char at the end
592 m_aSttStr
= m_aSttStr
->replaceAt( nUChrPos
, 0, rtl::OUStringChar(cDelChar
) );
593 pDelTextNd
->EraseText( *pStt
, 1 );
599 SwUndoDelete::~SwUndoDelete()
601 if( m_oMvStt
) // Delete also the selection from UndoNodes array
603 // Insert saves content in IconSection
604 m_oMvStt
->GetNode().GetNodes().Delete( *m_oMvStt
, m_nNode
);
607 m_pRedlSaveData
.reset();
610 static SwRewriter
lcl_RewriterFromHistory(SwHistory
& rHistory
)
612 SwRewriter aRewriter
;
616 for ( sal_uInt16 n
= 0; n
< rHistory
.Count(); n
++)
618 OUString aDescr
= rHistory
[n
]->GetDescription();
620 if (!aDescr
.isEmpty())
622 aRewriter
.AddRule(UndoArg2
, aDescr
);
631 aRewriter
.AddRule(UndoArg2
, SwResId(STR_FIELD
));
637 static bool lcl_IsSpecialCharacter(sal_Unicode nChar
)
641 case CH_TXTATR_BREAKWORD
:
642 case CH_TXTATR_INWORD
:
644 case CH_TXTATR_NEWLINE
:
645 case CH_TXT_ATR_INPUTFIELDSTART
:
646 case CH_TXT_ATR_INPUTFIELDEND
:
647 case CH_TXT_ATR_FORMELEMENT
:
648 case CH_TXT_ATR_FIELDSTART
:
649 case CH_TXT_ATR_FIELDSEP
:
650 case CH_TXT_ATR_FIELDEND
:
660 static OUString
lcl_DenotedPortion(std::u16string_view rStr
, sal_Int32 nStart
, sal_Int32 nEnd
, bool bQuoted
)
664 auto nCount
= nEnd
- nStart
;
667 sal_Unicode cLast
= rStr
[nEnd
- 1];
668 if (lcl_IsSpecialCharacter(cLast
))
673 aResult
= SwResId(STR_UNDO_TABS
, nCount
);
676 case CH_TXTATR_NEWLINE
:
677 aResult
= SwResId(STR_UNDO_NLS
, nCount
);
681 case CH_TXTATR_INWORD
:
682 case CH_TXTATR_BREAKWORD
:
683 aResult
= SwRewriter::GetPlaceHolder(UndoArg2
);
686 case CH_TXT_ATR_INPUTFIELDSTART
:
687 case CH_TXT_ATR_INPUTFIELDEND
:
688 case CH_TXT_ATR_FORMELEMENT
:
689 case CH_TXT_ATR_FIELDSTART
:
690 case CH_TXT_ATR_FIELDSEP
:
691 case CH_TXT_ATR_FIELDEND
:
695 assert(!"unexpected special character");
698 SwRewriter aRewriter
;
699 aRewriter
.AddRule(UndoArg1
, OUString::number(nCount
));
700 aResult
= aRewriter
.Apply(aResult
);
704 aResult
= SwResId(STR_START_QUOTE
) +
705 rStr
.substr(nStart
, nCount
) +
706 SwResId(STR_END_QUOTE
);
709 aResult
= rStr
.substr(nStart
, nCount
);
715 OUString
DenoteSpecialCharacters(std::u16string_view aStr
, bool bQuoted
)
717 OUStringBuffer aResult
;
722 sal_Int32 nStart
= 0;
723 sal_Unicode cLast
= 0;
725 for( size_t i
= 0; i
< aStr
.size(); i
++)
727 if (lcl_IsSpecialCharacter(aStr
[i
]))
729 if (cLast
!= aStr
[i
])
735 if (lcl_IsSpecialCharacter(cLast
))
741 aResult
.append(lcl_DenotedPortion(aStr
, nStart
, i
, bQuoted
));
750 aResult
.append(lcl_DenotedPortion(aStr
, nStart
, aStr
.size(), bQuoted
));
753 aResult
= SwRewriter::GetPlaceHolder(UndoArg2
);
755 return aResult
.makeStringAndClear();
758 SwRewriter
SwUndoDelete::GetRewriter() const
762 if (m_nNode
!= SwNodeOffset(0))
764 if (!m_sTableName
.isEmpty())
767 SwRewriter aRewriter
;
768 aRewriter
.AddRule(UndoArg1
, SwResId(STR_START_QUOTE
));
769 aRewriter
.AddRule(UndoArg2
, m_sTableName
);
770 aRewriter
.AddRule(UndoArg3
, SwResId(STR_END_QUOTE
));
772 OUString sTmp
= aRewriter
.Apply(SwResId(STR_TABLE_NAME
));
773 aResult
.AddRule(UndoArg1
, sTmp
);
776 aResult
.AddRule(UndoArg1
, SwResId(STR_PARAGRAPHS
));
782 if (m_aSttStr
&& m_aEndStr
&& m_aSttStr
->isEmpty() &&
783 m_aEndStr
->isEmpty())
785 aStr
= SwResId(STR_PARAGRAPH_UNDO
);
789 std::optional
<OUString
> aTmpStr
;
797 aStr
= DenoteSpecialCharacters(*aTmpStr
);
801 aStr
= SwRewriter::GetPlaceHolder(UndoArg2
);
805 aStr
= ShortenString(aStr
, nUndoStringLength
, SwResId(STR_LDOTS
));
808 SwRewriter aRewriter
= lcl_RewriterFromHistory(*m_pHistory
);
809 aStr
= aRewriter
.Apply(aStr
);
812 aResult
.AddRule(UndoArg1
, aStr
);
818 // Every object, anchored "AtContent" will be reanchored at rPos
819 static void lcl_ReAnchorAtContentFlyFrames(const sw::FrameFormats
<sw::SpzFrameFormat
*>& rSpzs
, const SwPosition
&rPos
, SwNodeOffset nOldIdx
)
821 const SwFormatAnchor
* pAnchor
;
822 for(auto pSpz
: rSpzs
)
824 pAnchor
= &pSpz
->GetAnchor();
825 if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_PARA
)
827 SwNode
* pAnchorNode
= pAnchor
->GetAnchorNode();
828 if( pAnchorNode
&& nOldIdx
== pAnchorNode
->GetIndex() )
830 SwFormatAnchor
aAnch( *pAnchor
);
831 aAnch
.SetAnchor( &rPos
);
832 pSpz
->SetFormatAttr( aAnch
);
838 void SwUndoDelete::UndoImpl(::sw::UndoRedoContext
& rContext
)
840 SwDoc
& rDoc
= rContext
.GetDoc();
842 SwNodeOffset nCalcStt
= m_nSttNode
- m_nNdDiff
;
844 if( m_nSectDiff
&& m_bBackSp
)
845 nCalcStt
+= m_nSectDiff
;
847 SwNodeIndex
aIdx(rDoc
.GetNodes(), nCalcStt
);
848 SwNode
* pInsNd
= &aIdx
.GetNode();
849 SwNode
* pMovedNode
= nullptr;
851 { // code block so that SwPosition is detached when deleting a Node
852 SwPosition
aPos( aIdx
);
853 if( !m_bDelFullPara
)
855 assert(!m_bTableDelLastNd
|| pInsNd
->IsTextNode());
856 if( pInsNd
->IsTableNode() )
858 pInsNd
= rDoc
.GetNodes().MakeTextNode( aIdx
.GetNode(),
859 rDoc
.GetDfltTextFormatColl() );
861 aPos
.Assign( *pInsNd
->GetContentNode(), m_nSttContent
);
865 if( pInsNd
->IsContentNode() )
866 aPos
.SetContent( m_nSttContent
);
867 if( !m_bTableDelLastNd
)
868 pInsNd
= nullptr; // do not delete Node!
872 pInsNd
= nullptr; // do not delete Node!
874 bool bNodeMove
= SwNodeOffset(0) != m_nNode
;
878 // discard attributes since they all saved!
879 SwTextNode
* pTextNd
;
880 if (!m_bDelFullPara
&& aPos
.GetNode().IsSectionNode())
881 { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below
882 assert(m_nSttContent
== 0);
884 pTextNd
= rDoc
.GetNodes()[aPos
.GetNodeIndex() + 1]->GetTextNode();
888 pTextNd
= aPos
.GetNode().GetTextNode();
891 if( pTextNd
&& pTextNd
->HasSwAttrSet() )
892 pTextNd
->ResetAllAttr();
894 if( pTextNd
&& pTextNd
->GetpSwpHints() )
895 pTextNd
->ClearSwpHintsArr( true );
897 if( m_aSttStr
&& !m_bFromTableCopy
)
899 SwNodeOffset nOldIdx
= aPos
.GetNodeIndex();
900 rDoc
.getIDocumentContentOperations().SplitNode( aPos
, false );
901 // After the split all objects are anchored at the first
902 // paragraph, but the pHistory of the fly frame formats relies
903 // on anchoring at the start of the selection
904 // => selection backwards needs a correction.
906 lcl_ReAnchorAtContentFlyFrames(*rDoc
.GetSpzFrameFormats(), aPos
, nOldIdx
);
907 pTextNd
= aPos
.GetNode().GetTextNode();
909 assert(pTextNd
); // else where does m_aEndStr come from?
912 SwContentIndex
aTmpIdx(pTextNd
, aPos
.GetContentIndex());
913 OUString
const ins( pTextNd
->InsertText(*m_aEndStr
, aTmpIdx
,
914 SwInsertFlags::NOHINTEXPAND
) );
915 assert(ins
.getLength() == m_aEndStr
->getLength()); // must succeed
918 pTextNd
->RestoreMetadata(m_pMetadataUndoEnd
);
921 else if (m_aSttStr
&& bNodeMove
&& pInsNd
== nullptr)
923 SwTextNode
* pNd
= aPos
.GetNode().GetTextNode();
926 if (m_nSttContent
< pNd
->GetText().getLength())
928 SwNodeOffset nOldIdx
= aPos
.GetNodeIndex();
929 rDoc
.getIDocumentContentOperations().SplitNode( aPos
, false );
931 lcl_ReAnchorAtContentFlyFrames(*rDoc
.GetSpzFrameFormats(), aPos
, nOldIdx
);
934 aPos
.Adjust(SwNodeOffset(+1));
939 SwNodeOffset nMoveIndex
= aPos
.GetNodeIndex();
940 SwNodeOffset
nDiff(0);
943 nMoveIndex
+= m_nSectDiff
+ 1;
944 pMovedNode
= &aPos
.GetNode();
948 nMoveIndex
-= m_nSectDiff
+ 1;
951 SwNodeIndex
aMvIdx(rDoc
.GetNodes(), nMoveIndex
);
952 SwNodeRange
aRg( aPos
.GetNode(), SwNodeOffset(0) - nDiff
, aPos
.GetNode(), SwNodeOffset(1) - nDiff
);
953 aPos
.Adjust(SwNodeOffset(-1));
955 pMovedNode
= &aPos
.GetNode();
956 rDoc
.GetNodes().MoveNodes(aRg
, rDoc
.GetNodes(), aMvIdx
.GetNode());
957 aPos
.Adjust(SwNodeOffset(+1));
962 SwNodeRange
aRange( *m_oMvStt
, SwNodeOffset(0), *m_oMvStt
, m_nNode
);
963 SwNodeIndex
aCopyIndex( aPos
.GetNode(), -1 );
964 rDoc
.GetUndoManager().GetUndoNodes().Copy_(aRange
, aPos
.GetNode(),
965 // sw_redlinehide: delay creating frames: the flags on the
966 // nodes aren't necessarily up-to-date, and the redlines
967 // from m_pRedlSaveData aren't applied yet...
970 if( m_nReplaceDummy
)
972 SwNodeOffset nMoveIndex
;
975 nMoveIndex
= m_nEndNode
- m_nNdDiff
;
976 aPos
.Assign( nMoveIndex
+ m_nReplaceDummy
);
980 aPos
.Assign( aCopyIndex
);
981 nMoveIndex
= aPos
.GetNodeIndex() + m_nReplaceDummy
+ 1;
983 SwNodeIndex
aMvIdx(rDoc
.GetNodes(), nMoveIndex
);
984 SwNodeRange
aRg( aPos
.GetNode(), SwNodeOffset(0), aPos
.GetNode(), SwNodeOffset(1) );
985 pMovedNode
= &aPos
.GetNode();
986 // tdf#131684 without deleting frames
987 rDoc
.GetNodes().MoveNodes(aRg
, rDoc
.GetNodes(), aMvIdx
.GetNode(), false);
988 rDoc
.GetNodes().Delete( aMvIdx
);
994 aPos
.Assign( m_nSttNode
- m_nNdDiff
+ ( m_bJoinNext
? SwNodeOffset(0) : m_nReplaceDummy
) );
995 SwTextNode
* pTextNd
= aPos
.GetNode().GetTextNode();
996 // If more than a single Node got deleted, also all "Node"
997 // attributes were saved
998 if (pTextNd
!= nullptr)
1000 if( pTextNd
->HasSwAttrSet() && bNodeMove
&& !m_aEndStr
)
1001 pTextNd
->ResetAllAttr();
1003 if( pTextNd
->GetpSwpHints() )
1004 pTextNd
->ClearSwpHintsArr( true );
1006 // SectionNode mode and selection from top to bottom:
1007 // -> in StartNode is still the rest of the Join => delete
1008 aPos
.SetContent( m_nSttContent
);
1009 pTextNd
->SetInSwUndo(true);
1010 OUString
const ins( pTextNd
->InsertText(*m_aSttStr
, aPos
,
1011 SwInsertFlags::NOHINTEXPAND
) );
1012 pTextNd
->SetInSwUndo(false);
1013 assert(ins
.getLength() == m_aSttStr
->getLength()); // must succeed
1015 // METADATA: restore
1016 pTextNd
->RestoreMetadata(m_pMetadataUndoStart
);
1022 m_pHistory
->TmpRollback(&rDoc
, m_nSetPos
, false);
1023 if( m_nSetPos
) // there were Footnodes/FlyFrames
1025 // are there others than these ones?
1026 if( m_nSetPos
< m_pHistory
->Count() )
1028 // if so save the attributes of the others
1030 aHstr
.Move( 0, m_pHistory
.get(), m_nSetPos
);
1031 m_pHistory
->Rollback(&rDoc
);
1032 m_pHistory
->Move( 0, &aHstr
);
1036 m_pHistory
->Rollback(&rDoc
);
1042 if( m_bResetPgDesc
|| m_bResetPgBrk
)
1044 sal_uInt16 nStt
= m_bResetPgDesc
? sal_uInt16(RES_PAGEDESC
) : sal_uInt16(RES_BREAK
);
1045 sal_uInt16 nEnd
= m_bResetPgBrk
? sal_uInt16(RES_BREAK
) : sal_uInt16(RES_PAGEDESC
);
1047 SwNode
* pNode
= rDoc
.GetNodes()[ m_nEndNode
+ 1 ];
1048 if( pNode
->IsContentNode() )
1049 static_cast<SwContentNode
*>(pNode
)->ResetAttr( nStt
, nEnd
);
1050 else if( pNode
->IsTableNode() )
1051 static_cast<SwTableNode
*>(pNode
)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt
, nEnd
);
1054 // delete the temporarily added Node
1055 if (pInsNd
&& !m_bTableDelLastNd
)
1057 assert(&aIdx
.GetNode() == pInsNd
);
1058 rDoc
.GetNodes().Delete( aIdx
);
1060 if( m_pRedlSaveData
)
1061 SetSaveData(rDoc
, *m_pRedlSaveData
);
1063 SwNodeOffset
delFullParaEndNode(m_nEndNode
);
1064 if (m_bDelFullPara
&& m_pRedlSaveData
)
1066 SwTextNode
* pFirstMergedDeletedTextNode(nullptr);
1067 SwTextNode
*const pNextNode
= FindFirstAndNextNode(rDoc
, *this,
1068 *m_pRedlSaveData
, pFirstMergedDeletedTextNode
);
1071 bool bNonMerged(false);
1072 std::vector
<SwTextFrame
*> frames
;
1073 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pNextNode
);
1074 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
1076 if (pFrame
->getRootFrame()->HasMergedParas())
1078 frames
.push_back(pFrame
);
1085 for (SwTextFrame
*const pFrame
: frames
)
1087 // could either destroy the text frames, or move them...
1088 // destroying them would have the advantage that we don't
1089 // need special code to *exclude* pFirstMergedDeletedTextNode
1090 // from MakeFrames for the layouts in Hide mode but not
1091 // layouts in Show mode ...
1092 // ... except that MakeFrames won't create them then :(
1093 pFrame
->RegisterToNode(*pFirstMergedDeletedTextNode
);
1094 assert(pFrame
->GetMergedPara());
1095 assert(!bNonMerged
); // delFullParaEndNode is such an awful hack
1097 delFullParaEndNode
= pFirstMergedDeletedTextNode
->GetIndex();
1101 else if (m_aSttStr
&& (!m_bFromTableCopy
|| SwNodeOffset(0) != m_nNode
))
1103 // only now do we have redlines in the document again; fix up the split
1105 SwTextNode
*const pStartNode(aIdx
.GetNodes()[m_nSttNode
]->GetTextNode());
1107 sw::RecreateStartTextFrames(*pStartNode
);
1110 // create frames after SetSaveData has recreated redlines
1111 if (SwNodeOffset(0) != m_nNode
)
1113 // tdf#136453 only if section nodes at the start
1114 if (m_bBackSp
&& m_nReplaceDummy
!= SwNodeOffset(0))
1116 // tdf#134252 *first* create outer section frames
1117 // note: text node m_nSttNode currently has frame with an upper;
1118 // there's a hack in InsertCnt_() to move it below new section frame
1119 SwNode
& start(*rDoc
.GetNodes()[m_nSttNode
- m_nReplaceDummy
]);
1120 SwNode
& end(*rDoc
.GetNodes()[m_nSttNode
]); // exclude m_nSttNode
1121 ::MakeFrames(&rDoc
, start
, end
);
1123 // tdf#121031 if the start node is a text node, it already has a frame;
1124 // if it's a table, it does not
1125 // tdf#109376 exception: end on non-text-node -> start node was inserted
1126 assert(!m_bDelFullPara
|| (m_nSectDiff
== SwNodeOffset(0)));
1127 SwNode
& start(*rDoc
.GetNodes()[m_nSttNode
+
1128 ((m_bDelFullPara
|| !rDoc
.GetNodes()[m_nSttNode
]->IsTextNode() || pInsNd
)
1130 // don't include end node in the range: it may have been merged already
1131 // by the start node, or it may be merged by one of the moved nodes,
1132 // but if it isn't merged, its current frame(s) should be good...
1133 SwNode
& end(*rDoc
.GetNodes()[ m_bDelFullPara
1134 ? delFullParaEndNode
1135 // tdf#147310 SwDoc::DeleteRowCol() may delete whole table - end must be node following table!
1136 : (m_nEndNode
+ (rDoc
.GetNodes()[m_nSttNode
]->IsTableNode() && rDoc
.GetNodes()[m_nEndNode
]->IsEndNode() ? 1 : 0))]);
1137 ::MakeFrames(&rDoc
, start
, end
);
1141 { // probably better do this after creating all frames
1142 lcl_MakeAutoFrames(*rDoc
.GetSpzFrameFormats(), pMovedNode
->GetIndex());
1145 // tdf#134021 only after MakeFrames(), because it may be the only node
1146 // that has layout frames
1147 if (pInsNd
&& m_bTableDelLastNd
)
1149 assert(&aIdx
.GetNode() == pInsNd
);
1150 SwPaM
tmp(aIdx
, aIdx
);
1151 rDoc
.getIDocumentContentOperations().DelFullPara(tmp
);
1154 AddUndoRedoPaM(rContext
, true);
1157 void SwUndoDelete::RedoImpl(::sw::UndoRedoContext
& rContext
)
1159 SwPaM
& rPam
= AddUndoRedoPaM(rContext
);
1160 SwDoc
& rDoc
= rPam
.GetDoc();
1162 if( m_pRedlSaveData
)
1164 const bool bSuccess
= FillSaveData(rPam
, *m_pRedlSaveData
);
1165 OSL_ENSURE(bSuccess
,
1166 "SwUndoDelete::Redo: used to have redline data, but now none?");
1169 m_pRedlSaveData
.reset();
1173 if( !m_bDelFullPara
)
1175 // tdf#128739 correct cursors but do not delete bookmarks yet
1176 ::PaMCorrAbs(rPam
, *rPam
.End());
1179 if( !m_bJoinNext
) // then restore selection from bottom to top
1183 if( m_pHistory
) // are the attributes saved?
1185 m_pHistory
->SetTmpEnd( m_pHistory
->Count() );
1187 aHstr
.Move( 0, m_pHistory
.get() );
1189 if( m_bDelFullPara
)
1191 OSL_ENSURE( rPam
.HasMark(), "PaM without Mark" );
1192 DelContentIndex( *rPam
.GetMark(), *rPam
.GetPoint(),
1193 DelContentType(DelContentType::AllMask
| DelContentType::CheckNoCntnt
) );
1195 DelBookmarks(rPam
.GetMark()->GetNode(), rPam
.GetPoint()->GetNode());
1199 DelContentIndex(*rPam
.GetMark(), *rPam
.GetPoint(),
1200 DelContentType::AllMask
1201 | ((m_DeleteFlags
& SwDeleteFlags::ArtificialSelection
) ? DelContentType::Replace
: DelContentType(0)));
1203 m_nSetPos
= m_pHistory
? m_pHistory
->Count() : 0;
1205 m_pHistory
->Move( m_nSetPos
, &aHstr
);
1209 if( m_bDelFullPara
)
1211 OSL_ENSURE( rPam
.HasMark(), "PaM without Mark" );
1212 DelContentIndex( *rPam
.GetMark(), *rPam
.GetPoint(),
1213 DelContentType(DelContentType::AllMask
| DelContentType::CheckNoCntnt
) );
1215 DelBookmarks( rPam
.GetMark()->GetNode(), rPam
.GetPoint()->GetNode() );
1219 DelContentIndex(*rPam
.GetMark(), *rPam
.GetPoint(),
1220 DelContentType::AllMask
1221 | ((m_DeleteFlags
& SwDeleteFlags::ArtificialSelection
) ? DelContentType::Replace
: DelContentType(0)));
1223 m_nSetPos
= m_pHistory
? m_pHistory
->Count() : 0;
1226 if( !m_aSttStr
&& !m_aEndStr
)
1228 if (m_bDelFullPara
&& m_pRedlSaveData
)
1230 DelFullParaMoveFrames(rDoc
, *this, *m_pRedlSaveData
);
1233 SwNode
& rSttNd
= ( m_bDelFullPara
|| m_bJoinNext
)
1234 ? rPam
.GetMark()->GetNode()
1235 : rPam
.GetPoint()->GetNode();
1236 SwTableNode
* pTableNd
= rSttNd
.GetTableNode();
1239 if( m_bTableDelLastNd
)
1241 // than add again a Node at the end
1242 const SwNodeIndex
aTmpIdx( *pTableNd
->EndOfSectionNode(), 1 );
1243 rDoc
.GetNodes().MakeTextNode( aTmpIdx
.GetNode(),
1244 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
) );
1247 SwContentNode
* pNextNd
= rDoc
.GetNodes()[
1248 pTableNd
->EndOfSectionIndex()+1 ]->GetContentNode();
1251 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
1253 if( const SwFormatPageDesc
* pItem
= pTableFormat
->GetItemIfSet( RES_PAGEDESC
,
1255 pNextNd
->SetAttr( *pItem
);
1257 if( const SvxFormatBreakItem
* pItem
= pTableFormat
->GetItemIfSet( RES_BREAK
,
1259 pNextNd
->SetAttr( *pItem
);
1261 pTableNd
->DelFrames();
1263 else if (*rPam
.GetMark() == *rPam
.GetPoint())
1264 { // paragraph with only footnote or as-char fly, delete that
1265 // => DelContentIndex has already deleted it! nothing to do here
1266 assert(m_nEndNode
== m_nSttNode
);
1270 // avoid asserts from ~SwContentIndexReg for deleted nodes
1271 SwPaM
aTmp(*rPam
.End());
1272 if (!aTmp
.Move(fnMoveForward
, GoInNode
))
1274 *aTmp
.GetPoint() = *rPam
.Start();
1275 aTmp
.Move(fnMoveBackward
, GoInNode
);
1277 // coverity[copy_paste_error : FALSE] : GetNode() is intentional on both branches
1278 assert(aTmp
.GetPoint()->GetNode() != rPam
.GetPoint()->GetNode()
1279 && aTmp
.GetPoint()->GetNode() != rPam
.GetMark()->GetNode());
1280 ::PaMCorrAbs(rPam
, *aTmp
.GetPoint());
1284 rDoc
.GetNodes().Delete( rSttNd
, m_nEndNode
- m_nSttNode
);
1286 else if( m_bDelFullPara
)
1288 assert(!"dead code");
1289 // The Pam was incremented by one at Point (== end) to provide space
1290 // for UNDO. This now needs to be reverted!
1291 rPam
.End()->Adjust(SwNodeOffset(-1));
1292 if( rPam
.GetPoint()->GetNode() == rPam
.GetMark()->GetNode() )
1293 *rPam
.GetMark() = *rPam
.GetPoint();
1294 rDoc
.getIDocumentContentOperations().DelFullPara( rPam
);
1297 // FIXME: this ends up calling DeleteBookmarks() on the entire rPam which deletes too many!
1298 rDoc
.getIDocumentContentOperations().DeleteAndJoin(rPam
, m_DeleteFlags
);
1301 void SwUndoDelete::RepeatImpl(::sw::RepeatContext
& rContext
)
1303 // this action does not seem idempotent,
1304 // so make sure it is only executed once on repeat
1305 if (rContext
.m_bDeleteRepeated
)
1308 SwPaM
& rPam
= rContext
.GetRepeatPaM();
1309 SwDoc
& rDoc
= rPam
.GetDoc();
1310 ::sw::GroupUndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
1311 if( !rPam
.HasMark() )
1314 rPam
.Move( fnMoveForward
, GoInContent
);
1316 if( m_bDelFullPara
)
1317 rDoc
.getIDocumentContentOperations().DelFullPara( rPam
);
1319 rDoc
.getIDocumentContentOperations().DeleteAndJoin( rPam
);
1320 rContext
.m_bDeleteRepeated
= true;
1323 void SwUndoDelete::SetTableName(const OUString
& rName
)
1325 m_sTableName
= rName
;
1328 void SwUndoDelete::dumpAsXml(xmlTextWriterPtr pWriter
) const
1330 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwUndoDelete"));
1331 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
1332 SwUndo::dumpAsXml(pWriter
);
1333 SwUndoSaveContent::dumpAsXml(pWriter
);
1334 (void)xmlTextWriterEndElement(pWriter
);
1337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */