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