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>
28 #include <unotools/charclass.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 */
57 static 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->nNode.GetIndex() )
67  pFormat->MakeFrames();
68  }
69  }
70 }
71 
72 static 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 
121 static 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;
131  SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode);
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  bool bFullPara,
179  bool bCalledByTableCpy )
180  : SwUndo(SwUndoId::DELETE, &rPam.GetDoc()),
181  SwUndRng( rPam ),
182  m_nNode(0),
183  m_nNdDiff(0),
184  m_nSectDiff(0),
185  m_nReplaceDummy(0),
186  m_nSetPos(0),
187  m_bGroup( false ),
188  m_bBackSp( false ),
189  m_bJoinNext( false ),
190  m_bTableDelLastNd( false ),
191  // bFullPara is set e.g. if an empty paragraph before a table is deleted
192  m_bDelFullPara( bFullPara ),
193  m_bResetPgDesc( false ),
194  m_bResetPgBrk( false ),
195  m_bFromTableCopy( bCalledByTableCpy )
196 {
197 
198  m_bCacheComment = false;
199 
200  SwDoc& rDoc = rPam.GetDoc();
201 
203  {
205  if( !FillSaveData( rPam, *m_pRedlSaveData ))
206  {
207  m_pRedlSaveData.reset();
208  }
209  }
210 
211  if( !m_pHistory )
212  m_pHistory.reset( new SwHistory );
213 
214  // delete all footnotes for now
215  const SwPosition *pStt = rPam.Start(),
216  *pEnd = rPam.End();
217 
218  // Step 1. deletion/record of content indices
219  if( m_bDelFullPara )
220  {
221  OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
222  DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
224 
225  ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
226  DelBookmarks(pStt->nNode, pEnd->nNode);
227  }
228  else
229  {
230  DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
231  ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
232  if (m_nEndNode - m_nSttNode > SwNodeOffset(1)) // check for fully selected nodes
233  {
234  SwNodeIndex const start(pStt->nNode, +1);
235  DelBookmarks(start, pEnd->nNode);
236  }
237  }
238 
239  m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
240 
241  // Is already anything deleted?
242  m_nNdDiff = m_nSttNode - pStt->nNode.GetIndex();
243 
244  m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
245  m_bBackSp = !bFullPara && !m_bJoinNext;
246 
247  SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr;
248  if( !bFullPara )
249  {
250  pSttTextNd = pStt->nNode.GetNode().GetTextNode();
251  pEndTextNd = m_nSttNode == m_nEndNode
252  ? pSttTextNd
253  : pEnd->nNode.GetNode().GetTextNode();
254  }
255  else if (m_pRedlSaveData)
256  {
257  DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
258  }
259 
260  bool bMoveNds = *pStt != *pEnd // any area still existent?
261  && ( SaveContent( pStt, pEnd, pSttTextNd, pEndTextNd ) || m_bFromTableCopy );
262 
263  if( pSttTextNd && pEndTextNd && pSttTextNd != pEndTextNd )
264  {
265  // two different TextNodes, thus save also the TextFormatCollection
266  m_pHistory->Add( pSttTextNd->GetTextColl(),pStt->nNode.GetIndex(), SwNodeType::Text );
267  m_pHistory->Add( pEndTextNd->GetTextColl(),pEnd->nNode.GetIndex(), SwNodeType::Text );
268 
269  if( !m_bJoinNext ) // Selection from bottom to top
270  {
271  // When using JoinPrev() all AUTO-PageBreak's will be copied
272  // correctly. To restore them with UNDO, Auto-PageBreak of the
273  // EndNode needs to be reset. Same for PageDesc and ColBreak.
274  if( pEndTextNd->HasSwAttrSet() )
275  {
276  SwRegHistory aRegHist( *pEndTextNd, m_pHistory.get() );
277  if( SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
278  RES_BREAK, false ) )
279  pEndTextNd->ResetAttr( RES_BREAK );
280  if( pEndTextNd->HasSwAttrSet() &&
281  SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
282  RES_PAGEDESC, false ) )
283  pEndTextNd->ResetAttr( RES_PAGEDESC );
284  }
285  }
286  }
287 
288  // Move now also the PaM. The SPoint is at the beginning of a SSelection.
289  if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTextNd || pEndTextNd ) )
290  rPam.Exchange();
291 
292  if( !pSttTextNd && !pEndTextNd )
293  --rPam.GetPoint()->nNode;
294  rPam.DeleteMark(); // the SPoint is in the selection
295 
296  if( !pEndTextNd )
297  m_nEndContent = 0;
298  if( !pSttTextNd )
299  m_nSttContent = 0;
300 
301  if( bMoveNds ) // Do Nodes exist that need to be moved?
302  {
303  SwNodes& rNds = rDoc.GetUndoManager().GetUndoNodes();
304  SwNodes& rDocNds = rDoc.GetNodes();
305  SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff,
306  rDocNds, m_nEndNode - m_nNdDiff );
307  if( !bFullPara && !pEndTextNd &&
308  &aRg.aEnd.GetNode() != &rDoc.GetNodes().GetEndOfContent() )
309  {
310  SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode();
311  if( pNode->GetIndex() >= m_nSttNode - m_nNdDiff )
312  ++aRg.aEnd; // Deletion of a complete table
313  }
314  SwNode* pTmpNd;
315  // Step 2: Expand selection if necessary
316  if( m_bJoinNext || bFullPara )
317  {
318  // If all content of a section will be moved into Undo, the section
319  // itself should be moved completely.
320  while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() &&
321  ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() &&
322  pTmpNd->StartOfSectionNode()->IsSectionNode() &&
323  pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) )
324  ++aRg.aEnd;
326  if( m_nReplaceDummy )
327  { // The selection has been expanded, because
328  ++aRg.aEnd;
329  if( pEndTextNd )
330  {
331  // The end text node has to leave the (expanded) selection
332  // The dummy is needed because MoveNodes deletes empty
333  // sections
334  ++m_nReplaceDummy;
335  SwNodeRange aMvRg( *pEndTextNd, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
336  SwPosition aSplitPos( *pEndTextNd );
337  ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
338  rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
339  rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
340  --aRg.aEnd;
341  }
342  else
344  }
345  }
346  if( m_bBackSp || bFullPara )
347  {
348  // See above, the selection has to be expanded if there are "nearly
349  // empty" sections and a replacement dummy has to be set if needed.
350  while( SwNodeOffset(1) < aRg.aStart.GetIndex() &&
351  ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() &&
352  pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) )
353  --aRg.aStart;
354  if( pSttTextNd )
355  {
357  if( m_nReplaceDummy )
358  {
359  SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
360  SwPosition aSplitPos( *pSttTextNd );
361  ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
362  rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
363  rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
364  --aRg.aStart;
365  }
366  }
367  }
368 
369  if( m_bFromTableCopy )
370  {
371  if( !pEndTextNd )
372  {
373  if( pSttTextNd )
374  ++aRg.aStart;
375  else if( !bFullPara && !aRg.aEnd.GetNode().IsContentNode() )
376  --aRg.aEnd;
377  }
378  }
379  else if (pSttTextNd && (pEndTextNd || pSttTextNd->GetText().getLength()))
380  ++aRg.aStart;
381 
382  // Step 3: Moving into UndoArray...
383  m_nNode = rNds.GetEndOfContent().GetIndex();
384  rDocNds.MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() ));
385  m_pMvStt.reset( new SwNodeIndex( rNds, m_nNode ) );
386  // remember difference!
388 
389  if( pSttTextNd && pEndTextNd )
390  {
391  //Step 4: Moving around sections
392  m_nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
393  // nSect is the number of sections which starts(ends) between start
394  // and end node of the selection. The "loser" paragraph has to be
395  // moved into the section(s) of the "winner" paragraph
396  if( m_nSectDiff )
397  {
398  if( m_bJoinNext )
399  {
400  SwNodeRange aMvRg( *pEndTextNd, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
401  rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
402  }
403  else
404  {
405  SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
406  rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
407  }
408  }
409  }
412  m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() );
413  }
414  else
415  m_nNode = SwNodeOffset(0); // moved no node -> no difference at the end
416 
417  // Are there any Nodes that got deleted before that (FootNotes
418  // have ContentNodes)?
419  if( !pSttTextNd && !pEndTextNd )
420  {
421  m_nNdDiff = m_nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1);
422  rPam.Move( fnMoveForward, GoInNode );
423  }
424  else
425  {
427  if( m_nSectDiff && m_bBackSp )
429  m_nNdDiff -= rPam.GetPoint()->nNode.GetIndex();
430  }
431 
432  if( !rPam.GetNode().IsContentNode() )
433  rPam.GetPoint()->nContent.Assign( nullptr, 0 );
434 
435  // is a history necessary here at all?
436  if( m_pHistory && !m_pHistory->Count() )
437  m_pHistory.reset();
438 }
439 
440 bool SwUndoDelete::SaveContent( const SwPosition* pStt, const SwPosition* pEnd,
441  SwTextNode* pSttTextNd, SwTextNode* pEndTextNd )
442 {
443  SwNodeOffset nNdIdx = pStt->nNode.GetIndex();
444  // 1 - copy start in Start-String
445  if( pSttTextNd )
446  {
447  bool bOneNode = m_nSttNode == m_nEndNode;
448  SwRegHistory aRHst( *pSttTextNd, m_pHistory.get() );
449  // always save all text atttibutes because of possibly overlapping
450  // areas of on/off
451  m_pHistory->CopyAttr( pSttTextNd->GetpSwpHints(), nNdIdx,
452  0, pSttTextNd->GetText().getLength(), true );
453  if( !bOneNode && pSttTextNd->HasSwAttrSet() )
454  m_pHistory->CopyFormatAttr( *pSttTextNd->GetpSwAttrSet(), nNdIdx );
455 
456  // the length might have changed (!!Fields!!)
457  sal_Int32 nLen = (bOneNode
458  ? pEnd->nContent.GetIndex()
459  : pSttTextNd->GetText().getLength())
460  - pStt->nContent.GetIndex();
461 
462  // delete now also the text (all attribute changes are added to
463  // UNDO history)
464  m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen);
465  pSttTextNd->EraseText( pStt->nContent, nLen );
466  if( pSttTextNd->GetpSwpHints() )
467  pSttTextNd->GetpSwpHints()->DeRegister();
468 
469  // METADATA: store
470  bool emptied( !m_aSttStr->isEmpty() && !pSttTextNd->Len() );
471  if (!bOneNode || emptied) // merging may overwrite xmlids...
472  {
473  m_pMetadataUndoStart = emptied
474  ? pSttTextNd->CreateUndoForDelete()
475  : pSttTextNd->CreateUndo();
476  }
477 
478  if( bOneNode )
479  return false; // stop moving more nodes
480  }
481 
482  // 2 - copy end into End-String
483  if( pEndTextNd )
484  {
485  SwIndex aEndIdx( pEndTextNd );
486  nNdIdx = pEnd->nNode.GetIndex();
487  SwRegHistory aRHst( *pEndTextNd, m_pHistory.get() );
488 
489  // always save all text atttibutes because of possibly overlapping
490  // areas of on/off
491  m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), nNdIdx, 0,
492  pEndTextNd->GetText().getLength(), true );
493 
494  if( pEndTextNd->HasSwAttrSet() )
495  m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), nNdIdx );
496 
497  // delete now also the text (all attribute changes are added to
498  // UNDO history)
499  m_aEndStr = pEndTextNd->GetText().copy( 0, pEnd->nContent.GetIndex() );
500  pEndTextNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() );
501  if( pEndTextNd->GetpSwpHints() )
502  pEndTextNd->GetpSwpHints()->DeRegister();
503 
504  // METADATA: store
505  bool emptied = !m_aEndStr->isEmpty() && !pEndTextNd->Len();
506 
507  m_pMetadataUndoEnd = emptied
508  ? pEndTextNd->CreateUndoForDelete()
509  : pEndTextNd->CreateUndo();
510  }
511 
512  // if there are only two Nodes then we're done
513  if( ( pSttTextNd || pEndTextNd ) && m_nSttNode + 1 == m_nEndNode )
514  return false; // do not move any Node
515 
516  return true; // move Nodes lying in between
517 }
518 
519 bool SwUndoDelete::CanGrouping( SwDoc& rDoc, const SwPaM& rDelPam )
520 {
521  // Is Undo greater than one Node (that is Start and EndString)?
522  if( !m_aSttStr || m_aSttStr->isEmpty() || m_aEndStr )
523  return false;
524 
525  // only the deletion of single char's can be condensed
527  return false;
528 
529  const SwPosition *pStt = rDelPam.Start(),
530  *pEnd = rDelPam.End();
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(rDoc, *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  rDoc.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, rtl::OUStringChar(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(std::u16string_view rStr, sal_Int32 nStart, sal_Int32 nEnd, bool bQuoted)
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 if (bQuoted)
705  {
706  aResult = SwResId(STR_START_QUOTE) +
707  rStr.substr(nStart, nCount) +
708  SwResId(STR_END_QUOTE);
709  }
710  else
711  aResult = rStr.substr(nStart, nCount);
712  }
713 
714  return aResult;
715 }
716 
717 OUString DenoteSpecialCharacters(const OUString & rStr, bool bQuoted)
718 {
719  OUStringBuffer aResult;
720 
721  if (!rStr.isEmpty())
722  {
723  bool bStart = false;
724  sal_Int32 nStart = 0;
725  sal_Unicode cLast = 0;
726 
727  for( sal_Int32 i = 0; i < rStr.getLength(); i++)
728  {
729  if (lcl_IsSpecialCharacter(rStr[i]))
730  {
731  if (cLast != rStr[i])
732  bStart = true;
733 
734  }
735  else
736  {
737  if (lcl_IsSpecialCharacter(cLast))
738  bStart = true;
739  }
740 
741  if (bStart)
742  {
743  aResult.append(lcl_DenotedPortion(rStr, nStart, i, bQuoted));
744 
745  nStart = i;
746  bStart = false;
747  }
748 
749  cLast = rStr[i];
750  }
751 
752  aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength(), bQuoted));
753  }
754  else
756 
757  return aResult.makeStringAndClear();
758 }
759 
761 {
762  SwRewriter aResult;
763 
764  if (m_nNode != SwNodeOffset(0))
765  {
766  if (!m_sTableName.isEmpty())
767  {
768 
769  SwRewriter aRewriter;
770  aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
771  aRewriter.AddRule(UndoArg2, m_sTableName);
772  aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
773 
774  OUString sTmp = aRewriter.Apply(SwResId(STR_TABLE_NAME));
775  aResult.AddRule(UndoArg1, sTmp);
776  }
777  else
778  aResult.AddRule(UndoArg1, SwResId(STR_PARAGRAPHS));
779  }
780  else
781  {
782  OUString aStr;
783 
784  if (m_aSttStr && m_aEndStr && m_aSttStr->isEmpty() &&
785  m_aEndStr->isEmpty())
786  {
787  aStr = SwResId(STR_PARAGRAPH_UNDO);
788  }
789  else
790  {
791  std::optional<OUString> aTmpStr;
792  if (m_aSttStr)
793  aTmpStr = m_aSttStr;
794  else if (m_aEndStr)
795  aTmpStr = m_aEndStr;
796 
797  if (aTmpStr)
798  {
799  aStr = DenoteSpecialCharacters(*aTmpStr);
800  }
801  else
802  {
804  }
805  }
806 
807  aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS));
808  if (m_pHistory)
809  {
811  aStr = aRewriter.Apply(aStr);
812  }
813 
814  aResult.AddRule(UndoArg1, aStr);
815  }
816 
817  return aResult;
818 }
819 
820 // Every object, anchored "AtContent" will be reanchored at rPos
821 static void lcl_ReAnchorAtContentFlyFrames( const SwFrameFormats& rSpzArr, const SwPosition &rPos, SwNodeOffset nOldIdx )
822 {
823  if( rSpzArr.empty() )
824  return;
825 
826  SwFrameFormat* pFormat;
827  const SwFormatAnchor* pAnchor;
828  const SwPosition* pAPos;
829  for( size_t n = 0; n < rSpzArr.size(); ++n )
830  {
831  pFormat = rSpzArr[n];
832  pAnchor = &pFormat->GetAnchor();
833  if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
834  {
835  pAPos = pAnchor->GetContentAnchor();
836  if( pAPos && nOldIdx == pAPos->nNode.GetIndex() )
837  {
838  SwFormatAnchor aAnch( *pAnchor );
839  aAnch.SetAnchor( &rPos );
840  pFormat->SetFormatAttr( aAnch );
841  }
842  }
843  }
844 }
845 
847 {
848  SwDoc& rDoc = rContext.GetDoc();
849 
850  SwNodeOffset nCalcStt = m_nSttNode - m_nNdDiff;
851 
852  if( m_nSectDiff && m_bBackSp )
853  nCalcStt += m_nSectDiff;
854 
855  SwNodeIndex aIdx(rDoc.GetNodes(), nCalcStt);
856  SwNode* pInsNd = &aIdx.GetNode();
857  SwNode* pMovedNode = nullptr;
858 
859  { // code block so that SwPosition is detached when deleting a Node
860  SwPosition aPos( aIdx );
861  if( !m_bDelFullPara )
862  {
863  assert(!m_bTableDelLastNd || pInsNd->IsTextNode());
864  if( pInsNd->IsTableNode() )
865  {
866  pInsNd = rDoc.GetNodes().MakeTextNode( aIdx,
867  rDoc.GetDfltTextFormatColl() );
868  --aIdx;
869  aPos.nNode = aIdx;
870  aPos.nContent.Assign( pInsNd->GetContentNode(), m_nSttContent );
871  }
872  else
873  {
874  if( pInsNd->IsContentNode() )
875  aPos.nContent.Assign( static_cast<SwContentNode*>(pInsNd), m_nSttContent );
876  if( !m_bTableDelLastNd )
877  pInsNd = nullptr; // do not delete Node!
878  }
879  }
880  else
881  pInsNd = nullptr; // do not delete Node!
882 
883  bool bNodeMove = SwNodeOffset(0) != m_nNode;
884 
885  if( m_aEndStr )
886  {
887  // discard attributes since they all saved!
888  SwTextNode * pTextNd;
889  if (!m_bDelFullPara && aPos.nNode.GetNode().IsSectionNode())
890  { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below
891  assert(m_nSttContent == 0);
892  assert(!m_aSttStr);
893  pTextNd = rDoc.GetNodes()[aPos.nNode.GetIndex() + 1]->GetTextNode();
894  }
895  else
896  {
897  pTextNd = aPos.nNode.GetNode().GetTextNode();
898  }
899 
900  if( pTextNd && pTextNd->HasSwAttrSet() )
901  pTextNd->ResetAllAttr();
902 
903  if( pTextNd && pTextNd->GetpSwpHints() )
904  pTextNd->ClearSwpHintsArr( true );
905 
906  if( m_aSttStr && !m_bFromTableCopy )
907  {
908  SwNodeOffset nOldIdx = aPos.nNode.GetIndex();
909  rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
910  // After the split all objects are anchored at the first
911  // paragraph, but the pHistory of the fly frame formats relies
912  // on anchoring at the start of the selection
913  // => selection backwards needs a correction.
914  if( m_bBackSp )
915  lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
916  pTextNd = aPos.nNode.GetNode().GetTextNode();
917  }
918  assert(pTextNd); // else where does m_aEndStr come from?
919  if( pTextNd )
920  {
921  OUString const ins( pTextNd->InsertText(*m_aEndStr, aPos.nContent,
923  assert(ins.getLength() == m_aEndStr->getLength()); // must succeed
924  (void) ins;
925  // METADATA: restore
927  }
928  }
929  else if (m_aSttStr && bNodeMove && pInsNd == nullptr)
930  {
931  SwTextNode * pNd = aPos.nNode.GetNode().GetTextNode();
932  if( pNd )
933  {
934  if (m_nSttContent < pNd->GetText().getLength())
935  {
936  SwNodeOffset nOldIdx = aPos.nNode.GetIndex();
937  rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
938  if( m_bBackSp )
939  lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
940  }
941  else
942  ++aPos.nNode;
943  }
944  }
945  if( m_nSectDiff )
946  {
947  SwNodeOffset nMoveIndex = aPos.nNode.GetIndex();
948  SwNodeOffset nDiff(0);
949  if( m_bJoinNext )
950  {
951  nMoveIndex += m_nSectDiff + 1;
952  pMovedNode = &aPos.nNode.GetNode();
953  }
954  else
955  {
956  nMoveIndex -= m_nSectDiff + 1;
957  ++nDiff;
958  }
959  SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
960  SwNodeRange aRg( aPos.nNode, SwNodeOffset(0) - nDiff, aPos.nNode, SwNodeOffset(1) - nDiff );
961  --aPos.nNode;
962  if( !m_bJoinNext )
963  pMovedNode = &aPos.nNode.GetNode();
964  rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx);
965  ++aPos.nNode;
966  }
967 
968  if( bNodeMove )
969  {
971  SwNodeIndex aCopyIndex( aPos.nNode, -1 );
972  rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.nNode,
973  // sw_redlinehide: delay creating frames: the flags on the
974  // nodes aren't necessarily up-to-date, and the redlines
975  // from m_pRedlSaveData aren't applied yet...
976  false);
977 
978  if( m_nReplaceDummy )
979  {
980  SwNodeOffset nMoveIndex;
981  if( m_bJoinNext )
982  {
983  nMoveIndex = m_nEndNode - m_nNdDiff;
984  aPos.nNode = nMoveIndex + m_nReplaceDummy;
985  }
986  else
987  {
988  aPos = SwPosition( aCopyIndex );
989  nMoveIndex = aPos.nNode.GetIndex() + m_nReplaceDummy + 1;
990  }
991  SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
992  SwNodeRange aRg( aPos.nNode, SwNodeOffset(0), aPos.nNode, SwNodeOffset(1) );
993  pMovedNode = &aPos.nNode.GetNode();
994  // tdf#131684 without deleting frames
995  rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx, false);
996  rDoc.GetNodes().Delete( aMvIdx);
997  }
998  }
999 
1000  if( m_aSttStr )
1001  {
1002  aPos.nNode = m_nSttNode - m_nNdDiff + ( m_bJoinNext ? SwNodeOffset(0) : m_nReplaceDummy );
1003  SwTextNode * pTextNd = aPos.nNode.GetNode().GetTextNode();
1004  // If more than a single Node got deleted, also all "Node"
1005  // attributes were saved
1006  if (pTextNd != nullptr)
1007  {
1008  if( pTextNd->HasSwAttrSet() && bNodeMove && !m_aEndStr )
1009  pTextNd->ResetAllAttr();
1010 
1011  if( pTextNd->GetpSwpHints() )
1012  pTextNd->ClearSwpHintsArr( true );
1013 
1014  // SectionNode mode and selection from top to bottom:
1015  // -> in StartNode is still the rest of the Join => delete
1016  aPos.nContent.Assign( pTextNd, m_nSttContent );
1017  pTextNd->SetInSwUndo(true);
1018  OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent,
1020  pTextNd->SetInSwUndo(false);
1021  assert(ins.getLength() == m_aSttStr->getLength()); // must succeed
1022  (void) ins;
1023  // METADATA: restore
1024  pTextNd->RestoreMetadata(m_pMetadataUndoStart);
1025  }
1026  }
1027 
1028  if( m_pHistory )
1029  {
1030  m_pHistory->TmpRollback(&rDoc, m_nSetPos, false);
1031  if( m_nSetPos ) // there were Footnodes/FlyFrames
1032  {
1033  // are there others than these ones?
1034  if( m_nSetPos < m_pHistory->Count() )
1035  {
1036  // if so save the attributes of the others
1037  SwHistory aHstr;
1038  aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
1039  m_pHistory->Rollback(&rDoc);
1040  m_pHistory->Move( 0, &aHstr );
1041  }
1042  else
1043  {
1044  m_pHistory->Rollback(&rDoc);
1045  m_pHistory.reset();
1046  }
1047  }
1048  }
1049 
1050  if( m_bResetPgDesc || m_bResetPgBrk )
1051  {
1052  sal_uInt16 nStt = m_bResetPgDesc ? sal_uInt16(RES_PAGEDESC) : sal_uInt16(RES_BREAK);
1053  sal_uInt16 nEnd = m_bResetPgBrk ? sal_uInt16(RES_BREAK) : sal_uInt16(RES_PAGEDESC);
1054 
1055  SwNode* pNode = rDoc.GetNodes()[ m_nEndNode + 1 ];
1056  if( pNode->IsContentNode() )
1057  static_cast<SwContentNode*>(pNode)->ResetAttr( nStt, nEnd );
1058  else if( pNode->IsTableNode() )
1059  static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt, nEnd );
1060  }
1061  }
1062  // delete the temporarily added Node
1063  if (pInsNd && !m_bTableDelLastNd)
1064  {
1065  assert(&aIdx.GetNode() == pInsNd);
1066  rDoc.GetNodes().Delete( aIdx );
1067  }
1068  if( m_pRedlSaveData )
1069  SetSaveData(rDoc, *m_pRedlSaveData);
1070 
1071  SwNodeOffset delFullParaEndNode(m_nEndNode);
1073  {
1074  SwTextNode * pFirstMergedDeletedTextNode(nullptr);
1075  SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, *this,
1076  *m_pRedlSaveData, pFirstMergedDeletedTextNode);
1077  if (pNextNode)
1078  {
1079  bool bNonMerged(false);
1080  std::vector<SwTextFrame*> frames;
1082  for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
1083  {
1084  if (pFrame->getRootFrame()->HasMergedParas())
1085  {
1086  frames.push_back(pFrame);
1087  }
1088  else
1089  {
1090  bNonMerged = true;
1091  }
1092  }
1093  for (SwTextFrame *const pFrame : frames)
1094  {
1095  // could either destroy the text frames, or move them...
1096  // destroying them would have the advantage that we don't
1097  // need special code to *exclude* pFirstMergedDeletedTextNode
1098  // from MakeFrames for the layouts in Hide mode but not
1099  // layouts in Show mode ...
1100  // ... except that MakeFrames won't create them then :(
1101  pFrame->RegisterToNode(*pFirstMergedDeletedTextNode);
1102  assert(pFrame->GetMergedPara());
1103  assert(!bNonMerged); // delFullParaEndNode is such an awful hack
1104  (void) bNonMerged;
1105  delFullParaEndNode = pFirstMergedDeletedTextNode->GetIndex();
1106  }
1107  }
1108  }
1109  else if (m_aSttStr && (!m_bFromTableCopy || SwNodeOffset(0) != m_nNode))
1110  {
1111  // only now do we have redlines in the document again; fix up the split
1112  // frames
1113  SwTextNode *const pStartNode(aIdx.GetNodes()[m_nSttNode]->GetTextNode());
1114  assert(pStartNode);
1115  sw::RecreateStartTextFrames(*pStartNode);
1116  }
1117 
1118  // create frames after SetSaveData has recreated redlines
1119  if (SwNodeOffset(0) != m_nNode)
1120  {
1121  // tdf#136453 only if section nodes at the start
1122  if (m_bBackSp && m_nReplaceDummy != SwNodeOffset(0))
1123  {
1124  // tdf#134252 *first* create outer section frames
1125  // note: text node m_nSttNode currently has frame with an upper;
1126  // there's a hack in InsertCnt_() to move it below new section frame
1128  SwNodeIndex const end(rDoc.GetNodes(), m_nSttNode); // exclude m_nSttNode
1129  ::MakeFrames(&rDoc, start, end);
1130  }
1131  // tdf#121031 if the start node is a text node, it already has a frame;
1132  // if it's a table, it does not
1133  // tdf#109376 exception: end on non-text-node -> start node was inserted
1134  assert(!m_bDelFullPara || (m_nSectDiff == SwNodeOffset(0)));
1135  SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode +
1136  ((m_bDelFullPara || !rDoc.GetNodes()[m_nSttNode]->IsTextNode() || pInsNd)
1137  ? 0 : 1));
1138  // don't include end node in the range: it may have been merged already
1139  // by the start node, or it may be merged by one of the moved nodes,
1140  // but if it isn't merged, its current frame(s) should be good...
1141  SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara
1142  ? delFullParaEndNode
1143  // tdf#147310 SwDoc::DeleteRowCol() may delete whole table - end must be node following table!
1144  : (m_nEndNode + (rDoc.GetNodes()[m_nSttNode]->IsTableNode() && rDoc.GetNodes()[m_nEndNode]->IsEndNode() ? 1 : 0)));
1145  ::MakeFrames(&rDoc, start, end);
1146  }
1147 
1148  if (pMovedNode)
1149  { // probably better do this after creating all frames
1150  lcl_MakeAutoFrames(*rDoc.GetSpzFrameFormats(), pMovedNode->GetIndex());
1151  }
1152 
1153  // tdf#134021 only after MakeFrames(), because it may be the only node
1154  // that has layout frames
1155  if (pInsNd && m_bTableDelLastNd)
1156  {
1157  assert(&aIdx.GetNode() == pInsNd);
1158  SwPaM tmp(aIdx, aIdx);
1160  }
1161 
1162  AddUndoRedoPaM(rContext, true);
1163 }
1164 
1166 {
1167  SwPaM & rPam = AddUndoRedoPaM(rContext);
1168  SwDoc& rDoc = rPam.GetDoc();
1169 
1170  if( m_pRedlSaveData )
1171  {
1172  const bool bSuccess = FillSaveData(rPam, *m_pRedlSaveData);
1173  OSL_ENSURE(bSuccess,
1174  "SwUndoDelete::Redo: used to have redline data, but now none?");
1175  if (!bSuccess)
1176  {
1177  m_pRedlSaveData.reset();
1178  }
1179  }
1180 
1181  if( !m_bDelFullPara )
1182  {
1183  // tdf#128739 correct cursors but do not delete bookmarks yet
1184  ::PaMCorrAbs(rPam, *rPam.End());
1185  SetPaM(rPam);
1186 
1187  if( !m_bJoinNext ) // then restore selection from bottom to top
1188  rPam.Exchange();
1189  }
1190 
1191  if( m_pHistory ) // are the attributes saved?
1192  {
1193  m_pHistory->SetTmpEnd( m_pHistory->Count() );
1194  SwHistory aHstr;
1195  aHstr.Move( 0, m_pHistory.get() );
1196 
1197  if( m_bDelFullPara )
1198  {
1199  OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
1200  DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
1202 
1203  DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
1204  }
1205  else
1206  DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
1207  m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
1208 
1209  m_pHistory->Move( m_nSetPos, &aHstr );
1210  }
1211  else
1212  {
1213  if( m_bDelFullPara )
1214  {
1215  OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
1216  DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
1218 
1219  DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode );
1220  }
1221  else
1222  DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
1223  m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
1224  }
1225 
1226  if( !m_aSttStr && !m_aEndStr )
1227  {
1229  {
1230  DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
1231  }
1232 
1233  SwNodeIndex aSttIdx = ( m_bDelFullPara || m_bJoinNext )
1234  ? rPam.GetMark()->nNode
1235  : rPam.GetPoint()->nNode;
1236  SwTableNode* pTableNd = aSttIdx.GetNode().GetTableNode();
1237  if( pTableNd )
1238  {
1239  if( m_bTableDelLastNd )
1240  {
1241  // than add again a Node at the end
1242  const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
1243  rDoc.GetNodes().MakeTextNode( aTmpIdx,
1245  }
1246 
1247  SwContentNode* pNextNd = rDoc.GetNodes()[
1248  pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
1249  if( pNextNd )
1250  {
1251  SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
1252 
1253  if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
1254  false ) )
1255  pNextNd->SetAttr( *pItem );
1256 
1257  if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
1258  false ) )
1259  pNextNd->SetAttr( *pItem );
1260  }
1261  pTableNd->DelFrames();
1262  }
1263  else if (*rPam.GetMark() == *rPam.GetPoint())
1264  { // paragraph with only footnote or as-char fly, delete that
1265  // => DelContentIndex has already deleted it! nothing to do here
1266  assert(m_nEndNode == m_nSttNode);
1267  return;
1268  }
1269 
1270  // avoid asserts from ~SwIndexReg for deleted nodes
1271  SwPaM aTmp(*rPam.End());
1272  if (!aTmp.Move(fnMoveForward, GoInNode))
1273  {
1274  *aTmp.GetPoint() = *rPam.Start();
1275  aTmp.Move(fnMoveBackward, GoInNode);
1276  }
1277  assert(aTmp.GetPoint()->nNode != rPam.GetPoint()->nNode
1278  && aTmp.GetPoint()->nNode != rPam.GetMark()->nNode);
1279  ::PaMCorrAbs(rPam, *aTmp.GetPoint());
1280 
1281  rPam.DeleteMark();
1282 
1283  rDoc.GetNodes().Delete( aSttIdx, m_nEndNode - m_nSttNode );
1284  }
1285  else if( m_bDelFullPara )
1286  {
1287  assert(!"dead code");
1288  // The Pam was incremented by one at Point (== end) to provide space
1289  // for UNDO. This now needs to be reverted!
1290  --rPam.End()->nNode;
1291  if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
1292  *rPam.GetMark() = *rPam.GetPoint();
1294  }
1295  else
1297 }
1298 
1300 {
1301  // this action does not seem idempotent,
1302  // so make sure it is only executed once on repeat
1303  if (rContext.m_bDeleteRepeated)
1304  return;
1305 
1306  SwPaM & rPam = rContext.GetRepeatPaM();
1307  SwDoc& rDoc = rPam.GetDoc();
1308  ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1309  if( !rPam.HasMark() )
1310  {
1311  rPam.SetMark();
1312  rPam.Move( fnMoveForward, GoInContent );
1313  }
1314  if( m_bDelFullPara )
1316  else
1318  rContext.m_bDeleteRepeated = true;
1319 }
1320 
1321 void SwUndoDelete::SetTableName(const OUString & rName)
1322 {
1323  m_sTableName = rName;
1324 }
1325 
1327 {
1328  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDelete"));
1329  (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1330  SwUndo::dumpAsXml(pWriter);
1332  (void)xmlTextWriterEndElement(pWriter);
1333 }
1334 
1335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const SwEndNode * EndOfSectionNode() const
Definition: node.hxx:692
SwPaM & AddUndoRedoPaM(::sw::UndoRedoContext &, bool const bCorrToContent=false) const
Definition: undobj.cxx:108
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:162
constexpr TypedWhichId< SwFormatPageDesc > RES_PAGEDESC(93)
void DeleteMark()
Definition: pam.hxx:178
SwNode & GetNode(bool bPoint=true) const
Definition: pam.hxx:224
bool GoInContent(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:968
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:687
static OUString GetPlaceHolder(SwUndoArg eId)
Definition: SwRewriter.cxx:51
Marks a position in the document model.
Definition: pam.hxx:36
void DelBookmarks(const SwNodeIndex &rStt, const SwNodeIndex &rEnd, std::vector< SaveBookmark > *pSaveBkmk, const SwIndex *pSttIdx, const SwIndex *pEndIdx)
Definition: docbm.cxx:1900
bool IsSectionNode() const
Definition: node.hxx:654
Pagedescriptor Client of SwPageDesc that is "described" by the attribute.
Definition: fmtpdsc.hxx:35
std::unique_ptr< SwHistory > m_pHistory
Definition: undobj.hxx:166
SwNodes const & GetUndoNodes() const
Definition: docundo.cxx:77
virtual void RedoImpl(::sw::UndoRedoContext &) override
Definition: undel.cxx:1165
void DelFrames(SwRootFrame const *pLayout=nullptr)
Method deletes all views of document for the node.
Definition: ndtbl.cxx:2437
const OUString & GetText() const
Definition: ndtxt.hxx:218
SwUndoId
Definition: swundo.hxx:29
virtual sal_uInt16 ResetAllAttr() override
Definition: ndtxt.cxx:5148
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:226
std::unique_ptr< SwNodeIndex > m_pMvStt
Definition: UndoDelete.hxx:41
size_t size() const
Definition: UndoCore.hxx:77
SwNodeIndex nNode
Definition: pam.hxx:38
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:277
SwPaM & GetRepeatPaM()
Definition: UndoCore.hxx:134
const SwPosition * GetMark() const
Definition: pam.hxx:210
sal_Int64 n
void dumpAsXml(xmlTextWriterPtr pWriter) const override
Definition: undel.cxx:1326
bool SaveContent(const SwPosition *pStt, const SwPosition *pEnd, SwTextNode *pSttTextNd, SwTextNode *pEndTextNd)
Definition: undel.cxx:440
Definition: doc.hxx:187
static void lcl_MakeAutoFrames(const SwFrameFormats &rSpzArr, SwNodeOffset nMovedIndex)
Definition: undel.cxx:57
TElementType * Next()
Definition: calbck.hxx:364
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:128
#define CH_TXTATR_NEWLINE
Definition: hintids.hxx:176
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:144
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:180
void Copy_(const SwNodeRange &rRg, const SwNodeIndex &rInsPos, bool bNewFrames=true) const
Definition: ndarr.hxx:175
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:204
const SwFrameFormats * GetSpzFrameFormats() const
Definition: doc.hxx:743
static SwTextNode * FindFirstAndNextNode(SwDoc &rDoc, SwUndRng const &rRange, SwRedlineSaveDatas const &rRedlineSaveData, SwTextNode *&o_rpFirstMergedDeletedTextNode)
Definition: undel.cxx:72
virtual SwRewriter GetRewriter() const override
Returns rewriter for this undo object.
Definition: undel.cxx:760
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const
Definition: undobj.cxx:710
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:164
std::shared_ptr< MetadatableUndo > CreateUndoForDelete()
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:173
const T * GetItemIfSet(TypedWhichId< T > nWhich, bool bSrchInParent=true) const
Templatized version of GetItemState() to directly return the correct type.
Definition: format.hxx:111
SwNodeOffset m_nNdDiff
Definition: UndoDelete.hxx:50
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:174
SwTableNode * GetTableNode()
Definition: node.hxx:609
SwUndoDelete(SwPaM &, bool bFullPara=false, bool bCalledByTableCpy=false)
Definition: undel.cxx:176
SwIndex nContent
Definition: pam.hxx:39
bool empty() const
Definition: docary.hxx:266
SwNodeIndex aStart
Definition: ndindex.hxx:141
IDocumentStylePoolAccess const & getIDocumentStylePoolAccess() const
Definition: doc.cxx:426
const SwTextFormatColl * GetDfltTextFormatColl() const
Definition: doc.hxx:775
#define CH_TXT_ATR_INPUTFIELDSTART
Definition: hintids.hxx:177
int nCount
const SwTable & GetTable() const
Definition: node.hxx:506
static bool CanRedlineGroup(SwRedlineSaveDatas &rCurr, const SwRedlineSaveDatas &rCheck, bool bCurrIsEnd)
Definition: undobj.cxx:1502
bool empty() const
bool m_bFromTableCopy
Definition: UndoDelete.hxx:62
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:161
SwDoc & GetDoc() const
Definition: UndoCore.hxx:95
virtual ~SwUndoDelete() override
Definition: undel.cxx:601
void MakeFrames(SwDoc *pDoc, const SwNodeIndex &rSttIdx, const SwNodeIndex &rEndIdx)
Definition: frmtool.cxx:2014
Specific frame formats (frames, DrawObjects).
SwNode & GetEndOfContent() const
Regular ContentSection (i.e. the BodyText).
Definition: ndarr.hxx:162
virtual void RepeatImpl(::sw::RepeatContext &) override
Definition: undel.cxx:1299
void SetTableName(const OUString &rName)
Definition: undel.cxx:1321
bool IsContentNode() const
Definition: node.hxx:638
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:137
const SwAttrSet * GetpSwAttrSet() const
Definition: node.hxx:457
bool Move(SwMoveFnCollection const &fnMove=fnMoveForward, SwGoInDoc fnGo=GoInContent)
Movement of cursor.
Definition: pam.cxx:502
Style of a layout element.
Definition: frmfmt.hxx:59
::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:959
const SwStartNode * StartOfSectionNode() const
Definition: node.hxx:133
const SwPosition * GetPoint() const
Definition: pam.hxx:208
void DelContentIndex(const SwPosition &pMark, const SwPosition &pPoint, DelContentType nDelContentType=DelContentType::AllMask)
Definition: undobj.cxx:865
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:39
const SwPosition * GetContentAnchor() const
Definition: fmtanchr.hxx:67
Count
static void SetSaveData(SwDoc &rDoc, SwRedlineSaveDatas &rSData)
Definition: undobj.cxx:1474
static SwRewriter lcl_RewriterFromHistory(SwHistory &rHistory)
Definition: undel.cxx:612
void Exchange()
Definition: pam.cxx:490
TElementType * First()
Definition: calbck.hxx:356
virtual bool DeleteRedline(const SwPaM &rPam, bool bSaveInUndo, RedlineType nDelType)=0
bool m_bTableDelLastNd
Definition: UndoDelete.hxx:58
SwNodeOffset GetIndex() const
Definition: node.hxx:292
FlyAnchors.
Definition: fmtanchr.hxx:34
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:206
static void lcl_ReAnchorAtContentFlyFrames(const SwFrameFormats &rSpzArr, const SwPosition &rPos, SwNodeOffset nOldIdx)
Definition: undel.cxx:821
sal_Int32 m_nEndContent
Definition: undobj.hxx:231
static void DelFullParaMoveFrames(SwDoc &rDoc, SwUndRng const &rRange, SwRedlineSaveDatas const &rRedlineSaveData)
Definition: undel.cxx:121
Marks a character position inside a document model node.
Definition: index.hxx:33
#define CH_TXT_ATR_FIELDSTART
Definition: hintids.hxx:182
void AddRule(SwUndoArg eWhat, const OUString &rWith)
Definition: SwRewriter.cxx:25
std::shared_ptr< MetadatableUndo > CreateUndo() const
Marks a node in the document model.
Definition: ndindex.hxx:30
bool m_bCacheComment
Definition: undobj.hxx:61
bool HasSwAttrSet() const
Definition: node.hxx:458
enumrange< T >::Iterator end(enumrange< T >)
const SwPosition * Start() const
Definition: pam.hxx:213
SwNodeOffset m_nEndNode
Definition: undobj.hxx:230
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const
virtual bool IsIgnoreRedline() const =0
sal_uInt16 Count() const
Definition: rolbck.hxx:378
virtual bool SetFormatAttr(const SfxPoolItem &rAttr)
Definition: format.cxx:448
static OUString lcl_DenotedPortion(std::u16string_view rStr, sal_Int32 nStart, sal_Int32 nEnd, bool bQuoted)
Definition: undel.cxx:662
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:2689
OUString InsertText(const OUString &rStr, const SwIndex &rIdx, const SwInsertFlags nMode=SwInsertFlags::DEFAULT)
insert text content
Definition: ndtxt.cxx:2297
void ClearSwpHintsArr(bool bDelFields)
Definition: thints.cxx:3396
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
#define CH_TXT_ATR_INPUTFIELDEND
Definition: hintids.hxx:178
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:1572
void SetPaM(SwPaM &, bool bCorrToContent=false) const
Definition: undobj.cxx:80
DelContentType
Definition: undobj.hxx:133
void RecreateStartTextFrames(SwTextNode &rNode)
Definition: frmtool.cxx:1430
SwNodeOffset m_nSectDiff
Definition: UndoDelete.hxx:51
OUString ShortenString(const OUString &rStr, sal_Int32 nLength, const OUString &rFillStr)
Shortens a string to a maximum length.
Definition: undobj.cxx:1534
Blocks grouping undo actions together into an SfxListUndoAction.
bool CanGrouping(SwDoc &, const SwPaM &)
Definition: undel.cxx:519
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:59
virtual bool DelFullPara(SwPaM &)=0
Delete full paragraphs.
sal_Int32 GetIndex() const
Definition: index.hxx:91
virtual void MakeFrames()
Creates the views.
Definition: atrfrm.cxx:2705
SwNodes & GetNodes()
Definition: doc.hxx:408
virtual SwTextFormatColl * GetTextCollFromPool(sal_uInt16 nId, bool bRegardLanguage=true)=0
Return "Auto-Collection with ID.
const SwPosition * End() const
Definition: pam.hxx:218
SwNodeIndex aEnd
Definition: ndindex.hxx:142
#define CH_TXTATR_TAB
Definition: hintids.hxx:175
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:86
void Delete(const SwNodeIndex &rPos, SwNodeOffset nNodes=SwNodeOffset(1))
delete nodes
Definition: nodes.cxx:1085
o3tl::strong_int< sal_Int32, struct Tag_SwNodeOffset > SwNodeOffset
Definition: nodeoffset.hxx:16
double getLength(const B2DPolygon &rCandidate)
static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
Definition: undel.cxx:639
#define CH_TXT_ATR_FIELDEND
Definition: hintids.hxx:184
SwMoveFnCollection const & fnMoveBackward
Definition: paminit.cxx:58
SwNodeOffset Count() const
Definition: ndarr.hxx:141
virtual bool SplitNode(const SwPosition &rPos, bool bChkTableStart)=0
Split a node at rPos (implemented only for TextNode).
bool IsTableNode() const
Definition: node.hxx:650
SwNodeOffset m_nReplaceDummy
Definition: UndoDelete.hxx:52
struct _xmlTextWriter * xmlTextWriterPtr
size_t size() const
SwDoc & GetDoc() const
Definition: pam.hxx:244
bool IsFlySelectedByCursor(SwDoc const &rDoc, SwPosition const &rStart, SwPosition const &rEnd)
check at-char and at-para flys in rDoc
Definition: undobj.cxx:1685
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:476
static bool FillSaveData(const SwPaM &rRange, SwRedlineSaveDatas &rSData, bool bDelRange=true, bool bCopyNext=true)
Definition: undobj.cxx:1409
OUString DenoteSpecialCharacters(const OUString &rStr, bool bQuoted)
Denotes special characters in a string.
Definition: undel.cxx:717
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:183
virtual bool DeleteAndJoin(SwPaM &)=0
complete delete of a given PaM
std::optional< OUString > m_aSttStr
Definition: UndoDelete.hxx:42
CharClass & GetAppCharClass()
Definition: init.cxx:705
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)
SwNodeOffset m_nNode
Definition: UndoDelete.hxx:49
sal_Int32 m_nSttContent
Definition: undobj.hxx:231
virtual void UndoImpl(::sw::UndoRedoContext &) override
Definition: undel.cxx:846
const int nUndoStringLength
Definition: UndoCore.hxx:243
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:864
SwTextNode * MakeTextNode(const SwNodeIndex &rWhere, SwTextFormatColl *pColl, bool bNewFrames=true)
Implementations of "Make...Node" are in the given .cxx-files.
Definition: ndtxt.cxx:104
void SetAnchor(const SwPosition *pPos)
Definition: atrfrm.cxx:1585
bool MoveNodes(const SwNodeRange &, SwNodes &rNodes, const SwNodeIndex &, bool bNewFrames=true)
move the node pointer
Definition: nodes.cxx:409
bool m_bDetectedRangeSegmentation false
Base class of the Writer document model elements.
Definition: node.hxx:81
SwTextFormatColl * GetTextColl() const
Definition: ndtxt.hxx:858
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo
SwNodeOffset m_nSttNode
Definition: undobj.hxx:230