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