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( 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 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 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 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 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 572 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 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 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 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 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 1006 void SwUndoDelete::SetTableName(const String & rName) 1007 { 1008 sTableName = rName; 1009 } 1010