blob: f1733f7b11796d402650d76dd31ee466f249ad52 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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;
}