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