LibreOffice Module sw (master) 1
undel.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <UndoDelete.hxx>
21
22#include <libxml/xmlwriter.h>
24
25#include <hintids.hxx>
26#include <osl/diagnose.h>
27#include <rtl/ustrbuf.hxx>
29#include <frmfmt.hxx>
30#include <fmtanchr.hxx>
31#include <doc.hxx>
32#include <UndoManager.hxx>
35#include <swtable.hxx>
36#include <swundo.hxx>
37#include <pam.hxx>
38#include <ndtxt.hxx>
39#include <UndoCore.hxx>
40#include <rolbck.hxx>
41#include <poolfmt.hxx>
42#include <mvsave.hxx>
43#include <docary.hxx>
44#include <frmtool.hxx>
45#include <txtfrm.hxx>
46#include <rootfrm.hxx>
47#include <strings.hrc>
48#include <frameformats.hxx>
49#include <fmtpdsc.hxx>
50#include <vector>
51
52// DELETE
53/* lcl_MakeAutoFrames has to call MakeFrames for objects bounded "AtChar"
54 ( == AUTO ), if the anchor frame has be moved via MoveNodes(..) and
55 DelFrames(..)
56*/
57static void lcl_MakeAutoFrames( const SwFrameFormats& rSpzArr, SwNodeOffset nMovedIndex )
58{
59 for( size_t n = 0; n < rSpzArr.size(); ++n )
60 {
61 SwFrameFormat * pFormat = rSpzArr[n];
62 const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
63 if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
64 {
65 const SwPosition* pAPos = pAnchor->GetContentAnchor();
66 if( pAPos && nMovedIndex == pAPos->GetNodeIndex() )
67 pFormat->MakeFrames();
68 }
69 }
70}
71
72static SwTextNode * FindFirstAndNextNode(SwDoc & rDoc, SwUndRng const& rRange,
73 SwRedlineSaveDatas const& rRedlineSaveData,
74 SwTextNode *& o_rpFirstMergedDeletedTextNode)
75{
76 // redlines are corrected now to exclude the deleted node
77 assert(rRange.m_nEndContent == 0);
78 SwNodeOffset nEndOfRedline(0);
79 for (size_t i = 0; i < rRedlineSaveData.size(); ++i)
80 {
81 auto const& rRedline(rRedlineSaveData[i]);
82 if (rRedline.m_nSttNode <= rRange.m_nSttNode
83 // coverity[copy_paste_error : FALSE] : m_nEndNode is intentional here
84 && rRedline.m_nSttNode < rRange.m_nEndNode
85 && rRange.m_nEndNode <= rRedline.m_nEndNode
86 && rRedline.GetType() == RedlineType::Delete)
87 {
88 nEndOfRedline = rRedline.m_nEndNode;
89 o_rpFirstMergedDeletedTextNode = rDoc.GetNodes()[rRedline.m_nSttNode]->GetTextNode();
90 assert(rRange.m_nSttNode == rRange.m_nEndNode - 1); // otherwise this needs to iterate more RL to find the first node?
91 break;
92 }
93 }
94 if (nEndOfRedline)
95 {
96 assert(o_rpFirstMergedDeletedTextNode);
97 SwTextNode * pNextNode(nullptr);
98 for (SwNodeOffset i = rRange.m_nEndNode; /* i <= nEndOfRedline */; ++i)
99 {
100 SwNode *const pNode(rDoc.GetNodes()[i]);
101 assert(!pNode->IsEndNode()); // cannot be both leaving section here *and* overlapping redline
102 if (pNode->IsStartNode())
103 {
104 i = pNode->EndOfSectionIndex(); // will be incremented again
105 }
106 else if (pNode->IsTextNode())
107 {
108 pNextNode = pNode->GetTextNode();
109 break;
110 }
111 }
112 assert(pNextNode);
113 return pNextNode;
114 }
115 else
116 {
117 return nullptr;
118 }
119}
120
121static void DelFullParaMoveFrames(SwDoc & rDoc, SwUndRng const& rRange,
122 SwRedlineSaveDatas const& rRedlineSaveData)
123{
124 SwTextNode * pFirstMergedDeletedTextNode(nullptr);
125 SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, rRange,
126 rRedlineSaveData, pFirstMergedDeletedTextNode);
127 if (!pNextNode)
128 return;
129
130 std::vector<SwTextFrame*> frames;
132 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
133 {
134 if (pFrame->getRootFrame()->HasMergedParas())
135 {
136 assert(pFrame->GetMergedPara());
137 assert(pFrame->GetMergedPara()->pFirstNode == pFirstMergedDeletedTextNode);
138 assert(pNextNode->GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
139 frames.push_back(pFrame);
140 }
141 }
142 for (SwTextFrame *const pFrame : frames)
143 {
144 // sw_redlinehide: don't need FrameMode::Existing here
145 // because everything from pNextNode onwards is already
146 // correctly hidden
147 pFrame->RegisterToNode(*pNextNode, true);
148 }
149}
150
151// SwUndoDelete has to perform a deletion and to record anything that is needed
152// to restore the situation before the deletion. Unfortunately a part of the
153// deletion will be done after calling this Ctor, this has to be kept in mind!
154// In this Ctor only the complete paragraphs will be deleted, the joining of
155// the first and last paragraph of the selection will be handled outside this
156// function.
157// Here are the main steps of the function:
158// 1. Deletion/recording of content indices of the selection: footnotes, fly
159// frames and bookmarks
160// Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
161// 2. If the paragraph where the selection ends, is the last content of a
162// section so that this section becomes empty when the paragraphs will be
163// joined we have to do some smart actions ;-) The paragraph will be moved
164// outside the section and replaced by a dummy text node, the complete
165// section will be deleted in step 3. The difference between replacement
166// dummy and original is nReplacementDummy.
167// 3. Moving complete selected nodes into the UndoArray. Before this happens the
168// selection has to be extended if there are sections which would become
169// empty otherwise. BTW: sections will be moved into the UndoArray if they
170// are complete part of the selection. Sections starting or ending outside
171// of the selection will not be removed from the DocNodeArray even they got
172// a "dummy"-copy in the UndoArray.
173// 4. We have to anticipate the joining of the two paragraphs if the start
174// paragraph is inside a section and the end paragraph not. Then we have to
175// move the paragraph into this section and to record this in nSectDiff.
177 SwPaM& rPam,
178 SwDeleteFlags const flags,
179 bool bFullPara,
180 bool bCalledByTableCpy )
181 : SwUndo(SwUndoId::DELETE, &rPam.GetDoc()),
182 SwUndRng( rPam ),
183 m_nNode(0),
184 m_nNdDiff(0),
185 m_nSectDiff(0),
186 m_nReplaceDummy(0),
187 m_nSetPos(0),
188 m_bGroup( false ),
189 m_bBackSp( false ),
190 m_bJoinNext( false ),
191 m_bTableDelLastNd( false ),
192 // bFullPara is set e.g. if an empty paragraph before a table is deleted
193 m_bDelFullPara( bFullPara ),
194 m_bResetPgDesc( false ),
195 m_bResetPgBrk( false ),
196 m_bFromTableCopy( bCalledByTableCpy )
197 , m_DeleteFlags(flags)
198{
200
201 m_bCacheComment = false;
202
203 SwDoc& rDoc = rPam.GetDoc();
204
206 {
208 if( !FillSaveData( rPam, *m_pRedlSaveData ))
209 {
210 m_pRedlSaveData.reset();
211 }
212 }
213
214 if( !m_pHistory )
215 m_pHistory.reset( new SwHistory );
216
217 // delete all footnotes for now
218 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
219
220 // Step 1. deletion/record of content indices
221 if( m_bDelFullPara )
222 {
223 OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
224 DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
226
227 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
228 DelBookmarks(pStt->GetNode(), pEnd->GetNode());
229 }
230 else
231 {
232 DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
235 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
236 if (m_nEndNode - m_nSttNode > SwNodeOffset(1)) // check for fully selected nodes
237 {
238 SwNodeIndex const start(pStt->GetNode(), +1);
239 DelBookmarks(start.GetNode(), pEnd->GetNode());
240 }
241 }
242
243 m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
244
245 // Is already anything deleted?
246 m_nNdDiff = m_nSttNode - pStt->GetNodeIndex();
247
248 m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
249 m_bBackSp = !bFullPara && !m_bJoinNext;
250
251 SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr;
252 if( !bFullPara )
253 {
254 pSttTextNd = pStt->GetNode().GetTextNode();
255 pEndTextNd = m_nSttNode == m_nEndNode
256 ? pSttTextNd
257 : pEnd->GetNode().GetTextNode();
258 }
259 else if (m_pRedlSaveData)
260 {
262 }
263
264 bool bMoveNds = *pStt != *pEnd // any area still existent?
265 && ( SaveContent( pStt, pEnd, pSttTextNd, pEndTextNd ) || m_bFromTableCopy );
266
267 if( pSttTextNd && pEndTextNd && pSttTextNd != pEndTextNd )
268 {
269 // two different TextNodes, thus save also the TextFormatCollection
270 m_pHistory->Add( pSttTextNd->GetTextColl(),pStt->GetNodeIndex(), SwNodeType::Text );
271 m_pHistory->Add( pEndTextNd->GetTextColl(),pEnd->GetNodeIndex(), SwNodeType::Text );
272
273 if( !m_bJoinNext ) // Selection from bottom to top
274 {
275 // When using JoinPrev() all AUTO-PageBreak's will be copied
276 // correctly. To restore them with UNDO, Auto-PageBreak of the
277 // EndNode needs to be reset. Same for PageDesc and ColBreak.
278 if( pEndTextNd->HasSwAttrSet() )
279 {
280 SwRegHistory aRegHist( *pEndTextNd, m_pHistory.get() );
281 if( SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
282 RES_BREAK, false ) )
283 pEndTextNd->ResetAttr( RES_BREAK );
284 if( pEndTextNd->HasSwAttrSet() &&
285 SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
286 RES_PAGEDESC, false ) )
287 pEndTextNd->ResetAttr( RES_PAGEDESC );
288 }
289 }
290 }
291
292 // Move now also the PaM. The SPoint is at the beginning of a SSelection.
293 if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTextNd || pEndTextNd ) )
294 rPam.Exchange();
295
296 if( !pSttTextNd && !pEndTextNd )
297 --rPam.GetPoint()->nNode;
298 rPam.DeleteMark(); // the SPoint is in the selection
299
300 if( !pEndTextNd )
301 m_nEndContent = 0;
302 if( !pSttTextNd )
303 m_nSttContent = 0;
304
305 if( bMoveNds ) // Do Nodes exist that need to be moved?
306 {
307 SwNodes& rNds = rDoc.GetUndoManager().GetUndoNodes();
308 SwNodes& rDocNds = rDoc.GetNodes();
310 if( !bFullPara && !pEndTextNd &&
311 aRg.aEnd.GetNode() != rDoc.GetNodes().GetEndOfContent() )
312 {
313 SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode();
314 if( pNode->GetIndex() >= m_nSttNode - m_nNdDiff )
315 ++aRg.aEnd; // Deletion of a complete table
316 }
317 SwNode* pTmpNd;
318 // Step 2: Expand selection if necessary
319 if( m_bJoinNext || bFullPara )
320 {
321 // If all content of a section will be moved into Undo, the section
322 // itself should be moved completely.
323 while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() &&
324 ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() &&
325 pTmpNd->StartOfSectionNode()->IsSectionNode() &&
326 pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) )
327 ++aRg.aEnd;
329 if( m_nReplaceDummy )
330 { // The selection has been expanded, because
331 ++aRg.aEnd;
332 if( pEndTextNd )
333 {
334 // The end text node has to leave the (expanded) selection
335 // The dummy is needed because MoveNodes deletes empty
336 // sections
338 SwNodeRange aMvRg( *pEndTextNd, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
339 SwPosition aSplitPos( *pEndTextNd );
340 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
341 rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
342 rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd.GetNode() );
343 --aRg.aEnd;
344 }
345 else
347 }
348 }
349 if( m_bBackSp || bFullPara )
350 {
351 // See above, the selection has to be expanded if there are "nearly
352 // empty" sections and a replacement dummy has to be set if needed.
353 while( SwNodeOffset(1) < aRg.aStart.GetIndex() &&
354 ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() &&
355 pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) )
356 --aRg.aStart;
357 if( pSttTextNd )
358 {
360 if( m_nReplaceDummy )
361 {
362 SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
363 SwPosition aSplitPos( *pSttTextNd );
364 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
365 rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
366 rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart.GetNode() );
367 --aRg.aStart;
368 }
369 }
370 }
371
372 if( m_bFromTableCopy )
373 {
374 if( !pEndTextNd )
375 {
376 if( pSttTextNd )
377 ++aRg.aStart;
378 else if( !bFullPara && !aRg.aEnd.GetNode().IsContentNode() )
379 --aRg.aEnd;
380 }
381 }
382 else if (pSttTextNd && (pEndTextNd || pSttTextNd->GetText().getLength()))
383 ++aRg.aStart;
384
385 // Step 3: Moving into UndoArray...
387 rDocNds.MoveNodes( aRg, rNds, rNds.GetEndOfContent() );
388 m_oMvStt.emplace( rNds, m_nNode );
389 // remember difference!
391
392 if( pSttTextNd && pEndTextNd )
393 {
394 //Step 4: Moving around sections
395 m_nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
396 // nSect is the number of sections which starts(ends) between start
397 // and end node of the selection. The "loser" paragraph has to be
398 // moved into the section(s) of the "winner" paragraph
399 if( m_nSectDiff )
400 {
401 if( m_bJoinNext )
402 {
403 SwNodeRange aMvRg( *pEndTextNd, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
404 rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart.GetNode() );
405 }
406 else
407 {
408 SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
409 rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd.GetNode() );
410 }
411 }
412 }
415 m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() );
416 }
417 else
418 m_nNode = SwNodeOffset(0); // moved no node -> no difference at the end
419
420 // Are there any Nodes that got deleted before that (FootNotes
421 // have ContentNodes)?
422 if( !pSttTextNd && !pEndTextNd )
423 {
424 m_nNdDiff = m_nSttNode - rPam.GetPoint()->GetNodeIndex() - (bFullPara ? 0 : 1);
425 rPam.Move( fnMoveForward, GoInNode );
426 }
427 else
428 {
430 if( m_nSectDiff && m_bBackSp )
432 m_nNdDiff -= rPam.GetPoint()->GetNodeIndex();
433 }
434
435 if( !rPam.GetPointNode().IsContentNode() )
436 rPam.GetPoint()->nContent.Assign( nullptr, 0 );
437
438 // is a history necessary here at all?
439 if( m_pHistory && !m_pHistory->Count() )
440 m_pHistory.reset();
441}
442
443bool SwUndoDelete::SaveContent( const SwPosition* pStt, const SwPosition* pEnd,
444 SwTextNode* pSttTextNd, SwTextNode* pEndTextNd )
445{
446 SwNodeOffset nNdIdx = pStt->GetNodeIndex();
447 // 1 - copy start in Start-String
448 if( pSttTextNd )
449 {
450 bool bOneNode = m_nSttNode == m_nEndNode;
451 SwRegHistory aRHst( *pSttTextNd, m_pHistory.get() );
452 // always save all text atttibutes because of possibly overlapping
453 // areas of on/off
454 m_pHistory->CopyAttr( pSttTextNd->GetpSwpHints(), nNdIdx,
455 0, pSttTextNd->GetText().getLength(), true );
456 if( !bOneNode && pSttTextNd->HasSwAttrSet() )
457 m_pHistory->CopyFormatAttr( *pSttTextNd->GetpSwAttrSet(), nNdIdx );
458
459 // the length might have changed (!!Fields!!)
460 sal_Int32 nLen = (bOneNode
461 ? pEnd->GetContentIndex()
462 : pSttTextNd->GetText().getLength())
463 - pStt->GetContentIndex();
464
465 // delete now also the text (all attribute changes are added to
466 // UNDO history)
467 m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen);
468 pSttTextNd->EraseText( *pStt, nLen );
469 if( pSttTextNd->GetpSwpHints() )
470 pSttTextNd->GetpSwpHints()->DeRegister();
471
472 // METADATA: store
473 bool emptied( !m_aSttStr->isEmpty() && !pSttTextNd->Len() );
474 if (!bOneNode || emptied) // merging may overwrite xmlids...
475 {
476 m_pMetadataUndoStart = emptied
477 ? pSttTextNd->CreateUndoForDelete()
478 : pSttTextNd->CreateUndo();
479 }
480
481 if( bOneNode )
482 return false; // stop moving more nodes
483 }
484
485 // 2 - copy end into End-String
486 if( pEndTextNd )
487 {
488 SwContentIndex aEndIdx( pEndTextNd );
489 nNdIdx = pEnd->GetNodeIndex();
490 SwRegHistory aRHst( *pEndTextNd, m_pHistory.get() );
491
492 // always save all text atttibutes because of possibly overlapping
493 // areas of on/off
494 m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), nNdIdx, 0,
495 pEndTextNd->GetText().getLength(), true );
496
497 if( pEndTextNd->HasSwAttrSet() )
498 m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), nNdIdx );
499
500 // delete now also the text (all attribute changes are added to
501 // UNDO history)
502 m_aEndStr = pEndTextNd->GetText().copy( 0, pEnd->GetContentIndex() );
503 pEndTextNd->EraseText( aEndIdx, pEnd->GetContentIndex() );
504 if( pEndTextNd->GetpSwpHints() )
505 pEndTextNd->GetpSwpHints()->DeRegister();
506
507 // METADATA: store
508 bool emptied = !m_aEndStr->isEmpty() && !pEndTextNd->Len();
509
510 m_pMetadataUndoEnd = emptied
511 ? pEndTextNd->CreateUndoForDelete()
512 : pEndTextNd->CreateUndo();
513 }
514
515 // if there are only two Nodes then we're done
516 if( ( pSttTextNd || pEndTextNd ) && m_nSttNode + 1 == m_nEndNode )
517 return false; // do not move any Node
518
519 return true; // move Nodes lying in between
520}
521
522bool SwUndoDelete::CanGrouping( SwDoc& rDoc, const SwPaM& rDelPam )
523{
524 // Is Undo greater than one Node (that is Start and EndString)?
525 if( !m_aSttStr || m_aSttStr->isEmpty() || m_aEndStr )
526 return false;
527
528 // only the deletion of single char's can be condensed
530 return false;
531
532 auto [pStt, pEnd] = rDelPam.StartEnd(); // SwPosition*
533
534 if( pStt->GetNode() != pEnd->GetNode() ||
535 pStt->GetContentIndex()+1 != pEnd->GetContentIndex() ||
536 pEnd->nNode != m_nSttNode )
537 return false;
538
539 // Distinguish between BackSpace and Delete because the Undo array needs to
540 // be constructed differently!
541 if( pEnd->GetContentIndex() == m_nSttContent )
542 {
543 if( m_bGroup && !m_bBackSp ) return false;
544 m_bBackSp = true;
545 }
546 else if( pStt->GetContentIndex() == m_nSttContent )
547 {
548 if( m_bGroup && m_bBackSp ) return false;
549 m_bBackSp = false;
550 }
551 else
552 return false;
553
554 // are both Nodes (Node/Undo array) TextNodes at all?
555 SwTextNode * pDelTextNd = pStt->GetNode().GetTextNode();
556 if( !pDelTextNd ) return false;
557
558 sal_Int32 nUChrPos = m_bBackSp ? 0 : m_aSttStr->getLength()-1;
559 sal_Unicode cDelChar = pDelTextNd->GetText()[ pStt->GetContentIndex() ];
560 CharClass& rCC = GetAppCharClass();
561 if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) ||
562 rCC.isLetterNumeric( OUString( cDelChar ), 0 ) !=
563 rCC.isLetterNumeric( *m_aSttStr, nUChrPos ) )
564 return false;
565
566 // tdf#132725 - if at-char/at-para flys would be deleted, don't group!
567 // DelContentIndex() would be called at the wrong time here, the indexes
568 // in the stored SwHistoryTextFlyCnt would be wrong when Undo is invoked
569 if (IsFlySelectedByCursor(rDoc, *pStt, *pEnd))
570 {
571 return false;
572 }
573
574 {
575 SwRedlineSaveDatas aTmpSav;
576 const bool bSaved = FillSaveData( rDelPam, aTmpSav, false );
577
578 bool bOk = ( !m_pRedlSaveData && !bSaved ) ||
579 ( m_pRedlSaveData && bSaved &&
581 // aTmpSav.DeleteAndDestroyAll();
582 if( !bOk )
583 return false;
584
585 rDoc.getIDocumentRedlineAccess().DeleteRedline( rDelPam, false, RedlineType::Any );
586 }
587
588 // Both 'deletes' can be consolidated, so 'move' the related character
589 if( m_bBackSp )
590 m_nSttContent--; // BackSpace: add char to array!
591 else
592 {
593 m_nEndContent++; // Delete: attach char at the end
594 nUChrPos++;
595 }
596 m_aSttStr = m_aSttStr->replaceAt( nUChrPos, 0, rtl::OUStringChar(cDelChar) );
597 pDelTextNd->EraseText( *pStt, 1 );
598
599 m_bGroup = true;
600 return true;
601}
602
604{
605 if( m_oMvStt ) // Delete also the selection from UndoNodes array
606 {
607 // Insert saves content in IconSection
608 m_oMvStt->GetNode().GetNodes().Delete( *m_oMvStt, m_nNode );
609 m_oMvStt.reset();
610 }
611 m_pRedlSaveData.reset();
612}
613
615{
616 SwRewriter aRewriter;
617
618 bool bDone = false;
619
620 for ( sal_uInt16 n = 0; n < rHistory.Count(); n++)
621 {
622 OUString aDescr = rHistory[n]->GetDescription();
623
624 if (!aDescr.isEmpty())
625 {
626 aRewriter.AddRule(UndoArg2, aDescr);
627
628 bDone = true;
629 break;
630 }
631 }
632
633 if (! bDone)
634 {
635 aRewriter.AddRule(UndoArg2, SwResId(STR_FIELD));
636 }
637
638 return aRewriter;
639}
640
642{
643 switch (nChar)
644 {
646 case CH_TXTATR_INWORD:
647 case CH_TXTATR_TAB:
655 return true;
656
657 default:
658 break;
659 }
660
661 return false;
662}
663
664static OUString lcl_DenotedPortion(std::u16string_view rStr, sal_Int32 nStart, sal_Int32 nEnd, bool bQuoted)
665{
666 OUString aResult;
667
668 auto nCount = nEnd - nStart;
669 if (nCount > 0)
670 {
671 sal_Unicode cLast = rStr[nEnd - 1];
672 if (lcl_IsSpecialCharacter(cLast))
673 {
674 switch(cLast)
675 {
676 case CH_TXTATR_TAB:
677 aResult = SwResId(STR_UNDO_TABS, nCount);
678
679 break;
681 aResult = SwResId(STR_UNDO_NLS, nCount);
682
683 break;
684
685 case CH_TXTATR_INWORD:
688 break;
689
696 break; // nothing?
697
698 default:
699 assert(!"unexpected special character");
700 break;
701 }
702 SwRewriter aRewriter;
703 aRewriter.AddRule(UndoArg1, OUString::number(nCount));
704 aResult = aRewriter.Apply(aResult);
705 }
706 else if (bQuoted)
707 {
708 aResult = SwResId(STR_START_QUOTE) +
709 rStr.substr(nStart, nCount) +
710 SwResId(STR_END_QUOTE);
711 }
712 else
713 aResult = rStr.substr(nStart, nCount);
714 }
715
716 return aResult;
717}
718
719OUString DenoteSpecialCharacters(const OUString & rStr, bool bQuoted)
720{
721 OUStringBuffer aResult;
722
723 if (!rStr.isEmpty())
724 {
725 bool bStart = false;
726 sal_Int32 nStart = 0;
727 sal_Unicode cLast = 0;
728
729 for( sal_Int32 i = 0; i < rStr.getLength(); i++)
730 {
731 if (lcl_IsSpecialCharacter(rStr[i]))
732 {
733 if (cLast != rStr[i])
734 bStart = true;
735
736 }
737 else
738 {
739 if (lcl_IsSpecialCharacter(cLast))
740 bStart = true;
741 }
742
743 if (bStart)
744 {
745 aResult.append(lcl_DenotedPortion(rStr, nStart, i, bQuoted));
746
747 nStart = i;
748 bStart = false;
749 }
750
751 cLast = rStr[i];
752 }
753
754 aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength(), bQuoted));
755 }
756 else
758
759 return aResult.makeStringAndClear();
760}
761
763{
764 SwRewriter aResult;
765
766 if (m_nNode != SwNodeOffset(0))
767 {
768 if (!m_sTableName.isEmpty())
769 {
770
771 SwRewriter aRewriter;
772 aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
773 aRewriter.AddRule(UndoArg2, m_sTableName);
774 aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
775
776 OUString sTmp = aRewriter.Apply(SwResId(STR_TABLE_NAME));
777 aResult.AddRule(UndoArg1, sTmp);
778 }
779 else
780 aResult.AddRule(UndoArg1, SwResId(STR_PARAGRAPHS));
781 }
782 else
783 {
784 OUString aStr;
785
786 if (m_aSttStr && m_aEndStr && m_aSttStr->isEmpty() &&
787 m_aEndStr->isEmpty())
788 {
789 aStr = SwResId(STR_PARAGRAPH_UNDO);
790 }
791 else
792 {
793 std::optional<OUString> aTmpStr;
794 if (m_aSttStr)
795 aTmpStr = m_aSttStr;
796 else if (m_aEndStr)
797 aTmpStr = m_aEndStr;
798
799 if (aTmpStr)
800 {
801 aStr = DenoteSpecialCharacters(*aTmpStr);
802 }
803 else
804 {
806 }
807 }
808
810 if (m_pHistory)
811 {
813 aStr = aRewriter.Apply(aStr);
814 }
815
816 aResult.AddRule(UndoArg1, aStr);
817 }
818
819 return aResult;
820}
821
822// Every object, anchored "AtContent" will be reanchored at rPos
823static void lcl_ReAnchorAtContentFlyFrames( const SwFrameFormats& rSpzArr, const SwPosition &rPos, SwNodeOffset nOldIdx )
824{
825 if( rSpzArr.empty() )
826 return;
827
828 SwFrameFormat* pFormat;
829 const SwFormatAnchor* pAnchor;
830 const SwPosition* pAPos;
831 for( size_t n = 0; n < rSpzArr.size(); ++n )
832 {
833 pFormat = rSpzArr[n];
834 pAnchor = &pFormat->GetAnchor();
835 if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
836 {
837 pAPos = pAnchor->GetContentAnchor();
838 if( pAPos && nOldIdx == pAPos->GetNodeIndex() )
839 {
840 SwFormatAnchor aAnch( *pAnchor );
841 aAnch.SetAnchor( &rPos );
842 pFormat->SetFormatAttr( aAnch );
843 }
844 }
845 }
846}
847
849{
850 SwDoc& rDoc = rContext.GetDoc();
851
852 SwNodeOffset nCalcStt = m_nSttNode - m_nNdDiff;
853
854 if( m_nSectDiff && m_bBackSp )
855 nCalcStt += m_nSectDiff;
856
857 SwNodeIndex aIdx(rDoc.GetNodes(), nCalcStt);
858 SwNode* pInsNd = &aIdx.GetNode();
859 SwNode* pMovedNode = nullptr;
860
861 { // code block so that SwPosition is detached when deleting a Node
862 SwPosition aPos( aIdx );
863 if( !m_bDelFullPara )
864 {
865 assert(!m_bTableDelLastNd || pInsNd->IsTextNode());
866 if( pInsNd->IsTableNode() )
867 {
868 pInsNd = rDoc.GetNodes().MakeTextNode( aIdx.GetNode(),
869 rDoc.GetDfltTextFormatColl() );
870 --aIdx;
871 aPos.nNode = aIdx;
872 aPos.nContent.Assign( pInsNd->GetContentNode(), m_nSttContent );
873 }
874 else
875 {
876 if( pInsNd->IsContentNode() )
877 aPos.nContent.Assign( static_cast<SwContentNode*>(pInsNd), m_nSttContent );
878 if( !m_bTableDelLastNd )
879 pInsNd = nullptr; // do not delete Node!
880 }
881 }
882 else
883 pInsNd = nullptr; // do not delete Node!
884
885 bool bNodeMove = SwNodeOffset(0) != m_nNode;
886
887 if( m_aEndStr )
888 {
889 // discard attributes since they all saved!
890 SwTextNode * pTextNd;
891 if (!m_bDelFullPara && aPos.GetNode().IsSectionNode())
892 { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below
893 assert(m_nSttContent == 0);
894 assert(!m_aSttStr);
895 pTextNd = rDoc.GetNodes()[aPos.GetNodeIndex() + 1]->GetTextNode();
896 }
897 else
898 {
899 pTextNd = aPos.GetNode().GetTextNode();
900 }
901
902 if( pTextNd && pTextNd->HasSwAttrSet() )
903 pTextNd->ResetAllAttr();
904
905 if( pTextNd && pTextNd->GetpSwpHints() )
906 pTextNd->ClearSwpHintsArr( true );
907
909 {
910 SwNodeOffset nOldIdx = aPos.GetNodeIndex();
911 rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
912 // After the split all objects are anchored at the first
913 // paragraph, but the pHistory of the fly frame formats relies
914 // on anchoring at the start of the selection
915 // => selection backwards needs a correction.
916 if( m_bBackSp )
918 pTextNd = aPos.GetNode().GetTextNode();
919 }
920 assert(pTextNd); // else where does m_aEndStr come from?
921 if( pTextNd )
922 {
923 SwContentIndex aTmpIdx(pTextNd, aPos.GetContentIndex());
924 OUString const ins( pTextNd->InsertText(*m_aEndStr, aTmpIdx,
926 assert(ins.getLength() == m_aEndStr->getLength()); // must succeed
927 (void) ins;
928 // METADATA: restore
930 }
931 }
932 else if (m_aSttStr && bNodeMove && pInsNd == nullptr)
933 {
934 SwTextNode * pNd = aPos.GetNode().GetTextNode();
935 if( pNd )
936 {
937 if (m_nSttContent < pNd->GetText().getLength())
938 {
939 SwNodeOffset nOldIdx = aPos.GetNodeIndex();
940 rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
941 if( m_bBackSp )
943 }
944 else
945 ++aPos.nNode;
946 }
947 }
948 if( m_nSectDiff )
949 {
950 SwNodeOffset nMoveIndex = aPos.GetNodeIndex();
951 SwNodeOffset nDiff(0);
952 if( m_bJoinNext )
953 {
954 nMoveIndex += m_nSectDiff + 1;
955 pMovedNode = &aPos.GetNode();
956 }
957 else
958 {
959 nMoveIndex -= m_nSectDiff + 1;
960 ++nDiff;
961 }
962 SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
963 SwNodeRange aRg( aPos.nNode, SwNodeOffset(0) - nDiff, aPos.nNode, SwNodeOffset(1) - nDiff );
964 --aPos.nNode;
965 if( !m_bJoinNext )
966 pMovedNode = &aPos.GetNode();
967 rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx.GetNode());
968 ++aPos.nNode;
969 }
970
971 if( bNodeMove )
972 {
974 SwNodeIndex aCopyIndex( aPos.GetNode(), -1 );
975 rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.GetNode(),
976 // sw_redlinehide: delay creating frames: the flags on the
977 // nodes aren't necessarily up-to-date, and the redlines
978 // from m_pRedlSaveData aren't applied yet...
979 false);
980
981 if( m_nReplaceDummy )
982 {
983 SwNodeOffset nMoveIndex;
984 if( m_bJoinNext )
985 {
986 nMoveIndex = m_nEndNode - m_nNdDiff;
987 aPos.nNode = nMoveIndex + m_nReplaceDummy;
988 }
989 else
990 {
991 aPos.Assign( aCopyIndex );
992 nMoveIndex = aPos.GetNodeIndex() + m_nReplaceDummy + 1;
993 }
994 SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
995 SwNodeRange aRg( aPos.nNode, SwNodeOffset(0), aPos.nNode, SwNodeOffset(1) );
996 pMovedNode = &aPos.GetNode();
997 // tdf#131684 without deleting frames
998 rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx.GetNode(), false);
999 rDoc.GetNodes().Delete( aMvIdx);
1000 }
1001 }
1002
1003 if( m_aSttStr )
1004 {
1006 SwTextNode * pTextNd = aPos.GetNode().GetTextNode();
1007 // If more than a single Node got deleted, also all "Node"
1008 // attributes were saved
1009 if (pTextNd != nullptr)
1010 {
1011 if( pTextNd->HasSwAttrSet() && bNodeMove && !m_aEndStr )
1012 pTextNd->ResetAllAttr();
1013
1014 if( pTextNd->GetpSwpHints() )
1015 pTextNd->ClearSwpHintsArr( true );
1016
1017 // SectionNode mode and selection from top to bottom:
1018 // -> in StartNode is still the rest of the Join => delete
1019 aPos.nContent.Assign( pTextNd, m_nSttContent );
1020 pTextNd->SetInSwUndo(true);
1021 OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos,
1023 pTextNd->SetInSwUndo(false);
1024 assert(ins.getLength() == m_aSttStr->getLength()); // must succeed
1025 (void) ins;
1026 // METADATA: restore
1027 pTextNd->RestoreMetadata(m_pMetadataUndoStart);
1028 }
1029 }
1030
1031 if( m_pHistory )
1032 {
1033 m_pHistory->TmpRollback(&rDoc, m_nSetPos, false);
1034 if( m_nSetPos ) // there were Footnodes/FlyFrames
1035 {
1036 // are there others than these ones?
1037 if( m_nSetPos < m_pHistory->Count() )
1038 {
1039 // if so save the attributes of the others
1040 SwHistory aHstr;
1041 aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
1042 m_pHistory->Rollback(&rDoc);
1043 m_pHistory->Move( 0, &aHstr );
1044 }
1045 else
1046 {
1047 m_pHistory->Rollback(&rDoc);
1048 m_pHistory.reset();
1049 }
1050 }
1051 }
1052
1054 {
1055 sal_uInt16 nStt = m_bResetPgDesc ? sal_uInt16(RES_PAGEDESC) : sal_uInt16(RES_BREAK);
1056 sal_uInt16 nEnd = m_bResetPgBrk ? sal_uInt16(RES_BREAK) : sal_uInt16(RES_PAGEDESC);
1057
1058 SwNode* pNode = rDoc.GetNodes()[ m_nEndNode + 1 ];
1059 if( pNode->IsContentNode() )
1060 static_cast<SwContentNode*>(pNode)->ResetAttr( nStt, nEnd );
1061 else if( pNode->IsTableNode() )
1062 static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt, nEnd );
1063 }
1064 }
1065 // delete the temporarily added Node
1066 if (pInsNd && !m_bTableDelLastNd)
1067 {
1068 assert(&aIdx.GetNode() == pInsNd);
1069 rDoc.GetNodes().Delete( aIdx );
1070 }
1071 if( m_pRedlSaveData )
1073
1074 SwNodeOffset delFullParaEndNode(m_nEndNode);
1076 {
1077 SwTextNode * pFirstMergedDeletedTextNode(nullptr);
1078 SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, *this,
1079 *m_pRedlSaveData, pFirstMergedDeletedTextNode);
1080 if (pNextNode)
1081 {
1082 bool bNonMerged(false);
1083 std::vector<SwTextFrame*> frames;
1085 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
1086 {
1087 if (pFrame->getRootFrame()->HasMergedParas())
1088 {
1089 frames.push_back(pFrame);
1090 }
1091 else
1092 {
1093 bNonMerged = true;
1094 }
1095 }
1096 for (SwTextFrame *const pFrame : frames)
1097 {
1098 // could either destroy the text frames, or move them...
1099 // destroying them would have the advantage that we don't
1100 // need special code to *exclude* pFirstMergedDeletedTextNode
1101 // from MakeFrames for the layouts in Hide mode but not
1102 // layouts in Show mode ...
1103 // ... except that MakeFrames won't create them then :(
1104 pFrame->RegisterToNode(*pFirstMergedDeletedTextNode);
1105 assert(pFrame->GetMergedPara());
1106 assert(!bNonMerged); // delFullParaEndNode is such an awful hack
1107 (void) bNonMerged;
1108 delFullParaEndNode = pFirstMergedDeletedTextNode->GetIndex();
1109 }
1110 }
1111 }
1112 else if (m_aSttStr && (!m_bFromTableCopy || SwNodeOffset(0) != m_nNode))
1113 {
1114 // only now do we have redlines in the document again; fix up the split
1115 // frames
1116 SwTextNode *const pStartNode(aIdx.GetNodes()[m_nSttNode]->GetTextNode());
1117 assert(pStartNode);
1118 sw::RecreateStartTextFrames(*pStartNode);
1119 }
1120
1121 // create frames after SetSaveData has recreated redlines
1122 if (SwNodeOffset(0) != m_nNode)
1123 {
1124 // tdf#136453 only if section nodes at the start
1126 {
1127 // tdf#134252 *first* create outer section frames
1128 // note: text node m_nSttNode currently has frame with an upper;
1129 // there's a hack in InsertCnt_() to move it below new section frame
1131 SwNode& end(*rDoc.GetNodes()[m_nSttNode]); // exclude m_nSttNode
1132 ::MakeFrames(&rDoc, start, end);
1133 }
1134 // tdf#121031 if the start node is a text node, it already has a frame;
1135 // if it's a table, it does not
1136 // tdf#109376 exception: end on non-text-node -> start node was inserted
1137 assert(!m_bDelFullPara || (m_nSectDiff == SwNodeOffset(0)));
1138 SwNode& start(*rDoc.GetNodes()[m_nSttNode +
1139 ((m_bDelFullPara || !rDoc.GetNodes()[m_nSttNode]->IsTextNode() || pInsNd)
1140 ? 0 : 1)]);
1141 // don't include end node in the range: it may have been merged already
1142 // by the start node, or it may be merged by one of the moved nodes,
1143 // but if it isn't merged, its current frame(s) should be good...
1145 ? delFullParaEndNode
1146 // tdf#147310 SwDoc::DeleteRowCol() may delete whole table - end must be node following table!
1147 : (m_nEndNode + (rDoc.GetNodes()[m_nSttNode]->IsTableNode() && rDoc.GetNodes()[m_nEndNode]->IsEndNode() ? 1 : 0))]);
1148 ::MakeFrames(&rDoc, start, end);
1149 }
1150
1151 if (pMovedNode)
1152 { // probably better do this after creating all frames
1153 lcl_MakeAutoFrames(*rDoc.GetSpzFrameFormats(), pMovedNode->GetIndex());
1154 }
1155
1156 // tdf#134021 only after MakeFrames(), because it may be the only node
1157 // that has layout frames
1158 if (pInsNd && m_bTableDelLastNd)
1159 {
1160 assert(&aIdx.GetNode() == pInsNd);
1161 SwPaM tmp(aIdx, aIdx);
1163 }
1164
1165 AddUndoRedoPaM(rContext, true);
1166}
1167
1169{
1170 SwPaM & rPam = AddUndoRedoPaM(rContext);
1171 SwDoc& rDoc = rPam.GetDoc();
1172
1173 if( m_pRedlSaveData )
1174 {
1175 const bool bSuccess = FillSaveData(rPam, *m_pRedlSaveData);
1176 OSL_ENSURE(bSuccess,
1177 "SwUndoDelete::Redo: used to have redline data, but now none?");
1178 if (!bSuccess)
1179 {
1180 m_pRedlSaveData.reset();
1181 }
1182 }
1183
1184 if( !m_bDelFullPara )
1185 {
1186 // tdf#128739 correct cursors but do not delete bookmarks yet
1187 ::PaMCorrAbs(rPam, *rPam.End());
1188 SetPaM(rPam);
1189
1190 if( !m_bJoinNext ) // then restore selection from bottom to top
1191 rPam.Exchange();
1192 }
1193
1194 if( m_pHistory ) // are the attributes saved?
1195 {
1196 m_pHistory->SetTmpEnd( m_pHistory->Count() );
1197 SwHistory aHstr;
1198 aHstr.Move( 0, m_pHistory.get() );
1199
1200 if( m_bDelFullPara )
1201 {
1202 OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
1203 DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
1205
1206 DelBookmarks(rPam.GetMark()->GetNode(), rPam.GetPoint()->GetNode());
1207 }
1208 else
1209 {
1210 DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
1213 }
1214 m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
1215
1216 m_pHistory->Move( m_nSetPos, &aHstr );
1217 }
1218 else
1219 {
1220 if( m_bDelFullPara )
1221 {
1222 OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
1223 DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
1225
1226 DelBookmarks( rPam.GetMark()->GetNode(), rPam.GetPoint()->GetNode() );
1227 }
1228 else
1229 {
1230 DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
1233 }
1234 m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
1235 }
1236
1237 if( !m_aSttStr && !m_aEndStr )
1238 {
1240 {
1242 }
1243
1244 SwNode& rSttNd = ( m_bDelFullPara || m_bJoinNext )
1245 ? rPam.GetMark()->GetNode()
1246 : rPam.GetPoint()->GetNode();
1247 SwTableNode* pTableNd = rSttNd.GetTableNode();
1248 if( pTableNd )
1249 {
1250 if( m_bTableDelLastNd )
1251 {
1252 // than add again a Node at the end
1253 const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
1254 rDoc.GetNodes().MakeTextNode( aTmpIdx.GetNode(),
1256 }
1257
1258 SwContentNode* pNextNd = rDoc.GetNodes()[
1259 pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
1260 if( pNextNd )
1261 {
1262 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
1263
1264 if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
1265 false ) )
1266 pNextNd->SetAttr( *pItem );
1267
1268 if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
1269 false ) )
1270 pNextNd->SetAttr( *pItem );
1271 }
1272 pTableNd->DelFrames();
1273 }
1274 else if (*rPam.GetMark() == *rPam.GetPoint())
1275 { // paragraph with only footnote or as-char fly, delete that
1276 // => DelContentIndex has already deleted it! nothing to do here
1277 assert(m_nEndNode == m_nSttNode);
1278 return;
1279 }
1280
1281 // avoid asserts from ~SwContentIndexReg for deleted nodes
1282 SwPaM aTmp(*rPam.End());
1283 if (!aTmp.Move(fnMoveForward, GoInNode))
1284 {
1285 *aTmp.GetPoint() = *rPam.Start();
1287 }
1288 // coverity[copy_paste_error : FALSE] : GetNode() is intentional on both branches
1289 assert(aTmp.GetPoint()->GetNode() != rPam.GetPoint()->GetNode()
1290 && aTmp.GetPoint()->GetNode() != rPam.GetMark()->GetNode());
1291 ::PaMCorrAbs(rPam, *aTmp.GetPoint());
1292
1293 rPam.DeleteMark();
1294
1295 rDoc.GetNodes().Delete( rSttNd, m_nEndNode - m_nSttNode );
1296 }
1297 else if( m_bDelFullPara )
1298 {
1299 assert(!"dead code");
1300 // The Pam was incremented by one at Point (== end) to provide space
1301 // for UNDO. This now needs to be reverted!
1302 --rPam.End()->nNode;
1303 if( rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode() )
1304 *rPam.GetMark() = *rPam.GetPoint();
1306 }
1307 else
1308 // FIXME: this ends up calling DeleteBookmarks() on the entire rPam which deletes too many!
1310}
1311
1313{
1314 // this action does not seem idempotent,
1315 // so make sure it is only executed once on repeat
1316 if (rContext.m_bDeleteRepeated)
1317 return;
1318
1319 SwPaM & rPam = rContext.GetRepeatPaM();
1320 SwDoc& rDoc = rPam.GetDoc();
1321 ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1322 if( !rPam.HasMark() )
1323 {
1324 rPam.SetMark();
1326 }
1327 if( m_bDelFullPara )
1329 else
1331 rContext.m_bDeleteRepeated = true;
1332}
1333
1334void SwUndoDelete::SetTableName(const OUString & rName)
1335{
1336 m_sTableName = rName;
1337}
1338
1340{
1341 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDelete"));
1342 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1343 SwUndo::dumpAsXml(pWriter);
1345 (void)xmlTextWriterEndElement(pWriter);
1346}
1347
1348/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
struct _xmlTextWriter * xmlTextWriterPtr
@ ArtificialSelection
keyboard delete, artificial selection, avoid deleting flys
@ UndoArg1
Definition: SwRewriter.hxx:29
@ UndoArg3
Definition: SwRewriter.hxx:31
@ UndoArg2
Definition: SwRewriter.hxx:30
const int nUndoStringLength
Definition: UndoCore.hxx:243
OUString ShortenString(const OUString &rStr, sal_Int32 nLength, const OUString &rFillStr)
Shortens a string to a maximum length.
Definition: undobj.cxx:1538
bool isLetterNumeric(const OUString &rStr, sal_Int32 nPos) const
virtual bool DeleteAndJoin(SwPaM &, SwDeleteFlags flags=SwDeleteFlags::Default)=0
complete delete of a given PaM
virtual bool SplitNode(const SwPosition &rPos, bool bChkTableStart)=0
Split a node at rPos (implemented only for TextNode).
virtual bool DelFullPara(SwPaM &)=0
Delete full paragraphs.
virtual bool IsIgnoreRedline() const =0
virtual bool DeleteRedline(const SwPaM &rPam, bool bSaveInUndo, RedlineType nDelType)=0
virtual const SwRedlineTable & GetRedlineTable() const =0
virtual SwTextFormatColl * GetTextCollFromPool(sal_uInt16 nId, bool bRegardLanguage=true)=0
Return "Auto-Collection with ID.
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const
Marks a character position inside a document model content node (SwContentNode)
SwContentIndex & Assign(const SwContentNode *, sal_Int32)
Definition: index.cxx:206
bool HasSwAttrSet() const
Definition: node.hxx:473
virtual bool SetAttr(const SfxPoolItem &)
made virtual
Definition: node.cxx:1586
const SwAttrSet * GetpSwAttrSet() const
Definition: node.hxx:472
Definition: doc.hxx:192
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:316
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:145
SwNodes & GetNodes()
Definition: doc.hxx:413
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:336
IDocumentStylePoolAccess const & getIDocumentStylePoolAccess() const
Definition: doc.cxx:427
const SwTextFormatColl * GetDfltTextFormatColl() const
Definition: doc.hxx:780
const SwFrameFormats * GetSpzFrameFormats() const
Definition: doc.hxx:748
::sw::UndoManager & GetUndoManager()
Definition: doc.cxx:134
FlyAnchors.
Definition: fmtanchr.hxx:37
void SetAnchor(const SwPosition *pPos)
Definition: atrfrm.cxx:1586
RndStdIds GetAnchorId() const
Definition: fmtanchr.hxx:67
const SwPosition * GetContentAnchor() const
Definition: fmtanchr.hxx:69
Pagedescriptor Client of SwPageDesc that is "described" by the attribute.
Definition: fmtpdsc.hxx:36
virtual bool ResetFormatAttr(sal_uInt16 nWhich1, sal_uInt16 nWhich2=0)
Definition: format.cxx:620
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:83
virtual bool SetFormatAttr(const SfxPoolItem &rAttr)
Definition: format.cxx:449
const T * GetItemIfSet(TypedWhichId< T > nWhich, bool bSrchInParent=true) const
Templatized version of GetItemState() to directly return the correct type.
Definition: format.hxx:111
Style of a layout element.
Definition: frmfmt.hxx:62
virtual void MakeFrames()
Creates the views.
Definition: atrfrm.cxx:2713
Specific frame formats (frames, DrawObjects).
bool empty() const
size_t size() const
sal_uInt16 Count() const
Definition: rolbck.hxx:378
void Move(sal_uInt16 nPos, SwHistory *pIns, sal_uInt16 const nStart=0)
Definition: rolbck.hxx:386
TElementType * Next()
Definition: calbck.hxx:364
TElementType * First()
Definition: calbck.hxx:356
Marks a node in the document model.
Definition: ndindex.hxx:31
const SwNodes & GetNodes() const
Definition: ndindex.hxx:175
SwNode & GetNode() const
Definition: ndindex.hxx:136
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:171
SwNodeIndex aStart
Definition: ndindex.hxx:149
SwNodeIndex aEnd
Definition: ndindex.hxx:150
Base class of the Writer document model elements.
Definition: node.hxx:84
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:876
SwNodeOffset GetIndex() const
Definition: node.hxx:296
bool IsContentNode() const
Definition: node.hxx:656
bool IsEndNode() const
Definition: node.hxx:660
bool IsStartNode() const
Definition: node.hxx:652
bool IsSectionNode() const
Definition: node.hxx:672
bool IsTableNode() const
Definition: node.hxx:668
bool IsTextNode() const
Definition: node.hxx:664
const SwStartNode * StartOfSectionNode() const
Definition: node.hxx:137
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:705
SwContentNode * GetContentNode()
Definition: node.hxx:643
SwTableNode * GetTableNode()
Definition: node.hxx:627
const SwEndNode * EndOfSectionNode() const
Definition: node.hxx:710
SwTextNode * MakeTextNode(SwNode &rWhere, SwTextFormatColl *pColl, bool bNewFrames=true)
Implementations of "Make...Node" are in the given .cxx-files.
Definition: ndtxt.cxx:103
SwNode & GetEndOfContent() const
Regular ContentSection (i.e. the BodyText).
Definition: ndarr.hxx:165
bool MoveNodes(const SwNodeRange &, SwNodes &rNodes, SwNode &rPos, bool bNewFrames=true)
move the node pointer
Definition: nodes.cxx:405
void Delete(const SwNodeIndex &rPos, SwNodeOffset nNodes=SwNodeOffset(1))
Definition: nodes.cxx:1071
void Copy_(const SwNodeRange &rRg, SwNode &rInsPos, bool bNewFrames=true) const
Definition: ndarr.hxx:179
SwNodeOffset Count() const
Definition: ndarr.hxx:142
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:187
const SwPosition * GetMark() const
Definition: pam.hxx:263
SwNode & GetPointNode() const
Definition: pam.hxx:283
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:612
void Exchange()
Definition: pam.cxx:626
std::pair< const SwPosition *, const SwPosition * > StartEnd() const
Because sometimes the cost of the operator<= can add up.
Definition: pam.hxx:277
bool Move(SwMoveFnCollection const &fnMove=fnMoveForward, SwGoInDoc fnGo=GoInContent)
Movement of cursor.
Definition: pam.cxx:638
const SwPosition * End() const
Definition: pam.hxx:271
SwDoc & GetDoc() const
Definition: pam.hxx:299
void DeleteMark()
Definition: pam.hxx:231
const SwPosition * GetPoint() const
Definition: pam.hxx:261
const SwPosition * Start() const
Definition: pam.hxx:266
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:259
size_t size() const
Definition: UndoCore.hxx:77
bool empty() const
Definition: docary.hxx:266
void AddRule(SwUndoArg eWhat, const OUString &rWith)
Definition: SwRewriter.cxx:25
static OUString GetPlaceHolder(SwUndoArg eId)
Definition: SwRewriter.cxx:51
OUString Apply(const OUString &rStr) const
Definition: SwRewriter.cxx:39
const SwTable & GetTable() const
Definition: node.hxx:521
void DelFrames(SwRootFrame const *pLayout=nullptr)
Method deletes all views of document for the node.
Definition: ndtbl.cxx:2428
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:204
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:165
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:86
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:275
void ClearSwpHintsArr(bool bDelFields)
Definition: thints.cxx:3427
void EraseText(const SwContentIndex &rIdx, const sal_Int32 nCount=SAL_MAX_INT32, const SwInsertFlags nMode=SwInsertFlags::DEFAULT)
delete text content ATTENTION: must not be called with a range that overlaps the start of an attribut...
Definition: ndtxt.cxx:2724
OUString InsertText(const OUString &rStr, const SwContentIndex &rIdx, const SwInsertFlags nMode=SwInsertFlags::DEFAULT)
insert text content
Definition: ndtxt.cxx:2322
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:230
const OUString & GetText() const
Definition: ndtxt.hxx:222
SwTextFormatColl * GetTextColl() const
Definition: ndtxt.hxx:870
virtual sal_uInt16 ResetAllAttr() override
Definition: ndtxt.cxx:5252
SwPaM & AddUndoRedoPaM(::sw::UndoRedoContext &, bool const bCorrToContent=false) const
Definition: undobj.cxx:100
void SetPaM(SwPaM &, bool bCorrToContent=false) const
Definition: undobj.cxx:80
sal_Int32 m_nSttContent
Definition: undobj.hxx:232
SwNodeOffset m_nSttNode
Definition: undobj.hxx:231
SwNodeOffset m_nEndNode
Definition: undobj.hxx:231
sal_Int32 m_nEndContent
Definition: undobj.hxx:232
SwDeleteFlags m_DeleteFlags
Definition: UndoDelete.hxx:65
bool m_bResetPgBrk
Definition: UndoDelete.hxx:63
bool m_bDelFullPara
Definition: UndoDelete.hxx:61
SwNodeOffset m_nNode
Definition: UndoDelete.hxx:51
virtual void RedoImpl(::sw::UndoRedoContext &) override
Definition: undel.cxx:1168
bool m_bResetPgDesc
Definition: UndoDelete.hxx:62
void dumpAsXml(xmlTextWriterPtr pWriter) const override
Definition: undel.cxx:1339
std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart
Definition: UndoDelete.hxx:46
std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd
Definition: UndoDelete.hxx:47
bool SaveContent(const SwPosition *pStt, const SwPosition *pEnd, SwTextNode *pSttTextNd, SwTextNode *pEndTextNd)
Definition: undel.cxx:443
SwNodeOffset m_nSectDiff
Definition: UndoDelete.hxx:53
virtual void RepeatImpl(::sw::RepeatContext &) override
Definition: undel.cxx:1312
std::optional< SwNodeIndex > m_oMvStt
Definition: UndoDelete.hxx:43
std::unique_ptr< SwRedlineSaveDatas > m_pRedlSaveData
Definition: UndoDelete.hxx:45
virtual SwRewriter GetRewriter() const override
Returns rewriter for this undo object.
Definition: undel.cxx:762
bool m_bJoinNext
Definition: UndoDelete.hxx:59
virtual ~SwUndoDelete() override
Definition: undel.cxx:603
bool m_bTableDelLastNd
Definition: UndoDelete.hxx:60
OUString m_sTableName
Definition: UndoDelete.hxx:49
bool m_bFromTableCopy
Definition: UndoDelete.hxx:64
SwUndoDelete(SwPaM &, SwDeleteFlags flags, bool bFullPara=false, bool bCalledByTableCpy=false)
Definition: undel.cxx:176
virtual void UndoImpl(::sw::UndoRedoContext &) override
Definition: undel.cxx:848
std::optional< OUString > m_aEndStr
Definition: UndoDelete.hxx:44
SwNodeOffset m_nNdDiff
Definition: UndoDelete.hxx:52
SwNodeOffset m_nReplaceDummy
Definition: UndoDelete.hxx:54
void SetTableName(const OUString &rName)
Definition: undel.cxx:1334
std::optional< OUString > m_aSttStr
Definition: UndoDelete.hxx:44
bool CanGrouping(SwDoc &, const SwPaM &)
Definition: undel.cxx:522
sal_uInt16 m_nSetPos
Definition: UndoDelete.hxx:55
void DelContentIndex(const SwPosition &pMark, const SwPosition &pPoint, DelContentType nDelContentType=DelContentType::AllMask)
Definition: undobj.cxx:861
std::unique_ptr< SwHistory > m_pHistory
Definition: undobj.hxx:167
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const
Definition: undobj.cxx:700
static bool FillSaveData(const SwPaM &rRange, SwRedlineSaveDatas &rSData, bool bDelRange=true, bool bCopyNext=true)
Definition: undobj.cxx:1414
static void SetSaveData(SwDoc &rDoc, SwRedlineSaveDatas &rSData)
Definition: undobj.cxx:1478
static bool CanRedlineGroup(SwRedlineSaveDatas &rCurr, const SwRedlineSaveDatas &rCheck, bool bCurrIsEnd)
Definition: undobj.cxx:1506
bool m_bCacheComment
Definition: undobj.hxx:62
void DeRegister()
deregister the currently registered History
Definition: ndhints.hxx:197
void RestoreMetadata(std::shared_ptr< MetadatableUndo > const &i_pUndo)
std::shared_ptr< MetadatableUndo > CreateUndo() const
std::shared_ptr< MetadatableUndo > CreateUndoForDelete()
Blocks grouping undo actions together into an SfxListUndoAction.
SwPaM & GetRepeatPaM()
Definition: UndoCore.hxx:134
SwNodes const & GetUndoNodes() const
Definition: docundo.cxx:78
SwDoc & GetDoc() const
Definition: UndoCore.hxx:95
int nCount
void DelBookmarks(SwNode &rStt, const SwNode &rEnd, std::vector< SaveBookmark > *pSaveBkmk, std::optional< sal_Int32 > oStartContentIdx, std::optional< sal_Int32 > oEndContentIdx)
Definition: docbm.cxx:1914
void PaMCorrAbs(const SwPaM &rRange, const SwPosition &rNewPos)
Function declarations so that everything below the CursorShell can move the Cursor once in a while.
Definition: doccorr.cxx:88
void MakeFrames(SwDoc *pDoc, SwNode &rSttIdx, SwNode &rEndIdx)
Definition: frmtool.cxx:2013
#define CH_TXT_ATR_FIELDSEP
Definition: hintids.hxx:183
constexpr TypedWhichId< SvxFormatBreakItem > RES_BREAK(94)
#define CH_TXT_ATR_INPUTFIELDSTART
Definition: hintids.hxx:177
#define CH_TXT_ATR_FORMELEMENT
Definition: hintids.hxx:180
#define CH_TXTATR_TAB
Definition: hintids.hxx:175
constexpr TypedWhichId< SwFormatPageDesc > RES_PAGEDESC(93)
#define CH_TXTATR_INWORD
Definition: hintids.hxx:174
#define CH_TXT_ATR_INPUTFIELDEND
Definition: hintids.hxx:178
#define CH_TXT_ATR_FIELDEND
Definition: hintids.hxx:184
#define CH_TXTATR_NEWLINE
Definition: hintids.hxx:176
#define CH_TXT_ATR_FIELDSTART
Definition: hintids.hxx:182
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:173
CharClass & GetAppCharClass()
Definition: init.cxx:705
sal_Int64 n
aStr
int i
Count
end
void RecreateStartTextFrames(SwTextNode &rNode)
Definition: frmtool.cxx:1429
o3tl::strong_int< sal_Int32, struct Tag_SwNodeOffset > SwNodeOffset
Definition: nodeoffset.hxx:16
bool GoInNode(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:1133
bool GoInContent(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:1142
SwMoveFnCollection const & fnMoveBackward
Definition: paminit.cxx:58
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:59
@ RES_POOLCOLL_STANDARD
Standard.
Definition: poolfmt.hxx:250
Marks a position in the document model.
Definition: pam.hxx:37
SwNode & GetNode() const
Definition: pam.hxx:80
void Assign(const SwNode &rNd, SwNodeOffset nDelta, sal_Int32 nContentOffset=0)
These all set both nNode and nContent.
Definition: pam.cxx:230
SwNodeIndex nNode
Definition: pam.hxx:38
SwNodeOffset GetNodeIndex() const
Definition: pam.hxx:77
sal_Int32 GetContentIndex() const
Definition: pam.hxx:84
SwContentIndex nContent
Definition: pam.hxx:39
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:164
SwUndoId
Definition: swundo.hxx:30
sal_uInt16 sal_Unicode
static SwRewriter lcl_RewriterFromHistory(SwHistory &rHistory)
Definition: undel.cxx:614
static OUString lcl_DenotedPortion(std::u16string_view rStr, sal_Int32 nStart, sal_Int32 nEnd, bool bQuoted)
Definition: undel.cxx:664
static void lcl_ReAnchorAtContentFlyFrames(const SwFrameFormats &rSpzArr, const SwPosition &rPos, SwNodeOffset nOldIdx)
Definition: undel.cxx:823
OUString DenoteSpecialCharacters(const OUString &rStr, bool bQuoted)
Denotes special characters in a string.
Definition: undel.cxx:719
static void DelFullParaMoveFrames(SwDoc &rDoc, SwUndRng const &rRange, SwRedlineSaveDatas const &rRedlineSaveData)
Definition: undel.cxx:121
static SwTextNode * FindFirstAndNextNode(SwDoc &rDoc, SwUndRng const &rRange, SwRedlineSaveDatas const &rRedlineSaveData, SwTextNode *&o_rpFirstMergedDeletedTextNode)
Definition: undel.cxx:72
static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
Definition: undel.cxx:641
static void lcl_MakeAutoFrames(const SwFrameFormats &rSpzArr, SwNodeOffset nMovedIndex)
Definition: undel.cxx:57
bool IsFlySelectedByCursor(SwDoc const &rDoc, SwPosition const &rStart, SwPosition const &rEnd)
check at-char and at-para flys in rDoc
Definition: undobj.cxx:1689
DelContentType
Definition: undobj.hxx:135