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