xref: /trunk/main/sw/source/core/undo/undel.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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