xref: /aoo41x/main/sw/source/core/undo/undel.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 #include <UndoDelete.hxx>
32 
33 #include <hintids.hxx>
34 #include <unotools/charclass.hxx>
35 #include <editeng/brkitem.hxx>
36 #include <fmtpdsc.hxx>
37 #include <frmfmt.hxx>
38 #include <fmtanchr.hxx>
39 #include <doc.hxx>
40 #include <UndoManager.hxx>
41 #include <swtable.hxx>
42 #include <swundo.hxx>			// fuer die UndoIds
43 #include <pam.hxx>
44 #include <ndtxt.hxx>
45 #include <UndoCore.hxx>
46 #include <rolbck.hxx>
47 #include <poolfmt.hxx>
48 #include <mvsave.hxx>
49 #include <redline.hxx>
50 #include <docary.hxx>
51 #include <sfx2/app.hxx>
52 
53 #include <fldbas.hxx>
54 #include <fmtfld.hxx>
55 #include <comcore.hrc> // #111827#
56 #include <undo.hrc>
57 
58 // #include <editeng/svxacorr.hxx>
59 // #include <comphelper/processfactory.hxx>
60 // #include <editeng/unolingu.hxx>
61 // #include <unotools/localedatawrapper.hxx>
62 
63 // using namespace comphelper;
64 
65 
66 // DELETE
67 /*  lcl_MakeAutoFrms has to call MakeFrms for objects bounded "AtChar" ( == AUTO ),
68     if the anchor frame has be moved via _MoveNodes(..) and DelFrms(..)
69 */
70 
71 void lcl_MakeAutoFrms( const SwSpzFrmFmts& rSpzArr, sal_uLong nMovedIndex )
72 {
73     if( rSpzArr.Count() )
74     {
75         SwFlyFrmFmt* pFmt;
76         const SwFmtAnchor* pAnchor;
77         for( sal_uInt16 n = 0; n < rSpzArr.Count(); ++n )
78         {
79             pFmt = (SwFlyFrmFmt*)rSpzArr[n];
80             pAnchor = &pFmt->GetAnchor();
81             if (pAnchor->GetAnchorId() == FLY_AT_CHAR)
82             {
83                 const SwPosition* pAPos = pAnchor->GetCntntAnchor();
84                 if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() )
85                     pFmt->MakeFrms();
86             }
87         }
88     }
89 }
90 
91 /*
92 SwUndoDelete has to perform a deletion and to record anything that is needed to restore the
93 situation before the deletion. Unfortunately a part of the deletion will be done after calling
94 this Ctor, this has to be kept in mind! In this Ctor only the complete paragraphs will be deleted,
95 the joining of the first and last paragraph of the selection will be handled outside this function.
96 Here are the main steps of the function:
97 1. Deletion/recording of content indizes of the selection: footnotes, fly frames and bookmarks
98 Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
99 2. If the paragraph where the selection ends, is the last content of a section so that this
100 section becomes empty when the paragraphs will be joined we have to do some smart actions ;-)
101 The paragraph will be moved outside the section and replaced by a dummy text node, the complete
102 section will be deleted in step 3. The difference between replacement dummy and original is
103 nReplacementDummy.
104 3. Moving complete selected nodes into the UndoArray. Before this happens the selection has to be
105 extended if there are sections which would become empty otherwise. BTW: sections will be moved into
106 the UndoArray if they are complete part of the selection. Sections starting or ending outside of the
107 selection will not be removed from the DocNodeArray even they got a "dummy"-copy in the UndoArray.
108 4. We have to anticipate the joining of the two paragraphs if the start paragraph is inside a
109 section and the end paragraph not. Then we have to move the paragraph into this section and to
110 record this in nSectDiff.
111 */
112 
113 SwUndoDelete::SwUndoDelete( SwPaM& rPam, sal_Bool bFullPara, sal_Bool bCalledByTblCpy )
114 	: SwUndo(UNDO_DELETE), SwUndRng( rPam ),
115 	pMvStt( 0 ), pSttStr(0), pEndStr(0), pRedlData(0), pRedlSaveData(0),
116     nNode(0), nNdDiff(0), nSectDiff(0), nReplaceDummy(0), nSetPos(0),
117 	bGroup( sal_False ), bBackSp( sal_False ), bJoinNext( sal_False ), bTblDelLastNd( sal_False ),
118     bDelFullPara( bFullPara ), bResetPgDesc( sal_False ), bResetPgBrk( sal_False ),
119     bFromTableCopy( bCalledByTblCpy )
120 {
121 	bDelFullPara = bFullPara; // This is set e.g. if an empty paragraph before a table is deleted
122 
123     bCacheComment = false;
124 
125 	SwDoc * pDoc = rPam.GetDoc();
126 
127 	if( !pDoc->IsIgnoreRedline() && pDoc->GetRedlineTbl().Count() )
128 	{
129 		pRedlSaveData = new SwRedlineSaveDatas;
130 		if( !FillSaveData( rPam, *pRedlSaveData ))
131 			delete pRedlSaveData, pRedlSaveData = 0;
132 	}
133 
134 	if( !pHistory )
135 		pHistory = new SwHistory;
136 
137 	// loesche erstmal alle Fussnoten
138 	const SwPosition *pStt = rPam.Start(),
139 					*pEnd = rPam.GetPoint() == pStt
140 						? rPam.GetMark()
141 						: rPam.GetPoint();
142 
143     // Step 1. deletion/record of content indizes
144 	if( bDelFullPara )
145 	{
146 		ASSERT( rPam.HasMark(), "PaM ohne Mark" );
147 		DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(),
148                         DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) );
149 
150         ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
151 		_DelBookmarks(pStt->nNode, pEnd->nNode);
152 	}
153 	else
154 		DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() );
155 
156 	nSetPos = pHistory ? pHistory->Count() : 0;
157 
158 	// wurde schon was geloescht ??
159 	nNdDiff = nSttNode - pStt->nNode.GetIndex();
160 
161 	bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
162 	bBackSp = !bFullPara && !bJoinNext;
163 
164 	SwTxtNode *pSttTxtNd = 0, *pEndTxtNd = 0;
165 	if( !bFullPara )
166 	{
167 		pSttTxtNd = pStt->nNode.GetNode().GetTxtNode();
168 		pEndTxtNd = nSttNode == nEndNode
169 					? pSttTxtNd
170 					: pEnd->nNode.GetNode().GetTxtNode();
171 	}
172 
173 	sal_Bool bMoveNds = *pStt == *pEnd      // noch ein Bereich vorhanden ??
174 				? sal_False
175 				: ( SaveCntnt( pStt, pEnd, pSttTxtNd, pEndTxtNd ) || bFromTableCopy );
176 
177 	if( pSttTxtNd && pEndTxtNd && pSttTxtNd != pEndTxtNd )
178 	{
179 		// zwei unterschiedliche TextNodes, also speicher noch die
180 		// TextFormatCollection fuers
181 		pHistory->Add( pSttTxtNd->GetTxtColl(),pStt->nNode.GetIndex(), ND_TEXTNODE );
182 		pHistory->Add( pEndTxtNd->GetTxtColl(),pEnd->nNode.GetIndex(), ND_TEXTNODE );
183 
184 		if( !bJoinNext )	 	// Selection von Unten nach Oben
185 		{
186 			// Beim JoinPrev() werden die AUTO-PageBreak's richtig
187 			// kopiert. Um diese beim Undo wieder herzustellen, muss das
188 			// Auto-PageBreak aus dem EndNode zurueckgesetzt werden.
189 			// - fuer die PageDesc, ColBreak dito !
190             if( pEndTxtNd->HasSwAttrSet() )
191 			{
192 				SwRegHistory aRegHist( *pEndTxtNd, pHistory );
193 				if( SFX_ITEM_SET == pEndTxtNd->GetpSwAttrSet()->GetItemState(
194 						RES_BREAK, sal_False ) )
195 					pEndTxtNd->ResetAttr( RES_BREAK );
196                 if( pEndTxtNd->HasSwAttrSet() &&
197 					SFX_ITEM_SET == pEndTxtNd->GetpSwAttrSet()->GetItemState(
198 						RES_PAGEDESC, sal_False ) )
199 					pEndTxtNd->ResetAttr( RES_PAGEDESC );
200 			}
201 		}
202 	}
203 
204 
205 	// verschiebe jetzt noch den PaM !!!
206 	// der SPoint steht am Anfang der SSelection
207 	if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTxtNd || pEndTxtNd ) )
208 		rPam.Exchange();
209 
210 	if( !pSttTxtNd && !pEndTxtNd )
211 		rPam.GetPoint()->nNode--;
212 	rPam.DeleteMark();			// der SPoint ist aus dem Bereich
213 
214 	if( !pEndTxtNd )
215 		nEndCntnt = 0;
216 	if( !pSttTxtNd )
217 		nSttCntnt = 0;
218 
219 	if( bMoveNds )		// sind noch Nodes zu verschieben ?
220     {
221         SwNodes& rNds = pDoc->GetUndoManager().GetUndoNodes();
222 		SwNodes& rDocNds = pDoc->GetNodes();
223 		SwNodeRange aRg( rDocNds, nSttNode - nNdDiff,
224 						 rDocNds, nEndNode - nNdDiff );
225 		if( !bFullPara && !pEndTxtNd &&
226 			&aRg.aEnd.GetNode() != &pDoc->GetNodes().GetEndOfContent() )
227         {
228             SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode();
229             if( pNode->GetIndex() >= nSttNode - nNdDiff )
230                 aRg.aEnd++; // Deletion of a complete table
231         }
232 		SwNode* pTmpNd;
233         // Step 2: Expand selection if necessary
234         if( bJoinNext || bFullPara )
235         {
236             // If all content of a section will be moved into Undo,
237             // the section itself should be moved complete.
238 			while( aRg.aEnd.GetIndex() + 2  < rDocNds.Count() &&
239 				( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() &&
240                 pTmpNd->StartOfSectionNode()->IsSectionNode() &&
241                 pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) )
242 				aRg.aEnd++;
243             nReplaceDummy = aRg.aEnd.GetIndex() + nNdDiff - nEndNode;
244             if( nReplaceDummy )
245             {   // The selection has been expanded, because
246                 aRg.aEnd++;
247                 if( pEndTxtNd )
248                 {
249                     // The end text node has to leave the (expanded) selection
250                     // The dummy is needed because _MoveNodes deletes empty sections
251                     ++nReplaceDummy;
252                     SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 );
253                     SwPosition aSplitPos( *pEndTxtNd );
254                     ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
255                     pDoc->SplitNode( aSplitPos, false );
256                     rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aEnd, sal_True );
257                     aRg.aEnd--;
258                 }
259                 else
260                     nReplaceDummy = 0;
261             }
262         }
263         if( bBackSp || bFullPara )
264         {
265             //See above, the selection has to expanded if there are "nearly empty" sections
266             // and a replacement dummy has to be set if needed.
267 			while( 1 < aRg.aStart.GetIndex() &&
268 				( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() &&
269 				pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) )
270 				aRg.aStart--;
271             if( pSttTxtNd )
272             {
273                 nReplaceDummy = nSttNode - nNdDiff - aRg.aStart.GetIndex();
274                 if( nReplaceDummy )
275                 {
276                     SwNodeRange aMvRg( *pSttTxtNd, 0, *pSttTxtNd, 1 );
277                     SwPosition aSplitPos( *pSttTxtNd );
278                     ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
279                     pDoc->SplitNode( aSplitPos, false );
280                     rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True );
281 					aRg.aStart--;
282                 }
283             }
284         }
285 
286         if( bFromTableCopy )
287         {
288             if( !pEndTxtNd )
289             {
290                 if( pSttTxtNd )
291                     aRg.aStart++;
292                 else if( !bFullPara && !aRg.aEnd.GetNode().IsCntntNode() )
293                     aRg.aEnd--;
294             }
295         }
296         else if( pSttTxtNd && ( pEndTxtNd || pSttTxtNd->GetTxt().Len() ) )
297             aRg.aStart++;
298 
299         // Step 3: Moving into UndoArray...
300 		nNode = rNds.GetEndOfContent().GetIndex();
301 		rDocNds._MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() ));
302 		pMvStt = new SwNodeIndex( rNds, nNode );
303 		nNode = rNds.GetEndOfContent().GetIndex() - nNode;		// Differenz merken !
304         if( pSttTxtNd && pEndTxtNd )
305         {
306             //Step 4: Moving around sections
307             nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
308             // nSect is the number of sections which starts(ends) between start and end node of the
309             // selection. The "loser" paragraph has to be moved into the section(s) of the
310             // "winner" paragraph
311             if( nSectDiff )
312             {
313                 if( bJoinNext )
314                 {
315                     SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 );
316                     rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True );
317                 }
318                 else
319                 {
320                     SwNodeRange aMvRg( *pSttTxtNd, 0, *pSttTxtNd, 1 );
321                     rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aEnd, sal_True );
322                 }
323             }
324         }
325         if( nSectDiff || nReplaceDummy )
326             lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(),
327                 bJoinNext ? pEndTxtNd->GetIndex() : pSttTxtNd->GetIndex() );
328 	}
329 	else
330 		nNode = 0;		// kein Node verschoben -> keine Differenz zum Ende
331 
332 	// wurden davor noch Nodes geloescht ?? (FootNotes haben ContentNodes!)
333 	if( !pSttTxtNd && !pEndTxtNd )
334 	{
335 		nNdDiff = nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1);
336 		rPam.Move( fnMoveForward, fnGoNode );
337 	}
338 	else
339     {
340 		nNdDiff = nSttNode;
341         if( nSectDiff && bBackSp )
342             nNdDiff += nSectDiff;
343         nNdDiff -= rPam.GetPoint()->nNode.GetIndex();
344     }
345 
346 	if( !rPam.GetNode()->IsCntntNode() )
347 		rPam.GetPoint()->nContent.Assign( 0, 0 );
348 
349 	// wird die History ueberhaupt benoetigt ??
350 	if( pHistory && !pHistory->Count() )
351 		DELETEZ( pHistory );
352 }
353 
354 sal_Bool SwUndoDelete::SaveCntnt( const SwPosition* pStt, const SwPosition* pEnd,
355 					SwTxtNode* pSttTxtNd, SwTxtNode* pEndTxtNd )
356 {
357 	sal_uLong nNdIdx = pStt->nNode.GetIndex();
358 	// 1 - kopiere den Anfang in den Start-String
359 	if( pSttTxtNd )
360 	{
361 		sal_Bool bOneNode = nSttNode == nEndNode;
362 		xub_StrLen nLen = bOneNode ? nEndCntnt - nSttCntnt
363 								: pSttTxtNd->GetTxt().Len() - nSttCntnt;
364 		SwRegHistory aRHst( *pSttTxtNd, pHistory );
365         // always save all text atttibutes because of possibly overlapping
366         // areas of on/off
367         pHistory->CopyAttr( pSttTxtNd->GetpSwpHints(), nNdIdx,
368                             0, pSttTxtNd->GetTxt().Len(), true );
369         if( !bOneNode && pSttTxtNd->HasSwAttrSet() )
370                 pHistory->CopyFmtAttr( *pSttTxtNd->GetpSwAttrSet(), nNdIdx );
371 
372 		// die Laenge kann sich veraendert haben (!!Felder!!)
373 		nLen = ( bOneNode ? pEnd->nContent.GetIndex() : pSttTxtNd->GetTxt().Len() )
374 				- pStt->nContent.GetIndex();
375 
376 
377 		// loesche jetzt noch den Text (alle Attribut-Aenderungen kommen in
378 		// die Undo-History
379 		pSttStr = (String*)new String( pSttTxtNd->GetTxt().Copy( nSttCntnt, nLen ));
380         pSttTxtNd->EraseText( pStt->nContent, nLen );
381 		if( pSttTxtNd->GetpSwpHints() )
382 			pSttTxtNd->GetpSwpHints()->DeRegister();
383 
384         // METADATA: store
385         bool emptied( pSttStr->Len() && !pSttTxtNd->Len() );
386         if (!bOneNode || emptied) // merging may overwrite xmlids...
387         {
388             m_pMetadataUndoStart = (emptied)
389                 ? pSttTxtNd->CreateUndoForDelete()
390                 : pSttTxtNd->CreateUndo();
391         }
392 
393 		if( bOneNode )
394 			return sal_False;           // keine Nodes mehr verschieben
395 	}
396 
397 
398 	// 2 - kopiere das Ende in den End-String
399 	if( pEndTxtNd )
400 	{
401 		SwIndex aEndIdx( pEndTxtNd );
402 		nNdIdx = pEnd->nNode.GetIndex();
403 		SwRegHistory aRHst( *pEndTxtNd, pHistory );
404 
405         // always save all text atttibutes because of possibly overlapping
406         // areas of on/off
407         pHistory->CopyAttr( pEndTxtNd->GetpSwpHints(), nNdIdx, 0,
408                             pEndTxtNd->GetTxt().Len(), true );
409 
410         if( pEndTxtNd->HasSwAttrSet() )
411             pHistory->CopyFmtAttr( *pEndTxtNd->GetpSwAttrSet(), nNdIdx );
412 
413 
414 		// loesche jetzt noch den Text (alle Attribut-Aenderungen kommen in
415 		// die Undo-History
416 		pEndStr = (String*)new String( pEndTxtNd->GetTxt().Copy( 0,
417 									pEnd->nContent.GetIndex() ));
418         pEndTxtNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() );
419 		if( pEndTxtNd->GetpSwpHints() )
420 			pEndTxtNd->GetpSwpHints()->DeRegister();
421 
422         // METADATA: store
423         bool emptied( pEndStr->Len() && !pEndTxtNd->Len() );
424 
425         m_pMetadataUndoEnd = (emptied)
426             ? pEndTxtNd->CreateUndoForDelete()
427             : pEndTxtNd->CreateUndo();
428     }
429 
430 	// sind es nur zwei Nodes, dann ist schon alles erledigt.
431 	if( ( pSttTxtNd || pEndTxtNd ) && nSttNode + 1 == nEndNode )
432 		return sal_False;           // keine Nodes mehr verschieben
433 
434 	return sal_True;                // verschiebe die dazwischen liegenden Nodes
435 }
436 
437 
438 sal_Bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam )
439 {
440 	// ist das Undo groesser als 1 Node ? (sprich: Start und EndString)
441 	if( pSttStr ? !pSttStr->Len() || pEndStr : sal_True )
442 		return sal_False;
443 
444 	// es kann nur das Loeschen von einzelnen char's zusammengefasst werden
445 	if( nSttNode != nEndNode || ( !bGroup && nSttCntnt+1 != nEndCntnt ))
446 		return sal_False;
447 
448 	const SwPosition *pStt = rDelPam.Start(),
449 					*pEnd = rDelPam.GetPoint() == pStt
450 						? rDelPam.GetMark()
451 						: rDelPam.GetPoint();
452 
453 	if( pStt->nNode != pEnd->nNode ||
454 		pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() ||
455 		pEnd->nNode != nSttNode )
456 		return sal_False;
457 
458 	// untercheide zwischen BackSpace und Delete. Es muss dann das
459 	// Undo-Array unterschiedlich aufgebaut werden !!
460 	if( pEnd->nContent == nSttCntnt )
461 	{
462 		if( bGroup && !bBackSp ) return sal_False;
463 		bBackSp = sal_True;
464 	}
465 	else if( pStt->nContent == nSttCntnt )
466 	{
467 		if( bGroup && bBackSp ) return sal_False;
468 		bBackSp = sal_False;
469 	}
470 	else
471 		return sal_False;
472 
473 	// sind die beiden Nodes (Nodes-/Undo-Array) ueberhaupt TextNodes?
474 	SwTxtNode * pDelTxtNd = pStt->nNode.GetNode().GetTxtNode();
475 	if( !pDelTxtNd ) return sal_False;
476 
477 	xub_StrLen nUChrPos = bBackSp ? 0 : pSttStr->Len()-1;
478 	sal_Unicode cDelChar = pDelTxtNd->GetTxt().GetChar( pStt->nContent.GetIndex() );
479 	CharClass& rCC = GetAppCharClass();
480 	if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) ||
481 		rCC.isLetterNumeric( String( cDelChar ), 0 ) !=
482 		rCC.isLetterNumeric( *pSttStr, nUChrPos ) )
483 		return sal_False;
484 
485 	{
486 		SwRedlineSaveDatas* pTmpSav = new SwRedlineSaveDatas;
487 		if( !FillSaveData( rDelPam, *pTmpSav, sal_False ))
488 			delete pTmpSav, pTmpSav = 0;
489 
490 		sal_Bool bOk = ( !pRedlSaveData && !pTmpSav ) ||
491 				   ( pRedlSaveData && pTmpSav &&
492 				SwUndo::CanRedlineGroup( *pRedlSaveData, *pTmpSav, bBackSp ));
493 		delete pTmpSav;
494 		if( !bOk )
495 			return sal_False;
496 
497         pDoc->DeleteRedline( rDelPam, false, USHRT_MAX );
498 	}
499 
500 	// Ok, die beiden 'Deletes' koennen zusammen gefasst werden, also
501 	// 'verschiebe' das enstprechende Zeichen
502 	if( bBackSp )
503 		nSttCntnt--;    // BackSpace: Zeichen in Array einfuegen !!
504 	else
505 	{
506 		nEndCntnt++;    // Delete: Zeichen am Ende anhaengen
507 		nUChrPos++;
508 	}
509 	pSttStr->Insert( cDelChar, nUChrPos );
510     pDelTxtNd->EraseText( pStt->nContent, 1 );
511 
512 	bGroup = sal_True;
513 	return sal_True;
514 }
515 
516 
517 
518 SwUndoDelete::~SwUndoDelete()
519 {
520 	delete pSttStr;
521 	delete pEndStr;
522 	if( pMvStt )		// loesche noch den Bereich aus dem UndoNodes Array
523 	{
524 		// Insert speichert den Inhalt in der IconSection
525 		pMvStt->GetNode().GetNodes().Delete( *pMvStt, nNode );
526 		delete pMvStt;
527 	}
528 	delete pRedlData;
529 	delete pRedlSaveData;
530 }
531 
532 static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory)
533 {
534     SwRewriter aRewriter;
535 
536     bool bDone = false;
537 
538     for ( sal_uInt16 n = 0; n < rHistory.Count(); n++)
539     {
540         String aDescr = rHistory[n]->GetDescription();
541 
542         if (aDescr.Len() > 0)
543         {
544             aRewriter.AddRule(UNDO_ARG2, aDescr);
545 
546             bDone = true;
547             break;
548         }
549     }
550 
551     if (! bDone)
552     {
553         aRewriter.AddRule(UNDO_ARG2, SW_RES(STR_FIELD));
554     }
555 
556     return aRewriter;
557 }
558 
559 SwRewriter SwUndoDelete::GetRewriter() const
560 {
561     SwRewriter aResult;
562     String * pStr = NULL;
563 
564     if (nNode != 0)
565     {
566         if (sTableName.Len() > 0)
567         {
568 
569             SwRewriter aRewriter;
570             aRewriter.AddRule(UNDO_ARG1, SW_RES(STR_START_QUOTE));
571             aRewriter.AddRule(UNDO_ARG2, sTableName);
572             aRewriter.AddRule(UNDO_ARG3, SW_RES(STR_END_QUOTE));
573 
574             String sTmp = aRewriter.Apply(SW_RES(STR_TABLE_NAME));
575             aResult.AddRule(UNDO_ARG1, sTmp);
576         }
577         else
578             aResult.AddRule(UNDO_ARG1, String(SW_RES(STR_PARAGRAPHS)));
579     }
580     else
581     {
582         String aStr;
583 
584         if (pSttStr != NULL && pEndStr != NULL && pSttStr->Len() == 0 &&
585             pEndStr->Len() == 0)
586         {
587             aStr = SW_RES(STR_PARAGRAPH_UNDO);
588         }
589         else
590         {
591             if (pSttStr != NULL)
592                 pStr = pSttStr;
593             else if (pEndStr != NULL)
594                 pStr = pEndStr;
595 
596             if (pStr != NULL)
597             {
598                 aStr = DenoteSpecialCharacters(*pStr);
599             }
600             else
601             {
602                 aStr = UNDO_ARG2;
603             }
604         }
605 
606         aStr = ShortenString(aStr, nUndoStringLength, String(SW_RES(STR_LDOTS)));
607         if (pHistory)
608         {
609             SwRewriter aRewriter = lcl_RewriterFromHistory(*pHistory);
610             aStr = aRewriter.Apply(aStr);
611         }
612 
613         aResult.AddRule(UNDO_ARG1, aStr);
614     }
615 
616     return aResult;
617 }
618 
619 // Every object, anchored "AtCntnt" will be reanchored at rPos
620 void lcl_ReAnchorAtCntntFlyFrames( const SwSpzFrmFmts& rSpzArr, SwPosition &rPos, sal_uLong nOldIdx )
621 {
622     if( rSpzArr.Count() )
623     {
624         SwFlyFrmFmt* pFmt;
625         const SwFmtAnchor* pAnchor;
626         const SwPosition* pAPos;
627         for( sal_uInt16 n = 0; n < rSpzArr.Count(); ++n )
628         {
629             pFmt = (SwFlyFrmFmt*)rSpzArr[n];
630             pAnchor = &pFmt->GetAnchor();
631             if (pAnchor->GetAnchorId() == FLY_AT_PARA)
632             {
633                 pAPos =  pAnchor->GetCntntAnchor();
634                 if( pAPos && nOldIdx == pAPos->nNode.GetIndex() )
635                 {
636                     SwFmtAnchor aAnch( *pAnchor );
637                     aAnch.SetAnchor( &rPos );
638                     pFmt->SetFmtAttr( aAnch );
639                 }
640             }
641         }
642     }
643 }
644 
645 void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext)
646 {
647     SwDoc *const pDoc = & rContext.GetDoc();
648 
649 	sal_uLong nCalcStt = nSttNode - nNdDiff;
650 
651     if( nSectDiff && bBackSp )
652         nCalcStt += nSectDiff;
653 
654 	SwNodeIndex aIdx( pDoc->GetNodes(), nCalcStt );
655 	SwNode* pInsNd = &aIdx.GetNode();
656 
657 	{		// Block, damit der SwPosition beim loeschen vom Node
658 			// abgemeldet ist
659 		SwPosition aPos( aIdx );
660 		if( !bDelFullPara )
661 		{
662 			if( pInsNd->IsTableNode() )
663 			{
664 				pInsNd = pDoc->GetNodes().MakeTxtNode( aIdx,
665 						(SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() );
666 				aIdx--;
667 				aPos.nNode = aIdx;
668 				aPos.nContent.Assign( pInsNd->GetCntntNode(), nSttCntnt );
669 			}
670 			else
671 			{
672 				if( pInsNd->IsCntntNode() )
673 					aPos.nContent.Assign( (SwCntntNode*)pInsNd, nSttCntnt );
674 				if( !bTblDelLastNd )
675 					pInsNd = 0;			// Node nicht loeschen !!
676 			}
677 		}
678 		else
679 			pInsNd = 0;			// Node nicht loeschen !!
680 
681 		sal_Bool bNodeMove = 0 != nNode;
682 
683 		if( pEndStr )
684 		{
685 			// alle Attribute verwerfen, wurden alle gespeichert!
686 			SwTxtNode* pTxtNd = aPos.nNode.GetNode().GetTxtNode();
687 
688             if( pTxtNd && pTxtNd->HasSwAttrSet() )
689                 pTxtNd->ResetAllAttr();
690 
691             if( pTxtNd && pTxtNd->GetpSwpHints() )
692                 pTxtNd->ClearSwpHintsArr( true );
693 
694 			if( pSttStr && !bFromTableCopy )
695 			{
696                 sal_uLong nOldIdx = aPos.nNode.GetIndex();
697                 pDoc->SplitNode( aPos, false );
698                 // After the split all objects are anchored at the first paragraph,
699                 // but the pHistory of the fly frame formats relies on anchoring at
700                 // the start of the selection => selection backwards needs a correction.
701                 if( bBackSp )
702                     lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx );
703 				pTxtNd = aPos.nNode.GetNode().GetTxtNode();
704 			}
705             if( pTxtNd )
706             {
707                 pTxtNd->InsertText( *pEndStr, aPos.nContent,
708                         IDocumentContentOperations::INS_NOHINTEXPAND );
709                 // METADATA: restore
710                 pTxtNd->RestoreMetadata(m_pMetadataUndoEnd);
711             }
712 		}
713 		else if( pSttStr && bNodeMove )
714 		{
715 			SwTxtNode * pNd = aPos.nNode.GetNode().GetTxtNode();
716 			if( pNd )
717 			{
718 				if( nSttCntnt < pNd->GetTxt().Len() )
719                 {
720                     sal_uLong nOldIdx = aPos.nNode.GetIndex();
721                     pDoc->SplitNode( aPos, false );
722                     if( bBackSp )
723                         lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx );
724                 }
725                 else
726                     aPos.nNode++;
727 			}
728 		}
729         SwNode* pMovedNode = NULL;
730         if( nSectDiff )
731         {
732             sal_uLong nMoveIndex = aPos.nNode.GetIndex();
733             int nDiff = 0;
734             if( bJoinNext )
735             {
736                 nMoveIndex += nSectDiff + 1;
737                 pMovedNode = &aPos.nNode.GetNode();
738             }
739             else
740             {
741                 nMoveIndex -= nSectDiff + 1;
742                 ++nDiff;
743             }
744             SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex );
745             SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff );
746             aPos.nNode--;
747             if( !bJoinNext )
748                 pMovedNode = &aPos.nNode.GetNode();
749             pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True );
750             aPos.nNode++;
751         }
752 
753 		if( bNodeMove )
754 		{
755 			SwNodeRange aRange( *pMvStt, 0, *pMvStt, nNode );
756 			SwNodeIndex aCopyIndex( aPos.nNode, -1 );
757             pDoc->GetUndoManager().GetUndoNodes()._Copy( aRange, aPos.nNode );
758 
759             if( nReplaceDummy )
760             {
761                 sal_uLong nMoveIndex;
762                 if( bJoinNext )
763                 {
764                     nMoveIndex = nEndNode - nNdDiff;
765                     aPos.nNode = nMoveIndex + nReplaceDummy;
766                 }
767                 else
768                 {
769                     aPos = SwPosition( aCopyIndex );
770                     nMoveIndex = aPos.nNode.GetIndex() + nReplaceDummy + 1;
771                 }
772                 SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex );
773                 SwNodeRange aRg( aPos.nNode, 0, aPos.nNode, 1 );
774                 pMovedNode = &aPos.nNode.GetNode();
775                 pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True );
776                 pDoc->GetNodes().Delete( aMvIdx, 1 );
777             }
778 		}
779 
780         if( pMovedNode )
781             lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(), pMovedNode->GetIndex() );
782 
783 		if( pSttStr )
784 		{
785             aPos.nNode = nSttNode - nNdDiff + ( bJoinNext ? 0 : nReplaceDummy );
786 			SwTxtNode * pTxtNd = aPos.nNode.GetNode().GetTxtNode();
787 			// wenn mehr als ein Node geloescht wurde, dann wurden auch
788 			// alle "Node"-Attribute gespeichert
789 
790             if (pTxtNd != NULL)
791             {
792                 if( pTxtNd->HasSwAttrSet() && bNodeMove && !pEndStr )
793                     pTxtNd->ResetAllAttr();
794 
795                 if( pTxtNd->GetpSwpHints() )
796                     pTxtNd->ClearSwpHintsArr( true );
797 
798                 // SectionNode-Modus und von oben nach unten selektiert:
799                 //	-> im StartNode steht noch der Rest vom Join => loeschen
800                 aPos.nContent.Assign( pTxtNd, nSttCntnt );
801                 pTxtNd->InsertText( *pSttStr, aPos.nContent,
802                         IDocumentContentOperations::INS_NOHINTEXPAND );
803                 // METADATA: restore
804                 pTxtNd->RestoreMetadata(m_pMetadataUndoStart);
805             }
806 		}
807 
808 		if( pHistory )
809         {
810             pHistory->TmpRollback( pDoc, nSetPos, false );
811 			if( nSetPos )		// es gab Fussnoten/FlyFrames
812 			{
813 				// gibts ausser diesen noch andere ?
814 				if( nSetPos < pHistory->Count() )
815 				{
816 					// dann sicher die Attribute anderen Attribute
817 					SwHistory aHstr;
818 					aHstr.Move( 0, pHistory, nSetPos );
819 					pHistory->Rollback( pDoc );
820 					pHistory->Move( 0, &aHstr );
821 				}
822 				else
823 				{
824 					pHistory->Rollback( pDoc );
825 					DELETEZ( pHistory );
826 				}
827 			}
828 		}
829 
830 		if( bResetPgDesc || bResetPgBrk )
831 		{
832             sal_uInt16 nStt = static_cast<sal_uInt16>( bResetPgDesc ? RES_PAGEDESC : RES_BREAK );
833             sal_uInt16 nEnd = static_cast<sal_uInt16>( bResetPgBrk ? RES_BREAK : RES_PAGEDESC );
834 
835 			SwNode* pNode = pDoc->GetNodes()[ nEndNode + 1 ];
836 			if( pNode->IsCntntNode() )
837 				((SwCntntNode*)pNode)->ResetAttr( nStt, nEnd );
838 			else if( pNode->IsTableNode() )
839                 ((SwTableNode*)pNode)->GetTable().GetFrmFmt()->ResetFmtAttr( nStt, nEnd );
840 		}
841 	}
842 	// den temp. eingefuegten Node noch loeschen !!
843 	if( pInsNd )
844 		pDoc->GetNodes().Delete( aIdx, 1 );
845 	if( pRedlSaveData )
846 		SetSaveData( *pDoc, *pRedlSaveData );
847 
848     AddUndoRedoPaM(rContext, true);
849 }
850 
851 void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext)
852 {
853     SwPaM & rPam = AddUndoRedoPaM(rContext);
854     SwDoc& rDoc = *rPam.GetDoc();
855 
856     if( pRedlSaveData )
857     {
858         bool bSuccess = FillSaveData(rPam, *pRedlSaveData, sal_True);
859         OSL_ENSURE(bSuccess,
860             "SwUndoDelete::Redo: used to have redline data, but now none?");
861         if (!bSuccess)
862         {
863             delete pRedlSaveData, pRedlSaveData = 0;
864         }
865     }
866 
867     if( !bDelFullPara )
868     {
869         SwUndRng aTmpRng( rPam );
870         RemoveIdxFromRange( rPam, sal_False );
871         aTmpRng.SetPaM( rPam );
872 
873         if( !bJoinNext )            // Dann Selektion von unten nach oben
874             rPam.Exchange();        // wieder herstellen!
875     }
876 
877     if( pHistory )      // wurden Attribute gesichert ?
878     {
879         pHistory->SetTmpEnd( pHistory->Count() );
880         SwHistory aHstr;
881         aHstr.Move( 0, pHistory );
882 
883         if( bDelFullPara )
884         {
885             ASSERT( rPam.HasMark(), "PaM ohne Mark" );
886             DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(),
887                             DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) );
888 
889             _DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
890         }
891         else
892             DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() );
893         nSetPos = pHistory ? pHistory->Count() : 0;
894 
895         pHistory->Move( nSetPos, &aHstr );
896     }
897     else
898     {
899         if( bDelFullPara )
900         {
901             ASSERT( rPam.HasMark(), "PaM ohne Mark" );
902             DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(),
903                             DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) );
904 
905             _DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode );
906         }
907         else
908             DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() );
909         nSetPos = pHistory ? pHistory->Count() : 0;
910     }
911 
912     if( !pSttStr && !pEndStr )
913     {
914         SwNodeIndex aSttIdx = ( bDelFullPara || bJoinNext )
915                                     ? rPam.GetMark()->nNode
916                                     : rPam.GetPoint()->nNode;
917         SwTableNode* pTblNd = aSttIdx.GetNode().GetTableNode();
918         if( pTblNd )
919         {
920             if( bTblDelLastNd )
921             {
922                 // dann am Ende wieder einen Node einfuegen
923                 const SwNodeIndex aTmpIdx( *pTblNd->EndOfSectionNode(), 1 );
924                 rDoc.GetNodes().MakeTxtNode( aTmpIdx,
925                         rDoc.GetTxtCollFromPool( RES_POOLCOLL_STANDARD ) );
926             }
927 
928             SwCntntNode* pNextNd = rDoc.GetNodes()[
929                     pTblNd->EndOfSectionIndex()+1 ]->GetCntntNode();
930             if( pNextNd )
931             {
932                 SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt();
933 
934                 const SfxPoolItem *pItem;
935                 if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_PAGEDESC,
936                     sal_False, &pItem ) )
937                     pNextNd->SetAttr( *pItem );
938 
939                 if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_BREAK,
940                     sal_False, &pItem ) )
941                     pNextNd->SetAttr( *pItem );
942             }
943             pTblNd->DelFrms();
944         }
945 
946         rPam.SetMark();
947         rPam.DeleteMark();
948 
949         rDoc.GetNodes().Delete( aSttIdx, nEndNode - nSttNode );
950 
951         // setze den Cursor immer in einen ContentNode !!
952         if( !rPam.Move( fnMoveBackward, fnGoCntnt ) &&
953             !rPam.Move( fnMoveForward, fnGoCntnt ) )
954             rPam.GetPoint()->nContent.Assign( rPam.GetCntntNode(), 0 );
955     }
956     else if( bDelFullPara )
957     {
958         // der Pam wurde am Point( == Ende) um eins erhoeht, um einen
959         // Bereich fuers Undo zu haben. Der muss jetzt aber wieder entfernt
960         // werden!!!
961         rPam.End()->nNode--;
962         if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
963             *rPam.GetMark() = *rPam.GetPoint();
964         rDoc.DelFullPara( rPam );
965     }
966     else
967         rDoc.DeleteAndJoin( rPam );
968 }
969 
970 void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext)
971 {
972     // this action does not seem idempotent,
973     // so make sure it is only executed once on repeat
974     if (rContext.m_bDeleteRepeated)
975 		return;
976 
977     SwPaM & rPam = rContext.GetRepeatPaM();
978 	SwDoc& rDoc = *rPam.GetDoc();
979     ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
980 	if( !rPam.HasMark() )
981 	{
982 		rPam.SetMark();
983 		rPam.Move( fnMoveForward, fnGoCntnt );
984 	}
985 	if( bDelFullPara )
986 		rDoc.DelFullPara( rPam );
987 	else
988 		rDoc.DeleteAndJoin( rPam );
989     rContext.m_bDeleteRepeated = true;
990 }
991 
992 
993 void SwUndoDelete::SetTableName(const String & rName)
994 {
995     sTableName = rName;
996 }
997