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 <UndoOverwrite.hxx> 28 29 #include <tools/resid.hxx> 30 31 #include <unotools/charclass.hxx> 32 #include <unotools/transliterationwrapper.hxx> 33 34 #include <comphelper/processfactory.hxx> 35 36 #include <doc.hxx> 37 #include <IDocumentUndoRedo.hxx> 38 #include <IShellCursorSupplier.hxx> 39 #include <swundo.hxx> // fuer die UndoIds 40 #include <pam.hxx> 41 #include <ndtxt.hxx> 42 #include <UndoCore.hxx> 43 #include <rolbck.hxx> 44 #include <acorrect.hxx> 45 #include <docary.hxx> 46 47 #include <comcore.hrc> // #111827# 48 #include <undo.hrc> 49 50 using namespace ::com::sun::star; 51 using namespace ::com::sun::star::i18n; 52 using namespace ::com::sun::star::uno; 53 54 55 //------------------------------------------------------------ 56 57 // OVERWRITE 58 59 60 SwUndoOverwrite::SwUndoOverwrite( SwDoc* pDoc, SwPosition& rPos, 61 sal_Unicode cIns ) 62 : SwUndo(UNDO_OVERWRITE), 63 pRedlSaveData( 0 ), bGroup( sal_False ) 64 { 65 if( !pDoc->IsIgnoreRedline() && pDoc->GetRedlineTbl().Count() ) 66 { 67 SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), 68 rPos.nNode, rPos.nContent.GetIndex()+1 ); 69 pRedlSaveData = new SwRedlineSaveDatas; 70 if( !FillSaveData( aPam, *pRedlSaveData, sal_False )) 71 delete pRedlSaveData, pRedlSaveData = 0; 72 } 73 74 nSttNode = rPos.nNode.GetIndex(); 75 nSttCntnt = rPos.nContent.GetIndex(); 76 77 SwTxtNode* pTxtNd = rPos.nNode.GetNode().GetTxtNode(); 78 ASSERT( pTxtNd, "Overwrite nicht im TextNode?" ); 79 80 bInsChar = sal_True; 81 xub_StrLen nTxtNdLen = pTxtNd->GetTxt().Len(); 82 if( nSttCntnt < nTxtNdLen ) // kein reines Einfuegen ? 83 { 84 aDelStr.Insert( pTxtNd->GetTxt().GetChar( nSttCntnt ) ); 85 if( !pHistory ) 86 pHistory = new SwHistory; 87 SwRegHistory aRHst( *pTxtNd, pHistory ); 88 pHistory->CopyAttr( pTxtNd->GetpSwpHints(), nSttNode, 0, 89 nTxtNdLen, false ); 90 rPos.nContent++; 91 bInsChar = sal_False; 92 } 93 94 sal_Bool bOldExpFlg = pTxtNd->IsIgnoreDontExpand(); 95 pTxtNd->SetIgnoreDontExpand( sal_True ); 96 97 pTxtNd->InsertText( cIns, rPos.nContent, 98 IDocumentContentOperations::INS_EMPTYEXPAND ); 99 aInsStr.Insert( cIns ); 100 101 if( !bInsChar ) 102 { 103 const SwIndex aTmpIndex( rPos.nContent, -2 ); 104 pTxtNd->EraseText( aTmpIndex, 1 ); 105 } 106 pTxtNd->SetIgnoreDontExpand( bOldExpFlg ); 107 108 bCacheComment = false; 109 } 110 111 SwUndoOverwrite::~SwUndoOverwrite() 112 { 113 delete pRedlSaveData; 114 } 115 116 sal_Bool SwUndoOverwrite::CanGrouping( SwDoc* pDoc, SwPosition& rPos, 117 sal_Unicode cIns ) 118 { 119 /// ?? was ist mit nur eingefuegten Charaktern ??? 120 121 // es kann nur das Loeschen von einzelnen char's zusammengefasst werden 122 if( rPos.nNode != nSttNode || !aInsStr.Len() || 123 ( !bGroup && aInsStr.Len() != 1 )) 124 return sal_False; 125 126 // ist der Node ueberhaupt ein TextNode? 127 SwTxtNode * pDelTxtNd = rPos.nNode.GetNode().GetTxtNode(); 128 if( !pDelTxtNd || 129 ( pDelTxtNd->GetTxt().Len() != rPos.nContent.GetIndex() && 130 rPos.nContent.GetIndex() != ( nSttCntnt + aInsStr.Len() ))) 131 return sal_False; 132 133 CharClass& rCC = GetAppCharClass(); 134 135 // befrage das einzufuegende Charakter 136 if (( CH_TXTATR_BREAKWORD == cIns || CH_TXTATR_INWORD == cIns ) || 137 rCC.isLetterNumeric( String( cIns ), 0 ) != 138 rCC.isLetterNumeric( aInsStr, aInsStr.Len()-1 ) ) 139 return sal_False; 140 141 { 142 SwRedlineSaveDatas* pTmpSav = new SwRedlineSaveDatas; 143 SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), 144 rPos.nNode, rPos.nContent.GetIndex()+1 ); 145 146 if( !FillSaveData( aPam, *pTmpSav, sal_False )) 147 delete pTmpSav, pTmpSav = 0; 148 149 sal_Bool bOk = ( !pRedlSaveData && !pTmpSav ) || 150 ( pRedlSaveData && pTmpSav && 151 SwUndo::CanRedlineGroup( *pRedlSaveData, *pTmpSav, 152 nSttCntnt > rPos.nContent.GetIndex() )); 153 delete pTmpSav; 154 if( !bOk ) 155 return sal_False; 156 157 pDoc->DeleteRedline( aPam, false, USHRT_MAX ); 158 } 159 160 // Ok, die beiden 'Overwrites' koennen zusammen gefasst werden, also 161 // 'verschiebe' das enstprechende Zeichen 162 if( !bInsChar ) 163 { 164 if( rPos.nContent.GetIndex() < pDelTxtNd->GetTxt().Len() ) 165 { 166 aDelStr.Insert( pDelTxtNd->GetTxt().GetChar(rPos.nContent.GetIndex()) ); 167 rPos.nContent++; 168 } 169 else 170 bInsChar = sal_True; 171 } 172 173 sal_Bool bOldExpFlg = pDelTxtNd->IsIgnoreDontExpand(); 174 pDelTxtNd->SetIgnoreDontExpand( sal_True ); 175 176 pDelTxtNd->InsertText( cIns, rPos.nContent, 177 IDocumentContentOperations::INS_EMPTYEXPAND ); 178 aInsStr.Insert( cIns ); 179 180 if( !bInsChar ) 181 { 182 const SwIndex aTmpIndex( rPos.nContent, -2 ); 183 pDelTxtNd->EraseText( aTmpIndex, 1 ); 184 } 185 pDelTxtNd->SetIgnoreDontExpand( bOldExpFlg ); 186 187 bGroup = sal_True; 188 return sal_True; 189 } 190 191 192 193 194 195 void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext) 196 { 197 SwDoc *const pDoc = & rContext.GetDoc(); 198 SwPaM *const pAktPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); 199 200 pAktPam->DeleteMark(); 201 pAktPam->GetPoint()->nNode = nSttNode; 202 SwTxtNode* pTxtNd = pAktPam->GetNode()->GetTxtNode(); 203 ASSERT( pTxtNd, "Overwrite nicht im TextNode?" ); 204 SwIndex& rIdx = pAktPam->GetPoint()->nContent; 205 rIdx.Assign( pTxtNd, nSttCntnt ); 206 207 SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord(); 208 if( pACEWord ) 209 { 210 if( 1 == aInsStr.Len() && 1 == aDelStr.Len() ) 211 pACEWord->CheckChar( *pAktPam->GetPoint(), aDelStr.GetChar( 0 ) ); 212 pDoc->SetAutoCorrExceptWord( 0 ); 213 } 214 215 // wurde nicht nur ueberschieben sondern auch geinsertet, so loesche 216 // den Ueberhang 217 if( aInsStr.Len() > aDelStr.Len() ) 218 { 219 rIdx += aDelStr.Len(); 220 pTxtNd->EraseText( rIdx, aInsStr.Len() - aDelStr.Len() ); 221 rIdx = nSttCntnt; 222 } 223 224 if( aDelStr.Len() ) 225 { 226 String aTmpStr( '1' ); 227 sal_Unicode* pTmpStr = aTmpStr.GetBufferAccess(); 228 229 sal_Bool bOldExpFlg = pTxtNd->IsIgnoreDontExpand(); 230 pTxtNd->SetIgnoreDontExpand( sal_True ); 231 232 rIdx++; 233 for( xub_StrLen n = 0; n < aDelStr.Len(); n++ ) 234 { 235 // einzeln, damit die Attribute stehen bleiben !!! 236 *pTmpStr = aDelStr.GetChar( n ); 237 pTxtNd->InsertText( aTmpStr, rIdx /*???, SETATTR_NOTXTATRCHR*/ ); 238 rIdx -= 2; 239 pTxtNd->EraseText( rIdx, 1 ); 240 rIdx += 2; 241 } 242 pTxtNd->SetIgnoreDontExpand( bOldExpFlg ); 243 rIdx--; 244 } 245 if( pHistory ) 246 { 247 if( pTxtNd->GetpSwpHints() ) 248 pTxtNd->ClearSwpHintsArr( false ); 249 pHistory->TmpRollback( pDoc, 0, false ); 250 } 251 252 if( pAktPam->GetMark()->nContent.GetIndex() != nSttCntnt ) 253 { 254 pAktPam->SetMark(); 255 pAktPam->GetMark()->nContent = nSttCntnt; 256 } 257 258 if( pRedlSaveData ) 259 SetSaveData( *pDoc, *pRedlSaveData ); 260 } 261 262 void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext) 263 { 264 SwPaM *const pAktPam = & rContext.GetRepeatPaM(); 265 if( !aInsStr.Len() || pAktPam->HasMark() ) 266 return; 267 268 SwDoc & rDoc = rContext.GetDoc(); 269 270 { 271 ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); 272 rDoc.Overwrite(*pAktPam, aInsStr.GetChar(0)); 273 } 274 for( xub_StrLen n = 1; n < aInsStr.Len(); ++n ) 275 rDoc.Overwrite( *pAktPam, aInsStr.GetChar( n ) ); 276 } 277 278 void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext) 279 { 280 SwDoc *const pDoc = & rContext.GetDoc(); 281 SwPaM *const pAktPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); 282 283 pAktPam->DeleteMark(); 284 pAktPam->GetPoint()->nNode = nSttNode; 285 SwTxtNode* pTxtNd = pAktPam->GetNode()->GetTxtNode(); 286 ASSERT( pTxtNd, "Overwrite nicht im TextNode?" ); 287 SwIndex& rIdx = pAktPam->GetPoint()->nContent; 288 289 if( pRedlSaveData ) 290 { 291 rIdx.Assign( pTxtNd, nSttCntnt ); 292 pAktPam->SetMark(); 293 pAktPam->GetMark()->nContent += aInsStr.Len(); 294 pDoc->DeleteRedline( *pAktPam, false, USHRT_MAX ); 295 pAktPam->DeleteMark(); 296 } 297 rIdx.Assign( pTxtNd, aDelStr.Len() ? nSttCntnt+1 : nSttCntnt ); 298 299 sal_Bool bOldExpFlg = pTxtNd->IsIgnoreDontExpand(); 300 pTxtNd->SetIgnoreDontExpand( sal_True ); 301 302 for( xub_StrLen n = 0; n < aInsStr.Len(); n++ ) 303 { 304 // einzeln, damit die Attribute stehen bleiben !!! 305 pTxtNd->InsertText( aInsStr.GetChar( n ), rIdx, 306 IDocumentContentOperations::INS_EMPTYEXPAND ); 307 if( n < aDelStr.Len() ) 308 { 309 rIdx -= 2; 310 pTxtNd->EraseText( rIdx, 1 ); 311 rIdx += n+1 < aDelStr.Len() ? 2 : 1; 312 } 313 } 314 pTxtNd->SetIgnoreDontExpand( bOldExpFlg ); 315 316 // alte Anfangs-Position vom UndoNodes-Array zurueckholen 317 if( pHistory ) 318 pHistory->SetTmpEnd( pHistory->Count() ); 319 if( pAktPam->GetMark()->nContent.GetIndex() != nSttCntnt ) 320 { 321 pAktPam->SetMark(); 322 pAktPam->GetMark()->nContent = nSttCntnt; 323 } 324 } 325 326 SwRewriter SwUndoOverwrite::GetRewriter() const 327 { 328 SwRewriter aResult; 329 330 String aString; 331 332 aString += String(SW_RES(STR_START_QUOTE)); 333 aString += ShortenString(aInsStr, nUndoStringLength, 334 String(SW_RES(STR_LDOTS))); 335 aString += String(SW_RES(STR_END_QUOTE)); 336 337 aResult.AddRule(UNDO_ARG1, aString); 338 339 return aResult; 340 } 341 342 //------------------------------------------------------------ 343 344 struct _UndoTransliterate_Data 345 { 346 String sText; 347 SwHistory* pHistory; 348 Sequence< sal_Int32 >* pOffsets; 349 sal_uLong nNdIdx; 350 xub_StrLen nStart, nLen; 351 352 _UndoTransliterate_Data( sal_uLong nNd, xub_StrLen nStt, xub_StrLen nStrLen, const String& rTxt ) 353 : sText( rTxt ), pHistory( 0 ), pOffsets( 0 ), 354 nNdIdx( nNd ), nStart( nStt ), nLen( nStrLen ) 355 {} 356 ~_UndoTransliterate_Data() { delete pOffsets; delete pHistory; } 357 358 void SetChangeAtNode( SwDoc& rDoc ); 359 }; 360 361 SwUndoTransliterate::SwUndoTransliterate( 362 const SwPaM& rPam, 363 const utl::TransliterationWrapper& rTrans ) 364 : SwUndo( UNDO_TRANSLITERATE ), SwUndRng( rPam ), nType( rTrans.getType() ) 365 { 366 } 367 368 SwUndoTransliterate::~SwUndoTransliterate() 369 { 370 for (size_t i = 0; i < aChanges.size(); ++i) 371 delete aChanges[i]; 372 } 373 374 void SwUndoTransliterate::UndoImpl(::sw::UndoRedoContext & rContext) 375 { 376 SwDoc & rDoc = rContext.GetDoc(); 377 378 // since the changes were added to the vector from the end of the string/node towards 379 // the start, we need to revert them from the start towards the end now to keep the 380 // offset information of the undo data in sync with the changing text. 381 // Thus we need to iterate from the end of the vector to the start 382 for (sal_Int32 i = aChanges.size() - 1; i >= 0; --i) 383 aChanges[i]->SetChangeAtNode( rDoc ); 384 385 AddUndoRedoPaM(rContext, true); 386 } 387 388 void SwUndoTransliterate::RedoImpl(::sw::UndoRedoContext & rContext) 389 { 390 SwPaM & rPam( AddUndoRedoPaM(rContext) ); 391 DoTransliterate(rContext.GetDoc(), rPam); 392 } 393 394 void SwUndoTransliterate::RepeatImpl(::sw::RepeatContext & rContext) 395 { 396 DoTransliterate(rContext.GetDoc(), rContext.GetRepeatPaM()); 397 } 398 399 void SwUndoTransliterate::DoTransliterate(SwDoc & rDoc, SwPaM & rPam) 400 { 401 utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), nType ); 402 rDoc.TransliterateText( rPam, aTrans ); 403 } 404 405 void SwUndoTransliterate::AddChanges( SwTxtNode& rTNd, 406 xub_StrLen nStart, xub_StrLen nLen, 407 uno::Sequence <sal_Int32>& rOffsets ) 408 { 409 long nOffsLen = rOffsets.getLength(); 410 _UndoTransliterate_Data* pNew = new _UndoTransliterate_Data( 411 rTNd.GetIndex(), nStart, (xub_StrLen)nOffsLen, 412 rTNd.GetTxt().Copy( nStart, nLen )); 413 414 aChanges.push_back( pNew ); 415 416 const sal_Int32* pOffsets = rOffsets.getConstArray(); 417 // where did we need less memory ? 418 const sal_Int32* p = pOffsets; 419 for( long n = 0; n < nOffsLen; ++n, ++p ) 420 if( *p != ( nStart + n )) 421 { 422 // create the Offset array 423 pNew->pOffsets = new Sequence <sal_Int32> ( nLen ); 424 sal_Int32* pIdx = pNew->pOffsets->getArray(); 425 p = pOffsets; 426 long nMyOff, nNewVal = nStart; 427 for( n = 0, nMyOff = nStart; n < nOffsLen; ++p, ++n, ++nMyOff ) 428 { 429 if( *p < nMyOff ) 430 { 431 // something is deleted 432 nMyOff = *p; 433 *(pIdx-1) = nNewVal++; 434 } 435 else if( *p > nMyOff ) 436 { 437 for( ; *p > nMyOff; ++nMyOff ) 438 *pIdx++ = nNewVal; 439 --nMyOff; 440 --n; 441 --p; 442 } 443 else 444 *pIdx++ = nNewVal++; 445 } 446 447 // and then we need to save the attributes/bookmarks 448 // but this data must moved every time to the last in the chain! 449 for (size_t i = 0; i + 1 < aChanges.size(); ++i) // check all changes but not the current one 450 { 451 _UndoTransliterate_Data* pD = aChanges[i]; 452 if( pD->nNdIdx == pNew->nNdIdx && pD->pHistory ) 453 { 454 // same node and have a history? 455 pNew->pHistory = pD->pHistory; 456 pD->pHistory = 0; 457 break; // more can't exist 458 } 459 } 460 461 if( !pNew->pHistory ) 462 { 463 pNew->pHistory = new SwHistory; 464 SwRegHistory aRHst( rTNd, pNew->pHistory ); 465 pNew->pHistory->CopyAttr( rTNd.GetpSwpHints(), 466 pNew->nNdIdx, 0, rTNd.GetTxt().Len(), false ); 467 } 468 break; 469 } 470 } 471 472 void _UndoTransliterate_Data::SetChangeAtNode( SwDoc& rDoc ) 473 { 474 SwTxtNode* pTNd = rDoc.GetNodes()[ nNdIdx ]->GetTxtNode(); 475 if( pTNd ) 476 { 477 Sequence <sal_Int32> aOffsets( pOffsets ? pOffsets->getLength() : nLen ); 478 if( pOffsets ) 479 aOffsets = *pOffsets; 480 else 481 { 482 sal_Int32* p = aOffsets.getArray(); 483 for( xub_StrLen n = 0; n < nLen; ++n, ++p ) 484 *p = n + nStart; 485 } 486 pTNd->ReplaceTextOnly( nStart, nLen, sText, aOffsets ); 487 488 if( pHistory ) 489 { 490 if( pTNd->GetpSwpHints() ) 491 pTNd->ClearSwpHintsArr( false ); 492 pHistory->TmpRollback( &rDoc, 0, false ); 493 pHistory->SetTmpEnd( pHistory->Count() ); 494 } 495 } 496 } 497 498 499