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