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