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