summaryrefslogtreecommitdiffstats
path: root/sw/source/core/undo/undel.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/undo/undel.cxx')
-rw-r--r--sw/source/core/undo/undel.cxx1317
1 files changed, 1317 insertions, 0 deletions
diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx
new file mode 100644
index 000000000..e1e519fa2
--- /dev/null
+++ b/sw/source/core/undo/undel.cxx
@@ -0,0 +1,1317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoDelete.hxx>
+#include <hintids.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <unotools/charclass.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <doc.hxx>
+#include <UndoManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <poolfmt.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <frmtool.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <vector>
+
+// DELETE
+/* lcl_MakeAutoFrames has to call MakeFrames for objects bounded "AtChar"
+ ( == AUTO ), if the anchor frame has be moved via MoveNodes(..) and
+ DelFrames(..)
+*/
+static void lcl_MakeAutoFrames( const SwFrameFormats& rSpzArr, sal_uLong nMovedIndex )
+{
+ for( size_t n = 0; n < rSpzArr.size(); ++n )
+ {
+ SwFrameFormat * pFormat = rSpzArr[n];
+ const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ const SwPosition* pAPos = pAnchor->GetContentAnchor();
+ if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() )
+ pFormat->MakeFrames();
+ }
+ }
+}
+
+static SwTextNode * FindFirstAndNextNode(SwDoc & rDoc, SwUndRng const& rRange,
+ SwRedlineSaveDatas const& rRedlineSaveData,
+ SwTextNode *& o_rpFirstMergedDeletedTextNode)
+{
+ // redlines are corrected now to exclude the deleted node
+ assert(rRange.m_nEndContent == 0);
+ sal_uLong nEndOfRedline = 0;
+ for (size_t i = 0; i < rRedlineSaveData.size(); ++i)
+ {
+ auto const& rRedline(rRedlineSaveData[i]);
+ if (rRedline.m_nSttNode <= rRange.m_nSttNode
+ && rRedline.m_nSttNode < rRange.m_nEndNode
+ && rRange.m_nEndNode <= rRedline.m_nEndNode
+ && rRedline.GetType() == RedlineType::Delete)
+ {
+ nEndOfRedline = rRedline.m_nEndNode;
+ o_rpFirstMergedDeletedTextNode = rDoc.GetNodes()[rRedline.m_nSttNode]->GetTextNode();
+ assert(rRange.m_nSttNode == rRange.m_nEndNode - 1); // otherwise this needs to iterate more RL to find the first node?
+ break;
+ }
+ }
+ if (nEndOfRedline)
+ {
+ assert(o_rpFirstMergedDeletedTextNode);
+ SwTextNode * pNextNode(nullptr);
+ for (sal_uLong i = rRange.m_nEndNode; /* i <= nEndOfRedline */; ++i)
+ {
+ SwNode *const pNode(rDoc.GetNodes()[i]);
+ assert(!pNode->IsEndNode()); // cannot be both leaving section here *and* overlapping redline
+ if (pNode->IsStartNode())
+ {
+ i = pNode->EndOfSectionIndex(); // will be incremented again
+ }
+ else if (pNode->IsTextNode())
+ {
+ pNextNode = pNode->GetTextNode();
+ break;
+ }
+ }
+ assert(pNextNode);
+ return pNextNode;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+static void DelFullParaMoveFrames(SwDoc & rDoc, SwUndRng const& rRange,
+ SwRedlineSaveDatas const& rRedlineSaveData)
+{
+ SwTextNode * pFirstMergedDeletedTextNode(nullptr);
+ SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, rRange,
+ rRedlineSaveData, pFirstMergedDeletedTextNode);
+ if (pNextNode)
+ {
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ assert(pFrame->GetMergedPara());
+ assert(pFrame->GetMergedPara()->pFirstNode == pFirstMergedDeletedTextNode);
+ assert(pNextNode->GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
+ frames.push_back(pFrame);
+ }
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ // sw_redlinehide: don't need FrameMode::Existing here
+ // because everything from pNextNode onwards is already
+ // correctly hidden
+ pFrame->RegisterToNode(*pNextNode, true);
+ }
+ }
+}
+
+// SwUndoDelete has to perform a deletion and to record anything that is needed
+// to restore the situation before the deletion. Unfortunately a part of the
+// deletion will be done after calling this Ctor, this has to be kept in mind!
+// In this Ctor only the complete paragraphs will be deleted, the joining of
+// the first and last paragraph of the selection will be handled outside this
+// function.
+// Here are the main steps of the function:
+// 1. Deletion/recording of content indices of the selection: footnotes, fly
+// frames and bookmarks
+// Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
+// 2. If the paragraph where the selection ends, is the last content of a
+// section so that this section becomes empty when the paragraphs will be
+// joined we have to do some smart actions ;-) The paragraph will be moved
+// outside the section and replaced by a dummy text node, the complete
+// section will be deleted in step 3. The difference between replacement
+// dummy and original is nReplacementDummy.
+// 3. Moving complete selected nodes into the UndoArray. Before this happens the
+// selection has to be extended if there are sections which would become
+// empty otherwise. BTW: sections will be moved into the UndoArray if they
+// are complete part of the selection. Sections starting or ending outside
+// of the selection will not be removed from the DocNodeArray even they got
+// a "dummy"-copy in the UndoArray.
+// 4. We have to anticipate the joining of the two paragraphs if the start
+// paragraph is inside a section and the end paragraph not. Then we have to
+// move the paragraph into this section and to record this in nSectDiff.
+SwUndoDelete::SwUndoDelete(
+ SwPaM& rPam,
+ bool bFullPara,
+ bool bCalledByTableCpy )
+ : SwUndo(SwUndoId::DELETE, rPam.GetDoc()),
+ SwUndRng( rPam ),
+ m_nNode(0),
+ m_nNdDiff(0),
+ m_nSectDiff(0),
+ m_nReplaceDummy(0),
+ m_nSetPos(0),
+ m_bGroup( false ),
+ m_bBackSp( false ),
+ m_bJoinNext( false ),
+ m_bTableDelLastNd( false ),
+ // bFullPara is set e.g. if an empty paragraph before a table is deleted
+ m_bDelFullPara( bFullPara ),
+ m_bResetPgDesc( false ),
+ m_bResetPgBrk( false ),
+ m_bFromTableCopy( bCalledByTableCpy )
+{
+
+ m_bCacheComment = false;
+
+ SwDoc * pDoc = rPam.GetDoc();
+
+ if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ m_pRedlSaveData.reset(new SwRedlineSaveDatas);
+ if( !FillSaveData( rPam, *m_pRedlSaveData ))
+ {
+ m_pRedlSaveData.reset();
+ }
+ }
+
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+
+ // delete all footnotes for now
+ const SwPosition *pStt = rPam.Start(),
+ *pEnd = rPam.GetPoint() == pStt
+ ? rPam.GetMark()
+ : rPam.GetPoint();
+
+ // Step 1. deletion/record of content indices
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ DelBookmarks(pStt->nNode, pEnd->nNode);
+ }
+ else
+ {
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ if (m_nEndNode - m_nSttNode > 1) // check for fully selected nodes
+ {
+ SwNodeIndex const start(pStt->nNode, +1);
+ DelBookmarks(start, pEnd->nNode);
+ }
+ }
+
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ // Is already anything deleted?
+ m_nNdDiff = m_nSttNode - pStt->nNode.GetIndex();
+
+ m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
+ m_bBackSp = !bFullPara && !m_bJoinNext;
+
+ SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr;
+ if( !bFullPara )
+ {
+ pSttTextNd = pStt->nNode.GetNode().GetTextNode();
+ pEndTextNd = m_nSttNode == m_nEndNode
+ ? pSttTextNd
+ : pEnd->nNode.GetNode().GetTextNode();
+ }
+ else if (m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(*pDoc, *this, *m_pRedlSaveData);
+ }
+
+ bool bMoveNds = *pStt != *pEnd // any area still existent?
+ && ( SaveContent( pStt, pEnd, pSttTextNd, pEndTextNd ) || m_bFromTableCopy );
+
+ if( pSttTextNd && pEndTextNd && pSttTextNd != pEndTextNd )
+ {
+ // two different TextNodes, thus save also the TextFormatCollection
+ m_pHistory->Add( pSttTextNd->GetTextColl(),pStt->nNode.GetIndex(), SwNodeType::Text );
+ m_pHistory->Add( pEndTextNd->GetTextColl(),pEnd->nNode.GetIndex(), SwNodeType::Text );
+
+ if( !m_bJoinNext ) // Selection from bottom to top
+ {
+ // When using JoinPrev() all AUTO-PageBreak's will be copied
+ // correctly. To restore them with UNDO, Auto-PageBreak of the
+ // EndNode needs to be reset. Same for PageDesc and ColBreak.
+ if( pEndTextNd->HasSwAttrSet() )
+ {
+ SwRegHistory aRegHist( *pEndTextNd, m_pHistory.get() );
+ if( SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
+ RES_BREAK, false ) )
+ pEndTextNd->ResetAttr( RES_BREAK );
+ if( pEndTextNd->HasSwAttrSet() &&
+ SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
+ RES_PAGEDESC, false ) )
+ pEndTextNd->ResetAttr( RES_PAGEDESC );
+ }
+ }
+ }
+
+ // Move now also the PaM. The SPoint is at the beginning of a SSelection.
+ if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTextNd || pEndTextNd ) )
+ rPam.Exchange();
+
+ if( !pSttTextNd && !pEndTextNd )
+ --rPam.GetPoint()->nNode;
+ rPam.DeleteMark(); // the SPoint is in the selection
+
+ if( !pEndTextNd )
+ m_nEndContent = 0;
+ if( !pSttTextNd )
+ m_nSttContent = 0;
+
+ if( bMoveNds ) // Do Nodes exist that need to be moved?
+ {
+ SwNodes& rNds = pDoc->GetUndoManager().GetUndoNodes();
+ SwNodes& rDocNds = pDoc->GetNodes();
+ SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff,
+ rDocNds, m_nEndNode - m_nNdDiff );
+ if( !bFullPara && !pEndTextNd &&
+ &aRg.aEnd.GetNode() != &pDoc->GetNodes().GetEndOfContent() )
+ {
+ SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode();
+ if( pNode->GetIndex() >= m_nSttNode - m_nNdDiff )
+ ++aRg.aEnd; // Deletion of a complete table
+ }
+ SwNode* pTmpNd;
+ // Step 2: Expand selection if necessary
+ if( m_bJoinNext || bFullPara )
+ {
+ // If all content of a section will be moved into Undo, the section
+ // itself should be moved completely.
+ while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() &&
+ ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() &&
+ pTmpNd->StartOfSectionNode()->IsSectionNode() &&
+ pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) )
+ ++aRg.aEnd;
+ m_nReplaceDummy = aRg.aEnd.GetIndex() + m_nNdDiff - m_nEndNode;
+ if( m_nReplaceDummy )
+ { // The selection has been expanded, because
+ ++aRg.aEnd;
+ if( pEndTextNd )
+ {
+ // The end text node has to leave the (expanded) selection
+ // The dummy is needed because MoveNodes deletes empty
+ // sections
+ ++m_nReplaceDummy;
+ SwNodeRange aMvRg( *pEndTextNd, 0, *pEndTextNd, 1 );
+ SwPosition aSplitPos( *pEndTextNd );
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
+ --aRg.aEnd;
+ }
+ else
+ m_nReplaceDummy = 0;
+ }
+ }
+ if( m_bBackSp || bFullPara )
+ {
+ // See above, the selection has to be expanded if there are "nearly
+ // empty" sections and a replacement dummy has to be set if needed.
+ while( 1 < aRg.aStart.GetIndex() &&
+ ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() &&
+ pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) )
+ --aRg.aStart;
+ if( pSttTextNd )
+ {
+ m_nReplaceDummy = m_nSttNode - m_nNdDiff - aRg.aStart.GetIndex();
+ if( m_nReplaceDummy )
+ {
+ SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 );
+ SwPosition aSplitPos( *pSttTextNd );
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
+ --aRg.aStart;
+ }
+ }
+ }
+
+ if( m_bFromTableCopy )
+ {
+ if( !pEndTextNd )
+ {
+ if( pSttTextNd )
+ ++aRg.aStart;
+ else if( !bFullPara && !aRg.aEnd.GetNode().IsContentNode() )
+ --aRg.aEnd;
+ }
+ }
+ else if (pSttTextNd && (pEndTextNd || pSttTextNd->GetText().getLength()))
+ ++aRg.aStart;
+
+ // Step 3: Moving into UndoArray...
+ m_nNode = rNds.GetEndOfContent().GetIndex();
+ rDocNds.MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() ));
+ m_pMvStt.reset( new SwNodeIndex( rNds, m_nNode ) );
+ // remember difference!
+ m_nNode = rNds.GetEndOfContent().GetIndex() - m_nNode;
+
+ if( pSttTextNd && pEndTextNd )
+ {
+ //Step 4: Moving around sections
+ m_nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ // nSect is the number of sections which starts(ends) between start
+ // and end node of the selection. The "loser" paragraph has to be
+ // moved into the section(s) of the "winner" paragraph
+ if( m_nSectDiff )
+ {
+ if( m_bJoinNext )
+ {
+ SwNodeRange aMvRg( *pEndTextNd, 0, *pEndTextNd, 1 );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
+ }
+ else
+ {
+ SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
+ }
+ }
+ }
+ if( m_nSectDiff || m_nReplaceDummy )
+ lcl_MakeAutoFrames( *pDoc->GetSpzFrameFormats(),
+ m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() );
+ }
+ else
+ m_nNode = 0; // moved no node -> no difference at the end
+
+ // Are there any Nodes that got deleted before that (FootNotes
+ // have ContentNodes)?
+ if( !pSttTextNd && !pEndTextNd )
+ {
+ m_nNdDiff = m_nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1);
+ rPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ {
+ m_nNdDiff = m_nSttNode;
+ if( m_nSectDiff && m_bBackSp )
+ m_nNdDiff += m_nSectDiff;
+ m_nNdDiff -= rPam.GetPoint()->nNode.GetIndex();
+ }
+
+ if( !rPam.GetNode().IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ // is a history necessary here at all?
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+}
+
+bool SwUndoDelete::SaveContent( const SwPosition* pStt, const SwPosition* pEnd,
+ SwTextNode* pSttTextNd, SwTextNode* pEndTextNd )
+{
+ sal_uLong nNdIdx = pStt->nNode.GetIndex();
+ // 1 - copy start in Start-String
+ if( pSttTextNd )
+ {
+ bool bOneNode = m_nSttNode == m_nEndNode;
+ SwRegHistory aRHst( *pSttTextNd, m_pHistory.get() );
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pSttTextNd->GetpSwpHints(), nNdIdx,
+ 0, pSttTextNd->GetText().getLength(), true );
+ if( !bOneNode && pSttTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pSttTextNd->GetpSwAttrSet(), nNdIdx );
+
+ // the length might have changed (!!Fields!!)
+ sal_Int32 nLen = (bOneNode
+ ? pEnd->nContent.GetIndex()
+ : pSttTextNd->GetText().getLength())
+ - pStt->nContent.GetIndex();
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen);
+ pSttTextNd->EraseText( pStt->nContent, nLen );
+ if( pSttTextNd->GetpSwpHints() )
+ pSttTextNd->GetpSwpHints()->DeRegister();
+
+ // METADATA: store
+ bool emptied( !m_aSttStr->isEmpty() && !pSttTextNd->Len() );
+ if (!bOneNode || emptied) // merging may overwrite xmlids...
+ {
+ m_pMetadataUndoStart = emptied
+ ? pSttTextNd->CreateUndoForDelete()
+ : pSttTextNd->CreateUndo();
+ }
+
+ if( bOneNode )
+ return false; // stop moving more nodes
+ }
+
+ // 2 - copy end into End-String
+ if( pEndTextNd )
+ {
+ SwIndex aEndIdx( pEndTextNd );
+ nNdIdx = pEnd->nNode.GetIndex();
+ SwRegHistory aRHst( *pEndTextNd, m_pHistory.get() );
+
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), nNdIdx, 0,
+ pEndTextNd->GetText().getLength(), true );
+
+ if( pEndTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), nNdIdx );
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aEndStr = pEndTextNd->GetText().copy( 0, pEnd->nContent.GetIndex() );
+ pEndTextNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() );
+ if( pEndTextNd->GetpSwpHints() )
+ pEndTextNd->GetpSwpHints()->DeRegister();
+
+ // METADATA: store
+ bool emptied = !m_aEndStr->isEmpty() && !pEndTextNd->Len();
+
+ m_pMetadataUndoEnd = emptied
+ ? pEndTextNd->CreateUndoForDelete()
+ : pEndTextNd->CreateUndo();
+ }
+
+ // if there are only two Nodes then we're done
+ if( ( pSttTextNd || pEndTextNd ) && m_nSttNode + 1 == m_nEndNode )
+ return false; // do not move any Node
+
+ return true; // move Nodes lying in between
+}
+
+bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam )
+{
+ // Is Undo greater than one Node (that is Start and EndString)?
+ if( !m_aSttStr || m_aSttStr->isEmpty() || m_aEndStr )
+ return false;
+
+ // only the deletion of single char's can be condensed
+ if( m_nSttNode != m_nEndNode || ( !m_bGroup && m_nSttContent+1 != m_nEndContent ))
+ return false;
+
+ const SwPosition *pStt = rDelPam.Start(),
+ *pEnd = rDelPam.GetPoint() == pStt
+ ? rDelPam.GetMark()
+ : rDelPam.GetPoint();
+
+ if( pStt->nNode != pEnd->nNode ||
+ pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() ||
+ pEnd->nNode != m_nSttNode )
+ return false;
+
+ // Distinguish between BackSpace and Delete because the Undo array needs to
+ // be constructed differently!
+ if( pEnd->nContent == m_nSttContent )
+ {
+ if( m_bGroup && !m_bBackSp ) return false;
+ m_bBackSp = true;
+ }
+ else if( pStt->nContent == m_nSttContent )
+ {
+ if( m_bGroup && m_bBackSp ) return false;
+ m_bBackSp = false;
+ }
+ else
+ return false;
+
+ // are both Nodes (Node/Undo array) TextNodes at all?
+ SwTextNode * pDelTextNd = pStt->nNode.GetNode().GetTextNode();
+ if( !pDelTextNd ) return false;
+
+ sal_Int32 nUChrPos = m_bBackSp ? 0 : m_aSttStr->getLength()-1;
+ sal_Unicode cDelChar = pDelTextNd->GetText()[ pStt->nContent.GetIndex() ];
+ CharClass& rCC = GetAppCharClass();
+ if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) ||
+ rCC.isLetterNumeric( OUString( cDelChar ), 0 ) !=
+ rCC.isLetterNumeric( *m_aSttStr, nUChrPos ) )
+ return false;
+
+ // tdf#132725 - if at-char/at-para flys would be deleted, don't group!
+ // DelContentIndex() would be called at the wrong time here, the indexes
+ // in the stored SwHistoryTextFlyCnt would be wrong when Undo is invoked
+ if (IsFlySelectedByCursor(*pDoc, *pStt, *pEnd))
+ {
+ return false;
+ }
+
+ {
+ SwRedlineSaveDatas aTmpSav;
+ const bool bSaved = FillSaveData( rDelPam, aTmpSav, false );
+
+ bool bOk = ( !m_pRedlSaveData && !bSaved ) ||
+ ( m_pRedlSaveData && bSaved &&
+ SwUndo::CanRedlineGroup( *m_pRedlSaveData, aTmpSav, m_bBackSp ));
+ // aTmpSav.DeleteAndDestroyAll();
+ if( !bOk )
+ return false;
+
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( rDelPam, false, RedlineType::Any );
+ }
+
+ // Both 'deletes' can be consolidated, so 'move' the related character
+ if( m_bBackSp )
+ m_nSttContent--; // BackSpace: add char to array!
+ else
+ {
+ m_nEndContent++; // Delete: attach char at the end
+ nUChrPos++;
+ }
+ m_aSttStr = m_aSttStr->replaceAt( nUChrPos, 0, OUString(cDelChar) );
+ pDelTextNd->EraseText( pStt->nContent, 1 );
+
+ m_bGroup = true;
+ return true;
+}
+
+SwUndoDelete::~SwUndoDelete()
+{
+ if( m_pMvStt ) // Delete also the selection from UndoNodes array
+ {
+ // Insert saves content in IconSection
+ m_pMvStt->GetNode().GetNodes().Delete( *m_pMvStt, m_nNode );
+ m_pMvStt.reset();
+ }
+ m_pRedlSaveData.reset();
+}
+
+static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory)
+{
+ SwRewriter aRewriter;
+
+ bool bDone = false;
+
+ for ( sal_uInt16 n = 0; n < rHistory.Count(); n++)
+ {
+ OUString aDescr = rHistory[n]->GetDescription();
+
+ if (!aDescr.isEmpty())
+ {
+ aRewriter.AddRule(UndoArg2, aDescr);
+
+ bDone = true;
+ break;
+ }
+ }
+
+ if (! bDone)
+ {
+ aRewriter.AddRule(UndoArg2, SwResId(STR_FIELD));
+ }
+
+ return aRewriter;
+}
+
+static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
+{
+ switch (nChar)
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_TAB:
+ case CH_TXTATR_NEWLINE:
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static OUString lcl_DenotedPortion(const OUString& rStr, sal_Int32 nStart, sal_Int32 nEnd)
+{
+ OUString aResult;
+
+ auto nCount = nEnd - nStart;
+ if (nCount > 0)
+ {
+ sal_Unicode cLast = rStr[nEnd - 1];
+ if (lcl_IsSpecialCharacter(cLast))
+ {
+ switch(cLast)
+ {
+ case CH_TXTATR_TAB:
+ aResult = SwResId(STR_UNDO_TABS, nCount);
+
+ break;
+ case CH_TXTATR_NEWLINE:
+ aResult = SwResId(STR_UNDO_NLS, nCount);
+
+ break;
+
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_BREAKWORD:
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+ break;
+
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ break; // nothing?
+
+ default:
+ assert(!"unexpected special character");
+ break;
+ }
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nCount));
+ aResult = aRewriter.Apply(aResult);
+ }
+ else
+ {
+ aResult = SwResId(STR_START_QUOTE) +
+ rStr.copy(nStart, nCount) +
+ SwResId(STR_END_QUOTE);
+ }
+ }
+
+ return aResult;
+}
+
+OUString DenoteSpecialCharacters(const OUString & rStr)
+{
+ OUStringBuffer aResult;
+
+ if (!rStr.isEmpty())
+ {
+ bool bStart = false;
+ sal_Int32 nStart = 0;
+ sal_Unicode cLast = 0;
+
+ for( sal_Int32 i = 0; i < rStr.getLength(); i++)
+ {
+ if (lcl_IsSpecialCharacter(rStr[i]))
+ {
+ if (cLast != rStr[i])
+ bStart = true;
+
+ }
+ else
+ {
+ if (lcl_IsSpecialCharacter(cLast))
+ bStart = true;
+ }
+
+ if (bStart)
+ {
+ aResult.append(lcl_DenotedPortion(rStr, nStart, i));
+
+ nStart = i;
+ bStart = false;
+ }
+
+ cLast = rStr[i];
+ }
+
+ aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength()));
+ }
+ else
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+
+ return aResult.makeStringAndClear();
+}
+
+SwRewriter SwUndoDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (m_nNode != 0)
+ {
+ if (!m_sTableName.isEmpty())
+ {
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
+ aRewriter.AddRule(UndoArg2, m_sTableName);
+ aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
+
+ OUString sTmp = aRewriter.Apply(SwResId(STR_TABLE_NAME));
+ aResult.AddRule(UndoArg1, sTmp);
+ }
+ else
+ aResult.AddRule(UndoArg1, SwResId(STR_PARAGRAPHS));
+ }
+ else
+ {
+ OUString aStr;
+
+ if (m_aSttStr && m_aEndStr && m_aSttStr->isEmpty() &&
+ m_aEndStr->isEmpty())
+ {
+ aStr = SwResId(STR_PARAGRAPH_UNDO);
+ }
+ else
+ {
+ std::optional<OUString> aTmpStr;
+ if (m_aSttStr)
+ aTmpStr = m_aSttStr;
+ else if (m_aEndStr)
+ aTmpStr = m_aEndStr;
+
+ if (aTmpStr)
+ {
+ aStr = DenoteSpecialCharacters(*aTmpStr);
+ }
+ else
+ {
+ aStr = SwRewriter::GetPlaceHolder(UndoArg2);
+ }
+ }
+
+ aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS));
+ if (m_pHistory)
+ {
+ SwRewriter aRewriter = lcl_RewriterFromHistory(*m_pHistory);
+ aStr = aRewriter.Apply(aStr);
+ }
+
+ aResult.AddRule(UndoArg1, aStr);
+ }
+
+ return aResult;
+}
+
+// Every object, anchored "AtContent" will be reanchored at rPos
+static void lcl_ReAnchorAtContentFlyFrames( const SwFrameFormats& rSpzArr, SwPosition &rPos, sal_uLong nOldIdx )
+{
+ if( !rSpzArr.empty() )
+ {
+ SwFlyFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+ const SwPosition* pAPos;
+ for( size_t n = 0; n < rSpzArr.size(); ++n )
+ {
+ pFormat = static_cast<SwFlyFrameFormat*>(rSpzArr[n]);
+ pAnchor = &pFormat->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ pAPos = pAnchor->GetContentAnchor();
+ if( pAPos && nOldIdx == pAPos->nNode.GetIndex() )
+ {
+ SwFormatAnchor aAnch( *pAnchor );
+ aAnch.SetAnchor( &rPos );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ }
+}
+
+void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+
+ sal_uLong nCalcStt = m_nSttNode - m_nNdDiff;
+
+ if( m_nSectDiff && m_bBackSp )
+ nCalcStt += m_nSectDiff;
+
+ SwNodeIndex aIdx(rDoc.GetNodes(), nCalcStt);
+ SwNode* pInsNd = &aIdx.GetNode();
+ SwNode* pMovedNode = nullptr;
+
+ { // code block so that SwPosition is detached when deleting a Node
+ SwPosition aPos( aIdx );
+ if( !m_bDelFullPara )
+ {
+ assert(!m_bTableDelLastNd || pInsNd->IsTextNode());
+ if( pInsNd->IsTableNode() )
+ {
+ pInsNd = rDoc.GetNodes().MakeTextNode( aIdx,
+ rDoc.GetDfltTextFormatColl() );
+ --aIdx;
+ aPos.nNode = aIdx;
+ aPos.nContent.Assign( pInsNd->GetContentNode(), m_nSttContent );
+ }
+ else
+ {
+ if( pInsNd->IsContentNode() )
+ aPos.nContent.Assign( static_cast<SwContentNode*>(pInsNd), m_nSttContent );
+ if( !m_bTableDelLastNd )
+ pInsNd = nullptr; // do not delete Node!
+ }
+ }
+ else
+ pInsNd = nullptr; // do not delete Node!
+
+ bool bNodeMove = 0 != m_nNode;
+
+ if( m_aEndStr )
+ {
+ // discard attributes since they all saved!
+ SwTextNode * pTextNd;
+ if (!m_bDelFullPara && aPos.nNode.GetNode().IsSectionNode())
+ { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below
+ assert(m_nSttContent == 0);
+ assert(!m_aSttStr);
+ pTextNd = rDoc.GetNodes()[aPos.nNode.GetIndex() + 1]->GetTextNode();
+ }
+ else
+ {
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+ }
+
+ if( pTextNd && pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd && pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ if( m_aSttStr && !m_bFromTableCopy )
+ {
+ sal_uLong nOldIdx = aPos.nNode.GetIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ // After the split all objects are anchored at the first
+ // paragraph, but the pHistory of the fly frame formats relies
+ // on anchoring at the start of the selection
+ // => selection backwards needs a correction.
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+ }
+ assert(pTextNd); // else where does m_aEndStr come from?
+ if( pTextNd )
+ {
+ OUString const ins( pTextNd->InsertText(*m_aEndStr, aPos.nContent,
+ SwInsertFlags::NOHINTEXPAND) );
+ assert(ins.getLength() == m_aEndStr->getLength()); // must succeed
+ (void) ins;
+ // METADATA: restore
+ pTextNd->RestoreMetadata(m_pMetadataUndoEnd);
+ }
+ }
+ else if (m_aSttStr && bNodeMove && pInsNd == nullptr)
+ {
+ SwTextNode * pNd = aPos.nNode.GetNode().GetTextNode();
+ if( pNd )
+ {
+ if (m_nSttContent < pNd->GetText().getLength())
+ {
+ sal_uLong nOldIdx = aPos.nNode.GetIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ }
+ else
+ ++aPos.nNode;
+ }
+ }
+ if( m_nSectDiff )
+ {
+ sal_uLong nMoveIndex = aPos.nNode.GetIndex();
+ int nDiff = 0;
+ if( m_bJoinNext )
+ {
+ nMoveIndex += m_nSectDiff + 1;
+ pMovedNode = &aPos.nNode.GetNode();
+ }
+ else
+ {
+ nMoveIndex -= m_nSectDiff + 1;
+ ++nDiff;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff );
+ --aPos.nNode;
+ if( !m_bJoinNext )
+ pMovedNode = &aPos.nNode.GetNode();
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx);
+ ++aPos.nNode;
+ }
+
+ if( bNodeMove )
+ {
+ SwNodeRange aRange( *m_pMvStt, 0, *m_pMvStt, m_nNode );
+ SwNodeIndex aCopyIndex( aPos.nNode, -1 );
+ rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.nNode,
+ // sw_redlinehide: delay creating frames: the flags on the
+ // nodes aren't necessarily up-to-date, and the redlines
+ // from m_pRedlSaveData aren't applied yet...
+ false);
+
+ if( m_nReplaceDummy )
+ {
+ sal_uLong nMoveIndex;
+ if( m_bJoinNext )
+ {
+ nMoveIndex = m_nEndNode - m_nNdDiff;
+ aPos.nNode = nMoveIndex + m_nReplaceDummy;
+ }
+ else
+ {
+ aPos = SwPosition( aCopyIndex );
+ nMoveIndex = aPos.nNode.GetIndex() + m_nReplaceDummy + 1;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.nNode, 0, aPos.nNode, 1 );
+ pMovedNode = &aPos.nNode.GetNode();
+ // tdf#131684 without deleting frames
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx, false);
+ rDoc.GetNodes().Delete( aMvIdx);
+ }
+ }
+
+ if( m_aSttStr )
+ {
+ aPos.nNode = m_nSttNode - m_nNdDiff + ( m_bJoinNext ? 0 : m_nReplaceDummy );
+ SwTextNode * pTextNd = aPos.nNode.GetNode().GetTextNode();
+ // If more than a single Node got deleted, also all "Node"
+ // attributes were saved
+ if (pTextNd != nullptr)
+ {
+ if( pTextNd->HasSwAttrSet() && bNodeMove && !m_aEndStr )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ // SectionNode mode and selection from top to bottom:
+ // -> in StartNode is still the rest of the Join => delete
+ aPos.nContent.Assign( pTextNd, m_nSttContent );
+ OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent,
+ SwInsertFlags::NOHINTEXPAND) );
+ assert(ins.getLength() == m_aSttStr->getLength()); // must succeed
+ (void) ins;
+ // METADATA: restore
+ pTextNd->RestoreMetadata(m_pMetadataUndoStart);
+ }
+ }
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback(&rDoc, m_nSetPos, false);
+ if( m_nSetPos ) // there were Footnodes/FlyFrames
+ {
+ // are there others than these ones?
+ if( m_nSetPos < m_pHistory->Count() )
+ {
+ // if so save the attributes of the others
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
+ m_pHistory->Rollback(&rDoc);
+ m_pHistory->Move( 0, &aHstr );
+ }
+ else
+ {
+ m_pHistory->Rollback(&rDoc);
+ m_pHistory.reset();
+ }
+ }
+ }
+
+ if( m_bResetPgDesc || m_bResetPgBrk )
+ {
+ sal_uInt16 nStt = m_bResetPgDesc ? sal_uInt16(RES_PAGEDESC) : sal_uInt16(RES_BREAK);
+ sal_uInt16 nEnd = m_bResetPgBrk ? sal_uInt16(RES_BREAK) : sal_uInt16(RES_PAGEDESC);
+
+ SwNode* pNode = rDoc.GetNodes()[ m_nEndNode + 1 ];
+ if( pNode->IsContentNode() )
+ static_cast<SwContentNode*>(pNode)->ResetAttr( nStt, nEnd );
+ else if( pNode->IsTableNode() )
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt, nEnd );
+ }
+ }
+ // delete the temporarily added Node
+ if (pInsNd && !m_bTableDelLastNd)
+ {
+ assert(&aIdx.GetNode() == pInsNd);
+ rDoc.GetNodes().Delete( aIdx );
+ }
+ if( m_pRedlSaveData )
+ SetSaveData(rDoc, *m_pRedlSaveData);
+
+ sal_uLong delFullParaEndNode(m_nEndNode);
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ SwTextNode * pFirstMergedDeletedTextNode(nullptr);
+ SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, *this,
+ *m_pRedlSaveData, pFirstMergedDeletedTextNode);
+ if (pNextNode)
+ {
+ bool bNonMerged(false);
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ frames.push_back(pFrame);
+ }
+ else
+ {
+ bNonMerged = true;
+ }
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ // could either destroy the text frames, or move them...
+ // destroying them would have the advantage that we don't
+ // need special code to *exclude* pFirstMergedDeletedTextNode
+ // from MakeFrames for the layouts in Hide mode but not
+ // layouts in Show mode ...
+ // ... except that MakeFrames won't create them then :(
+ pFrame->RegisterToNode(*pFirstMergedDeletedTextNode);
+ assert(pFrame->GetMergedPara());
+ assert(!bNonMerged); // delFullParaEndNode is such an awful hack
+ (void) bNonMerged;
+ delFullParaEndNode = pFirstMergedDeletedTextNode->GetIndex();
+ }
+ }
+ }
+ else if (m_aSttStr && (!m_bFromTableCopy || 0 != m_nNode))
+ {
+ // only now do we have redlines in the document again; fix up the split
+ // frames
+ SwTextNode *const pStartNode(aIdx.GetNodes()[m_nSttNode]->GetTextNode());
+ assert(pStartNode);
+ sw::RecreateStartTextFrames(*pStartNode);
+ }
+
+ // create frames after SetSaveData has recreated redlines
+ if (0 != m_nNode)
+ {
+ // tdf#136453 only if section nodes at the start
+ if (m_bBackSp && m_nReplaceDummy != 0)
+ {
+ // tdf#134252 *first* create outer section frames
+ // note: text node m_nSttNode currently has frame with an upper;
+ // there's a hack in InsertCnt_() to move it below new section frame
+ SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode - m_nReplaceDummy);
+ SwNodeIndex const end(rDoc.GetNodes(), m_nSttNode); // exclude m_nSttNode
+ ::MakeFrames(&rDoc, start, end);
+ }
+ // tdf#121031 if the start node is a text node, it already has a frame;
+ // if it's a table, it does not
+ // tdf#109376 exception: end on non-text-node -> start node was inserted
+ assert(!m_bDelFullPara || (m_nSectDiff == 0));
+ SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode +
+ ((m_bDelFullPara || !rDoc.GetNodes()[m_nSttNode]->IsTextNode() || pInsNd)
+ ? 0 : 1));
+ // don't include end node in the range: it may have been merged already
+ // by the start node, or it may be merged by one of the moved nodes,
+ // but if it isn't merged, its current frame(s) should be good...
+ SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara ? delFullParaEndNode : m_nEndNode);
+ ::MakeFrames(&rDoc, start, end);
+ }
+
+ if (pMovedNode)
+ { // probably better do this after creating all frames
+ lcl_MakeAutoFrames(*rDoc.GetSpzFrameFormats(), pMovedNode->GetIndex());
+ }
+
+ // tdf#134021 only after MakeFrames(), because it may be the only node
+ // that has layout frames
+ if (pInsNd && m_bTableDelLastNd)
+ {
+ assert(&aIdx.GetNode() == pInsNd);
+ SwPaM tmp(aIdx, aIdx);
+ rDoc.getIDocumentContentOperations().DelFullPara(tmp);
+ }
+
+ AddUndoRedoPaM(rContext, true);
+}
+
+void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+ SwDoc& rDoc = *rPam.GetDoc();
+
+ if( m_pRedlSaveData )
+ {
+ const bool bSuccess = FillSaveData(rPam, *m_pRedlSaveData);
+ OSL_ENSURE(bSuccess,
+ "SwUndoDelete::Redo: used to have redline data, but now none?");
+ if (!bSuccess)
+ {
+ m_pRedlSaveData.reset();
+ }
+ }
+
+ if( !m_bDelFullPara )
+ {
+ // tdf#128739 correct cursors but do not delete bookmarks yet
+ ::PaMCorrAbs(rPam, *rPam.End());
+ SetPaM(rPam);
+
+ if( !m_bJoinNext ) // then restore selection from bottom to top
+ rPam.Exchange();
+ }
+
+ if( m_pHistory ) // are the attributes saved?
+ {
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get() );
+
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
+ }
+ else
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ m_pHistory->Move( m_nSetPos, &aHstr );
+ }
+ else
+ {
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode );
+ }
+ else
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+ }
+
+ if( !m_aSttStr && !m_aEndStr )
+ {
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
+ }
+
+ SwNodeIndex aSttIdx = ( m_bDelFullPara || m_bJoinNext )
+ ? rPam.GetMark()->nNode
+ : rPam.GetPoint()->nNode;
+ SwTableNode* pTableNd = aSttIdx.GetNode().GetTableNode();
+ if( pTableNd )
+ {
+ if( m_bTableDelLastNd )
+ {
+ // than add again a Node at the end
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ rDoc.GetNodes().MakeTextNode( aTmpIdx,
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ SwContentNode* pNextNd = rDoc.GetNodes()[
+ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+ }
+ pTableNd->DelFrames();
+ }
+ else if (*rPam.GetMark() == *rPam.GetPoint())
+ { // paragraph with only footnote or as-char fly, delete that
+ // => DelContentIndex has already deleted it! nothing to do here
+ assert(m_nEndNode == m_nSttNode);
+ return;
+ }
+
+ // avoid asserts from ~SwIndexReg for deleted nodes
+ SwPaM aTmp(*rPam.End());
+ if (!aTmp.Move(fnMoveForward, GoInNode))
+ {
+ *aTmp.GetPoint() = *rPam.Start();
+ aTmp.Move(fnMoveBackward, GoInNode);
+ }
+ assert(aTmp.GetPoint()->nNode != rPam.GetPoint()->nNode
+ && aTmp.GetPoint()->nNode != rPam.GetMark()->nNode);
+ ::PaMCorrAbs(rPam, *aTmp.GetPoint());
+
+ rPam.DeleteMark();
+
+ rDoc.GetNodes().Delete( aSttIdx, m_nEndNode - m_nSttNode );
+ }
+ else if( m_bDelFullPara )
+ {
+ assert(!"dead code");
+ // The Pam was incremented by one at Point (== end) to provide space
+ // for UNDO. This now needs to be reverted!
+ --rPam.End()->nNode;
+ if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
+ *rPam.GetMark() = *rPam.GetPoint();
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ }
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam );
+}
+
+void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // this action does not seem idempotent,
+ // so make sure it is only executed once on repeat
+ if (rContext.m_bDeleteRepeated)
+ return;
+
+ SwPaM & rPam = rContext.GetRepeatPaM();
+ SwDoc& rDoc = *rPam.GetDoc();
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ if( !rPam.HasMark() )
+ {
+ rPam.SetMark();
+ rPam.Move( fnMoveForward, GoInContent );
+ }
+ if( m_bDelFullPara )
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam );
+ rContext.m_bDeleteRepeated = true;
+}
+
+void SwUndoDelete::SetTableName(const OUString & rName)
+{
+ m_sTableName = rName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */