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