diff options
Diffstat (limited to 'sw/source/core/undo/undel.cxx')
-rw-r--r-- | sw/source/core/undo/undel.cxx | 996 |
1 files changed, 996 insertions, 0 deletions
diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx new file mode 100644 index 000000000000..3b197b81c4f8 --- /dev/null +++ b/sw/source/core/undo/undel.cxx @@ -0,0 +1,996 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +#include <UndoDelete.hxx> + +#include <hintids.hxx> +#include <unotools/charclass.hxx> +#include <editeng/brkitem.hxx> +#include <fmtpdsc.hxx> +#include <frmfmt.hxx> +#include <fmtanchr.hxx> +#include <doc.hxx> +#include <UndoManager.hxx> +#include <swtable.hxx> +#include <swundo.hxx> // fuer die UndoIds +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <poolfmt.hxx> +#include <mvsave.hxx> +#include <redline.hxx> +#include <docary.hxx> +#include <sfx2/app.hxx> + +#include <fldbas.hxx> +#include <fmtfld.hxx> +#include <comcore.hrc> // #111827# +#include <undo.hrc> + +// #include <editeng/svxacorr.hxx> +// #include <comphelper/processfactory.hxx> +// #include <editeng/unolingu.hxx> +// #include <unotools/localedatawrapper.hxx> + +// using namespace comphelper; + + +// DELETE +/* lcl_MakeAutoFrms has to call MakeFrms for objects bounded "AtChar" ( == AUTO ), + if the anchor frame has be moved via _MoveNodes(..) and DelFrms(..) +*/ + +void lcl_MakeAutoFrms( const SwSpzFrmFmts& rSpzArr, sal_uLong nMovedIndex ) +{ + if( rSpzArr.Count() ) + { + SwFlyFrmFmt* pFmt; + const SwFmtAnchor* pAnchor; + for( sal_uInt16 n = 0; n < rSpzArr.Count(); ++n ) + { + pFmt = (SwFlyFrmFmt*)rSpzArr[n]; + pAnchor = &pFmt->GetAnchor(); + if (pAnchor->GetAnchorId() == FLY_AT_CHAR) + { + const SwPosition* pAPos = pAnchor->GetCntntAnchor(); + if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() ) + pFmt->MakeFrms(); + } + } + } +} + +/* +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 indizes 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, sal_Bool bFullPara, sal_Bool bCalledByTblCpy ) + : SwUndo(UNDO_DELETE), SwUndRng( rPam ), + pMvStt( 0 ), pSttStr(0), pEndStr(0), pRedlData(0), pRedlSaveData(0), + nNode(0), nNdDiff(0), nSectDiff(0), nReplaceDummy(0), nSetPos(0), + bGroup( sal_False ), bBackSp( sal_False ), bJoinNext( sal_False ), bTblDelLastNd( sal_False ), + bDelFullPara( bFullPara ), bResetPgDesc( sal_False ), bResetPgBrk( sal_False ), + bFromTableCopy( bCalledByTblCpy ) +{ + bDelFullPara = bFullPara; // This is set e.g. if an empty paragraph before a table is deleted + + bCacheComment = false; + + SwDoc * pDoc = rPam.GetDoc(); + + if( !pDoc->IsIgnoreRedline() && pDoc->GetRedlineTbl().Count() ) + { + pRedlSaveData = new SwRedlineSaveDatas; + if( !FillSaveData( rPam, *pRedlSaveData )) + delete pRedlSaveData, pRedlSaveData = 0; + } + + if( !pHistory ) + pHistory = new SwHistory; + + // loesche erstmal alle Fussnoten + const SwPosition *pStt = rPam.Start(), + *pEnd = rPam.GetPoint() == pStt + ? rPam.GetMark() + : rPam.GetPoint(); + + // Step 1. deletion/record of content indizes + if( bDelFullPara ) + { + ASSERT( rPam.HasMark(), "PaM ohne Mark" ); + DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(), + DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) ); + + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + _DelBookmarks(pStt->nNode, pEnd->nNode); + } + else + DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() ); + + nSetPos = pHistory ? pHistory->Count() : 0; + + // wurde schon was geloescht ?? + nNdDiff = nSttNode - pStt->nNode.GetIndex(); + + bJoinNext = !bFullPara && pEnd == rPam.GetPoint(); + bBackSp = !bFullPara && !bJoinNext; + + SwTxtNode *pSttTxtNd = 0, *pEndTxtNd = 0; + if( !bFullPara ) + { + pSttTxtNd = pStt->nNode.GetNode().GetTxtNode(); + pEndTxtNd = nSttNode == nEndNode + ? pSttTxtNd + : pEnd->nNode.GetNode().GetTxtNode(); + } + + sal_Bool bMoveNds = *pStt == *pEnd // noch ein Bereich vorhanden ?? + ? sal_False + : ( SaveCntnt( pStt, pEnd, pSttTxtNd, pEndTxtNd ) || bFromTableCopy ); + + if( pSttTxtNd && pEndTxtNd && pSttTxtNd != pEndTxtNd ) + { + // zwei unterschiedliche TextNodes, also speicher noch die + // TextFormatCollection fuers + pHistory->Add( pSttTxtNd->GetTxtColl(),pStt->nNode.GetIndex(), ND_TEXTNODE ); + pHistory->Add( pEndTxtNd->GetTxtColl(),pEnd->nNode.GetIndex(), ND_TEXTNODE ); + + if( !bJoinNext ) // Selection von Unten nach Oben + { + // Beim JoinPrev() werden die AUTO-PageBreak's richtig + // kopiert. Um diese beim Undo wieder herzustellen, muss das + // Auto-PageBreak aus dem EndNode zurueckgesetzt werden. + // - fuer die PageDesc, ColBreak dito ! + if( pEndTxtNd->HasSwAttrSet() ) + { + SwRegHistory aRegHist( *pEndTxtNd, pHistory ); + if( SFX_ITEM_SET == pEndTxtNd->GetpSwAttrSet()->GetItemState( + RES_BREAK, sal_False ) ) + pEndTxtNd->ResetAttr( RES_BREAK ); + if( pEndTxtNd->HasSwAttrSet() && + SFX_ITEM_SET == pEndTxtNd->GetpSwAttrSet()->GetItemState( + RES_PAGEDESC, sal_False ) ) + pEndTxtNd->ResetAttr( RES_PAGEDESC ); + } + } + } + + + // verschiebe jetzt noch den PaM !!! + // der SPoint steht am Anfang der SSelection + if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTxtNd || pEndTxtNd ) ) + rPam.Exchange(); + + if( !pSttTxtNd && !pEndTxtNd ) + rPam.GetPoint()->nNode--; + rPam.DeleteMark(); // der SPoint ist aus dem Bereich + + if( !pEndTxtNd ) + nEndCntnt = 0; + if( !pSttTxtNd ) + nSttCntnt = 0; + + if( bMoveNds ) // sind noch Nodes zu verschieben ? + { + SwNodes& rNds = pDoc->GetUndoManager().GetUndoNodes(); + SwNodes& rDocNds = pDoc->GetNodes(); + SwNodeRange aRg( rDocNds, nSttNode - nNdDiff, + rDocNds, nEndNode - nNdDiff ); + if( !bFullPara && !pEndTxtNd && + &aRg.aEnd.GetNode() != &pDoc->GetNodes().GetEndOfContent() ) + { + SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode(); + if( pNode->GetIndex() >= nSttNode - nNdDiff ) + aRg.aEnd++; // Deletion of a complete table + } + SwNode* pTmpNd; + // Step 2: Expand selection if necessary + if( bJoinNext || bFullPara ) + { + // If all content of a section will be moved into Undo, + // the section itself should be moved complete. + 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++; + nReplaceDummy = aRg.aEnd.GetIndex() + nNdDiff - nEndNode; + if( nReplaceDummy ) + { // The selection has been expanded, because + aRg.aEnd++; + if( pEndTxtNd ) + { + // The end text node has to leave the (expanded) selection + // The dummy is needed because _MoveNodes deletes empty sections + ++nReplaceDummy; + SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 ); + SwPosition aSplitPos( *pEndTxtNd ); + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + pDoc->SplitNode( aSplitPos, false ); + rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aEnd, sal_True ); + aRg.aEnd--; + } + else + nReplaceDummy = 0; + } + } + if( bBackSp || bFullPara ) + { + //See above, the selection has to 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( pSttTxtNd ) + { + nReplaceDummy = nSttNode - nNdDiff - aRg.aStart.GetIndex(); + if( nReplaceDummy ) + { + SwNodeRange aMvRg( *pSttTxtNd, 0, *pSttTxtNd, 1 ); + SwPosition aSplitPos( *pSttTxtNd ); + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + pDoc->SplitNode( aSplitPos, false ); + rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True ); + aRg.aStart--; + } + } + } + + if( bFromTableCopy ) + { + if( !pEndTxtNd ) + { + if( pSttTxtNd ) + aRg.aStart++; + else if( !bFullPara && !aRg.aEnd.GetNode().IsCntntNode() ) + aRg.aEnd--; + } + } + else if( pSttTxtNd && ( pEndTxtNd || pSttTxtNd->GetTxt().Len() ) ) + aRg.aStart++; + + // Step 3: Moving into UndoArray... + nNode = rNds.GetEndOfContent().GetIndex(); + rDocNds._MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() )); + pMvStt = new SwNodeIndex( rNds, nNode ); + nNode = rNds.GetEndOfContent().GetIndex() - nNode; // Differenz merken ! + if( pSttTxtNd && pEndTxtNd ) + { + //Step 4: Moving around sections + 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( nSectDiff ) + { + if( bJoinNext ) + { + SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 ); + rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True ); + } + else + { + SwNodeRange aMvRg( *pSttTxtNd, 0, *pSttTxtNd, 1 ); + rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aEnd, sal_True ); + } + } + } + if( nSectDiff || nReplaceDummy ) + lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(), + bJoinNext ? pEndTxtNd->GetIndex() : pSttTxtNd->GetIndex() ); + } + else + nNode = 0; // kein Node verschoben -> keine Differenz zum Ende + + // wurden davor noch Nodes geloescht ?? (FootNotes haben ContentNodes!) + if( !pSttTxtNd && !pEndTxtNd ) + { + nNdDiff = nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1); + rPam.Move( fnMoveForward, fnGoNode ); + } + else + { + nNdDiff = nSttNode; + if( nSectDiff && bBackSp ) + nNdDiff += nSectDiff; + nNdDiff -= rPam.GetPoint()->nNode.GetIndex(); + } + + if( !rPam.GetNode()->IsCntntNode() ) + rPam.GetPoint()->nContent.Assign( 0, 0 ); + + // wird die History ueberhaupt benoetigt ?? + if( pHistory && !pHistory->Count() ) + DELETEZ( pHistory ); +} + +sal_Bool SwUndoDelete::SaveCntnt( const SwPosition* pStt, const SwPosition* pEnd, + SwTxtNode* pSttTxtNd, SwTxtNode* pEndTxtNd ) +{ + sal_uLong nNdIdx = pStt->nNode.GetIndex(); + // 1 - kopiere den Anfang in den Start-String + if( pSttTxtNd ) + { + sal_Bool bOneNode = nSttNode == nEndNode; + xub_StrLen nLen = bOneNode ? nEndCntnt - nSttCntnt + : pSttTxtNd->GetTxt().Len() - nSttCntnt; + SwRegHistory aRHst( *pSttTxtNd, pHistory ); + // always save all text atttibutes because of possibly overlapping + // areas of on/off + pHistory->CopyAttr( pSttTxtNd->GetpSwpHints(), nNdIdx, + 0, pSttTxtNd->GetTxt().Len(), true ); + if( !bOneNode && pSttTxtNd->HasSwAttrSet() ) + pHistory->CopyFmtAttr( *pSttTxtNd->GetpSwAttrSet(), nNdIdx ); + + // die Laenge kann sich veraendert haben (!!Felder!!) + nLen = ( bOneNode ? pEnd->nContent.GetIndex() : pSttTxtNd->GetTxt().Len() ) + - pStt->nContent.GetIndex(); + + + // loesche jetzt noch den Text (alle Attribut-Aenderungen kommen in + // die Undo-History + pSttStr = (String*)new String( pSttTxtNd->GetTxt().Copy( nSttCntnt, nLen )); + pSttTxtNd->EraseText( pStt->nContent, nLen ); + if( pSttTxtNd->GetpSwpHints() ) + pSttTxtNd->GetpSwpHints()->DeRegister(); + + // METADATA: store + bool emptied( pSttStr->Len() && !pSttTxtNd->Len() ); + if (!bOneNode || emptied) // merging may overwrite xmlids... + { + m_pMetadataUndoStart = (emptied) + ? pSttTxtNd->CreateUndoForDelete() + : pSttTxtNd->CreateUndo(); + } + + if( bOneNode ) + return sal_False; // keine Nodes mehr verschieben + } + + + // 2 - kopiere das Ende in den End-String + if( pEndTxtNd ) + { + SwIndex aEndIdx( pEndTxtNd ); + nNdIdx = pEnd->nNode.GetIndex(); + SwRegHistory aRHst( *pEndTxtNd, pHistory ); + + // always save all text atttibutes because of possibly overlapping + // areas of on/off + pHistory->CopyAttr( pEndTxtNd->GetpSwpHints(), nNdIdx, 0, + pEndTxtNd->GetTxt().Len(), true ); + + if( pEndTxtNd->HasSwAttrSet() ) + pHistory->CopyFmtAttr( *pEndTxtNd->GetpSwAttrSet(), nNdIdx ); + + + // loesche jetzt noch den Text (alle Attribut-Aenderungen kommen in + // die Undo-History + pEndStr = (String*)new String( pEndTxtNd->GetTxt().Copy( 0, + pEnd->nContent.GetIndex() )); + pEndTxtNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() ); + if( pEndTxtNd->GetpSwpHints() ) + pEndTxtNd->GetpSwpHints()->DeRegister(); + + // METADATA: store + bool emptied( pEndStr->Len() && !pEndTxtNd->Len() ); + + m_pMetadataUndoEnd = (emptied) + ? pEndTxtNd->CreateUndoForDelete() + : pEndTxtNd->CreateUndo(); + } + + // sind es nur zwei Nodes, dann ist schon alles erledigt. + if( ( pSttTxtNd || pEndTxtNd ) && nSttNode + 1 == nEndNode ) + return sal_False; // keine Nodes mehr verschieben + + return sal_True; // verschiebe die dazwischen liegenden Nodes +} + + +sal_Bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam ) +{ + // ist das Undo groesser als 1 Node ? (sprich: Start und EndString) + if( pSttStr ? !pSttStr->Len() || pEndStr : sal_True ) + return sal_False; + + // es kann nur das Loeschen von einzelnen char's zusammengefasst werden + if( nSttNode != nEndNode || ( !bGroup && nSttCntnt+1 != nEndCntnt )) + return sal_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 != nSttNode ) + return sal_False; + + // untercheide zwischen BackSpace und Delete. Es muss dann das + // Undo-Array unterschiedlich aufgebaut werden !! + if( pEnd->nContent == nSttCntnt ) + { + if( bGroup && !bBackSp ) return sal_False; + bBackSp = sal_True; + } + else if( pStt->nContent == nSttCntnt ) + { + if( bGroup && bBackSp ) return sal_False; + bBackSp = sal_False; + } + else + return sal_False; + + // sind die beiden Nodes (Nodes-/Undo-Array) ueberhaupt TextNodes? + SwTxtNode * pDelTxtNd = pStt->nNode.GetNode().GetTxtNode(); + if( !pDelTxtNd ) return sal_False; + + xub_StrLen nUChrPos = bBackSp ? 0 : pSttStr->Len()-1; + sal_Unicode cDelChar = pDelTxtNd->GetTxt().GetChar( pStt->nContent.GetIndex() ); + CharClass& rCC = GetAppCharClass(); + if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) || + rCC.isLetterNumeric( String( cDelChar ), 0 ) != + rCC.isLetterNumeric( *pSttStr, nUChrPos ) ) + return sal_False; + + { + SwRedlineSaveDatas* pTmpSav = new SwRedlineSaveDatas; + if( !FillSaveData( rDelPam, *pTmpSav, sal_False )) + delete pTmpSav, pTmpSav = 0; + + sal_Bool bOk = ( !pRedlSaveData && !pTmpSav ) || + ( pRedlSaveData && pTmpSav && + SwUndo::CanRedlineGroup( *pRedlSaveData, *pTmpSav, bBackSp )); + delete pTmpSav; + if( !bOk ) + return sal_False; + + pDoc->DeleteRedline( rDelPam, false, USHRT_MAX ); + } + + // Ok, die beiden 'Deletes' koennen zusammen gefasst werden, also + // 'verschiebe' das enstprechende Zeichen + if( bBackSp ) + nSttCntnt--; // BackSpace: Zeichen in Array einfuegen !! + else + { + nEndCntnt++; // Delete: Zeichen am Ende anhaengen + nUChrPos++; + } + pSttStr->Insert( cDelChar, nUChrPos ); + pDelTxtNd->EraseText( pStt->nContent, 1 ); + + bGroup = sal_True; + return sal_True; +} + + + +SwUndoDelete::~SwUndoDelete() +{ + delete pSttStr; + delete pEndStr; + if( pMvStt ) // loesche noch den Bereich aus dem UndoNodes Array + { + // Insert speichert den Inhalt in der IconSection + pMvStt->GetNode().GetNodes().Delete( *pMvStt, nNode ); + delete pMvStt; + } + delete pRedlData; + delete pRedlSaveData; +} + +static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory) +{ + SwRewriter aRewriter; + + bool bDone = false; + + for ( sal_uInt16 n = 0; n < rHistory.Count(); n++) + { + String aDescr = rHistory[n]->GetDescription(); + + if (aDescr.Len() > 0) + { + aRewriter.AddRule(UNDO_ARG2, aDescr); + + bDone = true; + break; + } + } + + if (! bDone) + { + aRewriter.AddRule(UNDO_ARG2, SW_RES(STR_FIELD)); + } + + return aRewriter; +} + +SwRewriter SwUndoDelete::GetRewriter() const +{ + SwRewriter aResult; + String * pStr = NULL; + + if (nNode != 0) + { + if (sTableName.Len() > 0) + { + + SwRewriter aRewriter; + aRewriter.AddRule(UNDO_ARG1, SW_RES(STR_START_QUOTE)); + aRewriter.AddRule(UNDO_ARG2, sTableName); + aRewriter.AddRule(UNDO_ARG3, SW_RES(STR_END_QUOTE)); + + String sTmp = aRewriter.Apply(SW_RES(STR_TABLE_NAME)); + aResult.AddRule(UNDO_ARG1, sTmp); + } + else + aResult.AddRule(UNDO_ARG1, String(SW_RES(STR_PARAGRAPHS))); + } + else + { + String aStr; + + if (pSttStr != NULL && pEndStr != NULL && pSttStr->Len() == 0 && + pEndStr->Len() == 0) + { + aStr = SW_RES(STR_PARAGRAPH_UNDO); + } + else + { + if (pSttStr != NULL) + pStr = pSttStr; + else if (pEndStr != NULL) + pStr = pEndStr; + + if (pStr != NULL) + { + aStr = DenoteSpecialCharacters(*pStr); + } + else + { + aStr = UNDO_ARG2; + } + } + + aStr = ShortenString(aStr, nUndoStringLength, String(SW_RES(STR_LDOTS))); + if (pHistory) + { + SwRewriter aRewriter = lcl_RewriterFromHistory(*pHistory); + aStr = aRewriter.Apply(aStr); + } + + aResult.AddRule(UNDO_ARG1, aStr); + } + + return aResult; +} + +// Every object, anchored "AtCntnt" will be reanchored at rPos +void lcl_ReAnchorAtCntntFlyFrames( const SwSpzFrmFmts& rSpzArr, SwPosition &rPos, sal_uLong nOldIdx ) +{ + if( rSpzArr.Count() ) + { + SwFlyFrmFmt* pFmt; + const SwFmtAnchor* pAnchor; + const SwPosition* pAPos; + for( sal_uInt16 n = 0; n < rSpzArr.Count(); ++n ) + { + pFmt = (SwFlyFrmFmt*)rSpzArr[n]; + pAnchor = &pFmt->GetAnchor(); + if (pAnchor->GetAnchorId() == FLY_AT_PARA) + { + pAPos = pAnchor->GetCntntAnchor(); + if( pAPos && nOldIdx == pAPos->nNode.GetIndex() ) + { + SwFmtAnchor aAnch( *pAnchor ); + aAnch.SetAnchor( &rPos ); + pFmt->SetFmtAttr( aAnch ); + } + } + } + } +} + +void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + + sal_uLong nCalcStt = nSttNode - nNdDiff; + + if( nSectDiff && bBackSp ) + nCalcStt += nSectDiff; + + SwNodeIndex aIdx( pDoc->GetNodes(), nCalcStt ); + SwNode* pInsNd = &aIdx.GetNode(); + + { // Block, damit der SwPosition beim loeschen vom Node + // abgemeldet ist + SwPosition aPos( aIdx ); + if( !bDelFullPara ) + { + if( pInsNd->IsTableNode() ) + { + pInsNd = pDoc->GetNodes().MakeTxtNode( aIdx, + (SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() ); + aIdx--; + aPos.nNode = aIdx; + aPos.nContent.Assign( pInsNd->GetCntntNode(), nSttCntnt ); + } + else + { + if( pInsNd->IsCntntNode() ) + aPos.nContent.Assign( (SwCntntNode*)pInsNd, nSttCntnt ); + if( !bTblDelLastNd ) + pInsNd = 0; // Node nicht loeschen !! + } + } + else + pInsNd = 0; // Node nicht loeschen !! + + sal_Bool bNodeMove = 0 != nNode; + + if( pEndStr ) + { + // alle Attribute verwerfen, wurden alle gespeichert! + SwTxtNode* pTxtNd = aPos.nNode.GetNode().GetTxtNode(); + + if( pTxtNd && pTxtNd->HasSwAttrSet() ) + pTxtNd->ResetAllAttr(); + + if( pTxtNd && pTxtNd->GetpSwpHints() ) + pTxtNd->ClearSwpHintsArr( true ); + + if( pSttStr && !bFromTableCopy ) + { + sal_uLong nOldIdx = aPos.nNode.GetIndex(); + pDoc->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( bBackSp ) + lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx ); + pTxtNd = aPos.nNode.GetNode().GetTxtNode(); + } + if( pTxtNd ) + { + pTxtNd->InsertText( *pEndStr, aPos.nContent, + IDocumentContentOperations::INS_NOHINTEXPAND ); + // METADATA: restore + pTxtNd->RestoreMetadata(m_pMetadataUndoEnd); + } + } + else if( pSttStr && bNodeMove ) + { + SwTxtNode * pNd = aPos.nNode.GetNode().GetTxtNode(); + if( pNd ) + { + if( nSttCntnt < pNd->GetTxt().Len() ) + { + sal_uLong nOldIdx = aPos.nNode.GetIndex(); + pDoc->SplitNode( aPos, false ); + if( bBackSp ) + lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx ); + } + else + aPos.nNode++; + } + } + SwNode* pMovedNode = NULL; + if( nSectDiff ) + { + sal_uLong nMoveIndex = aPos.nNode.GetIndex(); + int nDiff = 0; + if( bJoinNext ) + { + nMoveIndex += nSectDiff + 1; + pMovedNode = &aPos.nNode.GetNode(); + } + else + { + nMoveIndex -= nSectDiff + 1; + ++nDiff; + } + SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex ); + SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff ); + aPos.nNode--; + if( !bJoinNext ) + pMovedNode = &aPos.nNode.GetNode(); + pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True ); + aPos.nNode++; + } + + if( bNodeMove ) + { + SwNodeRange aRange( *pMvStt, 0, *pMvStt, nNode ); + SwNodeIndex aCopyIndex( aPos.nNode, -1 ); + pDoc->GetUndoManager().GetUndoNodes()._Copy( aRange, aPos.nNode ); + + if( nReplaceDummy ) + { + sal_uLong nMoveIndex; + if( bJoinNext ) + { + nMoveIndex = nEndNode - nNdDiff; + aPos.nNode = nMoveIndex + nReplaceDummy; + } + else + { + aPos = SwPosition( aCopyIndex ); + nMoveIndex = aPos.nNode.GetIndex() + nReplaceDummy + 1; + } + SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex ); + SwNodeRange aRg( aPos.nNode, 0, aPos.nNode, 1 ); + pMovedNode = &aPos.nNode.GetNode(); + pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True ); + pDoc->GetNodes().Delete( aMvIdx, 1 ); + } + } + + if( pMovedNode ) + lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(), pMovedNode->GetIndex() ); + + if( pSttStr ) + { + aPos.nNode = nSttNode - nNdDiff + ( bJoinNext ? 0 : nReplaceDummy ); + SwTxtNode * pTxtNd = aPos.nNode.GetNode().GetTxtNode(); + // wenn mehr als ein Node geloescht wurde, dann wurden auch + // alle "Node"-Attribute gespeichert + + if (pTxtNd != NULL) + { + if( pTxtNd->HasSwAttrSet() && bNodeMove && !pEndStr ) + pTxtNd->ResetAllAttr(); + + if( pTxtNd->GetpSwpHints() ) + pTxtNd->ClearSwpHintsArr( true ); + + // SectionNode-Modus und von oben nach unten selektiert: + // -> im StartNode steht noch der Rest vom Join => loeschen + aPos.nContent.Assign( pTxtNd, nSttCntnt ); + pTxtNd->InsertText( *pSttStr, aPos.nContent, + IDocumentContentOperations::INS_NOHINTEXPAND ); + // METADATA: restore + pTxtNd->RestoreMetadata(m_pMetadataUndoStart); + } + } + + if( pHistory ) + { + pHistory->TmpRollback( pDoc, nSetPos, false ); + if( nSetPos ) // es gab Fussnoten/FlyFrames + { + // gibts ausser diesen noch andere ? + if( nSetPos < pHistory->Count() ) + { + // dann sicher die Attribute anderen Attribute + SwHistory aHstr; + aHstr.Move( 0, pHistory, nSetPos ); + pHistory->Rollback( pDoc ); + pHistory->Move( 0, &aHstr ); + } + else + { + pHistory->Rollback( pDoc ); + DELETEZ( pHistory ); + } + } + } + + if( bResetPgDesc || bResetPgBrk ) + { + sal_uInt16 nStt = static_cast<sal_uInt16>( bResetPgDesc ? RES_PAGEDESC : RES_BREAK ); + sal_uInt16 nEnd = static_cast<sal_uInt16>( bResetPgBrk ? RES_BREAK : RES_PAGEDESC ); + + SwNode* pNode = pDoc->GetNodes()[ nEndNode + 1 ]; + if( pNode->IsCntntNode() ) + ((SwCntntNode*)pNode)->ResetAttr( nStt, nEnd ); + else if( pNode->IsTableNode() ) + ((SwTableNode*)pNode)->GetTable().GetFrmFmt()->ResetFmtAttr( nStt, nEnd ); + } + } + // den temp. eingefuegten Node noch loeschen !! + if( pInsNd ) + pDoc->GetNodes().Delete( aIdx, 1 ); + if( pRedlSaveData ) + SetSaveData( *pDoc, *pRedlSaveData ); + + AddUndoRedoPaM(rContext, true); +} + +void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam = AddUndoRedoPaM(rContext); + SwDoc& rDoc = *rPam.GetDoc(); + + if( pRedlSaveData ) + { + bool bSuccess = FillSaveData(rPam, *pRedlSaveData, sal_True); + OSL_ENSURE(bSuccess, + "SwUndoDelete::Redo: used to have redline data, but now none?"); + if (!bSuccess) + { + delete pRedlSaveData, pRedlSaveData = 0; + } + } + + if( !bDelFullPara ) + { + SwUndRng aTmpRng( rPam ); + RemoveIdxFromRange( rPam, sal_False ); + aTmpRng.SetPaM( rPam ); + + if( !bJoinNext ) // Dann Selektion von unten nach oben + rPam.Exchange(); // wieder herstellen! + } + + if( pHistory ) // wurden Attribute gesichert ? + { + pHistory->SetTmpEnd( pHistory->Count() ); + SwHistory aHstr; + aHstr.Move( 0, pHistory ); + + if( bDelFullPara ) + { + ASSERT( rPam.HasMark(), "PaM ohne Mark" ); + DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(), + DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) ); + + _DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode); + } + else + DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() ); + nSetPos = pHistory ? pHistory->Count() : 0; + + pHistory->Move( nSetPos, &aHstr ); + } + else + { + if( bDelFullPara ) + { + ASSERT( rPam.HasMark(), "PaM ohne Mark" ); + DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(), + DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) ); + + _DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode ); + } + else + DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() ); + nSetPos = pHistory ? pHistory->Count() : 0; + } + + if( !pSttStr && !pEndStr ) + { + SwNodeIndex aSttIdx = ( bDelFullPara || bJoinNext ) + ? rPam.GetMark()->nNode + : rPam.GetPoint()->nNode; + SwTableNode* pTblNd = aSttIdx.GetNode().GetTableNode(); + if( pTblNd ) + { + if( bTblDelLastNd ) + { + // dann am Ende wieder einen Node einfuegen + const SwNodeIndex aTmpIdx( *pTblNd->EndOfSectionNode(), 1 ); + rDoc.GetNodes().MakeTxtNode( aTmpIdx, + rDoc.GetTxtCollFromPool( RES_POOLCOLL_STANDARD ) ); + } + + SwCntntNode* pNextNd = rDoc.GetNodes()[ + pTblNd->EndOfSectionIndex()+1 ]->GetCntntNode(); + if( pNextNd ) + { + SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt(); + + const SfxPoolItem *pItem; + if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_PAGEDESC, + sal_False, &pItem ) ) + pNextNd->SetAttr( *pItem ); + + if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_BREAK, + sal_False, &pItem ) ) + pNextNd->SetAttr( *pItem ); + } + pTblNd->DelFrms(); + } + + rPam.SetMark(); + rPam.DeleteMark(); + + rDoc.GetNodes().Delete( aSttIdx, nEndNode - nSttNode ); + + // setze den Cursor immer in einen ContentNode !! + if( !rPam.Move( fnMoveBackward, fnGoCntnt ) && + !rPam.Move( fnMoveForward, fnGoCntnt ) ) + rPam.GetPoint()->nContent.Assign( rPam.GetCntntNode(), 0 ); + } + else if( bDelFullPara ) + { + // der Pam wurde am Point( == Ende) um eins erhoeht, um einen + // Bereich fuers Undo zu haben. Der muss jetzt aber wieder entfernt + // werden!!! + rPam.End()->nNode--; + if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode ) + *rPam.GetMark() = *rPam.GetPoint(); + rDoc.DelFullPara( rPam ); + } + else + rDoc.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, fnGoCntnt ); + } + if( bDelFullPara ) + rDoc.DelFullPara( rPam ); + else + rDoc.DeleteAndJoin( rPam ); + rContext.m_bDeleteRepeated = true; +} + + +void SwUndoDelete::SetTableName(const String & rName) +{ + sTableName = rName; +} |