xref: /AOO41X/main/sw/source/core/undo/undel.cxx (revision 54628ca40d27d15cc98fe861da7fff7e60c2f7d6)
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