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 32 #include <hintids.hxx> 33 #include <tools/list.hxx> 34 #include <vcl/vclenum.hxx> 35 #include <editeng/crsditem.hxx> 36 #include <editeng/colritem.hxx> 37 #include <editeng/boxitem.hxx> 38 #include <editeng/udlnitem.hxx> 39 #include <doc.hxx> 40 #include <IDocumentUndoRedo.hxx> 41 #include <docary.hxx> 42 #include <pam.hxx> 43 #include <ndtxt.hxx> 44 #include <redline.hxx> 45 #include <UndoRedline.hxx> 46 #include <section.hxx> 47 #include <tox.hxx> 48 #include <docsh.hxx> 49 50 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> 51 #include <com/sun/star/document/XDocumentProperties.hpp> 52 53 using namespace ::com::sun::star; 54 55 56 class CompareLine 57 { 58 public: 59 CompareLine() {} 60 virtual ~CompareLine(); 61 62 virtual sal_uLong GetHashValue() const = 0; 63 virtual sal_Bool Compare( const CompareLine& rLine ) const = 0; 64 }; 65 66 DECLARE_LIST( CompareList, CompareLine* ) 67 68 class CompareData 69 { 70 sal_uLong* pIndex; 71 sal_Bool* pChangedFlag; 72 73 protected: 74 CompareList aLines; 75 sal_uLong nSttLineNum; 76 77 // Anfang und Ende beschneiden und alle anderen in das 78 // LinesArray setzen 79 virtual void CheckRanges( CompareData& ) = 0; 80 81 public: 82 CompareData(); 83 virtual ~CompareData(); 84 85 // gibt es unterschiede? 86 sal_Bool HasDiffs( const CompareData& rData ) const; 87 88 // startet das Vergleichen und Erzeugen der Unterschiede zweier 89 // Dokumente 90 void CompareLines( CompareData& rData ); 91 // lasse die Unterschiede anzeigen - ruft die beiden Methoden 92 // ShowInsert / ShowDelete. Diese bekommen die Start und EndLine-Nummer 93 // uebergeben. Die Abbildung auf den tatsaechline Inhalt muss die 94 // Ableitung uebernehmen! 95 sal_uLong ShowDiffs( const CompareData& rData ); 96 97 virtual void ShowInsert( sal_uLong nStt, sal_uLong nEnd ); 98 virtual void ShowDelete( const CompareData& rData, sal_uLong nStt, 99 sal_uLong nEnd, sal_uLong nInsPos ); 100 virtual void CheckForChangesInLine( const CompareData& rData, 101 sal_uLong& nStt, sal_uLong& nEnd, 102 sal_uLong& nThisStt, sal_uLong& nThisEnd ); 103 104 // Eindeutigen Index fuer eine Line setzen. Gleiche Lines haben den 105 // selben Index; auch in den anderen CompareData! 106 void SetIndex( sal_uLong nLine, sal_uLong nIndex ); 107 sal_uLong GetIndex( sal_uLong nLine ) const 108 { return nLine < aLines.Count() ? pIndex[ nLine ] : 0; } 109 110 // setze/erfrage ob eine Zeile veraendert ist 111 void SetChanged( sal_uLong nLine, sal_Bool bFlag = sal_True ); 112 sal_Bool GetChanged( sal_uLong nLine ) const 113 { 114 return (pChangedFlag && nLine < aLines.Count()) 115 ? pChangedFlag[ nLine ] 116 : 0; 117 } 118 119 sal_uLong GetLineCount() const { return aLines.Count(); } 120 sal_uLong GetLineOffset() const { return nSttLineNum; } 121 const CompareLine* GetLine( sal_uLong nLine ) const 122 { return aLines.GetObject( nLine ); } 123 void InsertLine( CompareLine* pLine ) 124 { aLines.Insert( pLine, LIST_APPEND ); } 125 }; 126 127 class Hash 128 { 129 struct _HashData 130 { 131 sal_uLong nNext, nHash; 132 const CompareLine* pLine; 133 134 _HashData() 135 : nNext( 0 ), nHash( 0 ), pLine(0) {} 136 }; 137 138 sal_uLong* pHashArr; 139 _HashData* pDataArr; 140 sal_uLong nCount, nPrime; 141 142 public: 143 Hash( sal_uLong nSize ); 144 ~Hash(); 145 146 void CalcHashValue( CompareData& rData ); 147 148 sal_uLong GetCount() const { return nCount; } 149 }; 150 151 class Compare 152 { 153 public: 154 class MovedData 155 { 156 sal_uLong* pIndex; 157 sal_uLong* pLineNum; 158 sal_uLong nCount; 159 160 public: 161 MovedData( CompareData& rData, sal_Char* pDiscard ); 162 ~MovedData(); 163 164 sal_uLong GetIndex( sal_uLong n ) const { return pIndex[ n ]; } 165 sal_uLong GetLineNum( sal_uLong n ) const { return pLineNum[ n ]; } 166 sal_uLong GetCount() const { return nCount; } 167 }; 168 169 private: 170 // Suche die verschobenen Lines 171 class CompareSequence 172 { 173 CompareData &rData1, &rData2; 174 const MovedData &rMoved1, &rMoved2; 175 long *pMemory, *pFDiag, *pBDiag; 176 177 void Compare( sal_uLong nStt1, sal_uLong nEnd1, sal_uLong nStt2, sal_uLong nEnd2 ); 178 sal_uLong CheckDiag( sal_uLong nStt1, sal_uLong nEnd1, 179 sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost ); 180 public: 181 CompareSequence( CompareData& rData1, CompareData& rData2, 182 const MovedData& rD1, const MovedData& rD2 ); 183 ~CompareSequence(); 184 }; 185 186 187 static void CountDifference( const CompareData& rData, sal_uLong* pCounts ); 188 static void SetDiscard( const CompareData& rData, 189 sal_Char* pDiscard, sal_uLong* pCounts ); 190 static void CheckDiscard( sal_uLong nLen, sal_Char* pDiscard ); 191 static sal_uLong SetChangedFlag( CompareData& rData, sal_Char* pDiscard, int bFirst ); 192 static void ShiftBoundaries( CompareData& rData1, CompareData& rData2 ); 193 194 public: 195 Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 ); 196 }; 197 198 // ==================================================================== 199 200 CompareLine::~CompareLine() {} 201 202 // ---------------------------------------------------------------------- 203 204 CompareData::CompareData() 205 : pIndex( 0 ), pChangedFlag( 0 ), nSttLineNum( 0 ) 206 { 207 } 208 209 CompareData::~CompareData() 210 { 211 delete[] pIndex; 212 delete[] pChangedFlag; 213 } 214 215 void CompareData::SetIndex( sal_uLong nLine, sal_uLong nIndex ) 216 { 217 if( !pIndex ) 218 { 219 pIndex = new sal_uLong[ aLines.Count() ]; 220 memset( pIndex, 0, aLines.Count() * sizeof( sal_uLong ) ); 221 } 222 if( nLine < aLines.Count() ) 223 pIndex[ nLine ] = nIndex; 224 } 225 226 void CompareData::SetChanged( sal_uLong nLine, sal_Bool bFlag ) 227 { 228 if( !pChangedFlag ) 229 { 230 pChangedFlag = new sal_Bool[ aLines.Count() +1 ]; 231 memset( pChangedFlag, 0, aLines.Count() +1 * sizeof( sal_Bool ) ); 232 } 233 if( nLine < aLines.Count() ) 234 pChangedFlag[ nLine ] = bFlag; 235 } 236 237 void CompareData::CompareLines( CompareData& rData ) 238 { 239 CheckRanges( rData ); 240 241 sal_uLong nDifferent; 242 { 243 Hash aH( GetLineCount() + rData.GetLineCount() + 1 ); 244 aH.CalcHashValue( *this ); 245 aH.CalcHashValue( rData ); 246 nDifferent = aH.GetCount(); 247 } 248 { 249 Compare aComp( nDifferent, *this, rData ); 250 } 251 } 252 253 sal_uLong CompareData::ShowDiffs( const CompareData& rData ) 254 { 255 sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount(); 256 sal_uLong nStt1 = 0, nStt2 = 0; 257 sal_uLong nCnt = 0; 258 259 while( nStt1 < nLen1 || nStt2 < nLen2 ) 260 { 261 if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) ) 262 { 263 sal_uLong nSav1 = nStt1, nSav2 = nStt2; 264 while( nStt1 < nLen1 && rData.GetChanged( nStt1 )) ++nStt1; 265 while( nStt2 < nLen2 && GetChanged( nStt2 )) ++nStt2; 266 267 // rData ist das Original, 268 // this ist das, in das die Veraenderungen sollen 269 if( nSav2 != nStt2 && nSav1 != nStt1 ) 270 CheckForChangesInLine( rData, nSav1, nStt1, nSav2, nStt2 ); 271 272 if( nSav2 != nStt2 ) 273 ShowInsert( nSav2, nStt2 ); 274 275 if( nSav1 != nStt1 ) 276 ShowDelete( rData, nSav1, nStt1, nStt2 ); 277 ++nCnt; 278 } 279 ++nStt1, ++nStt2; 280 } 281 return nCnt; 282 } 283 284 sal_Bool CompareData::HasDiffs( const CompareData& rData ) const 285 { 286 sal_Bool bRet = sal_False; 287 sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount(); 288 sal_uLong nStt1 = 0, nStt2 = 0; 289 290 while( nStt1 < nLen1 || nStt2 < nLen2 ) 291 { 292 if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) ) 293 { 294 bRet = sal_True; 295 break; 296 } 297 ++nStt1, ++nStt2; 298 } 299 return bRet; 300 } 301 302 void CompareData::ShowInsert( sal_uLong, sal_uLong ) 303 { 304 } 305 306 void CompareData::ShowDelete( const CompareData&, sal_uLong, sal_uLong, sal_uLong ) 307 { 308 } 309 310 void CompareData::CheckForChangesInLine( const CompareData& , 311 sal_uLong&, sal_uLong&, sal_uLong&, sal_uLong& ) 312 { 313 } 314 315 // ---------------------------------------------------------------------- 316 317 Hash::Hash( sal_uLong nSize ) 318 : nCount( 1 ) 319 { 320 321 static const sal_uLong primes[] = 322 { 323 509, 324 1021, 325 2039, 326 4093, 327 8191, 328 16381, 329 32749, 330 65521, 331 131071, 332 262139, 333 524287, 334 1048573, 335 2097143, 336 4194301, 337 8388593, 338 16777213, 339 33554393, 340 67108859, /* Preposterously large . . . */ 341 134217689, 342 268435399, 343 536870909, 344 1073741789, 345 2147483647, 346 0 347 }; 348 int i; 349 350 pDataArr = new _HashData[ nSize ]; 351 pDataArr[0].nNext = 0; 352 pDataArr[0].nHash = 0, 353 pDataArr[0].pLine = 0; 354 355 for( i = 0; primes[i] < nSize / 3; i++) 356 if( !primes[i] ) 357 { 358 pHashArr = 0; 359 return; 360 } 361 nPrime = primes[ i ]; 362 pHashArr = new sal_uLong[ nPrime ]; 363 memset( pHashArr, 0, nPrime * sizeof( sal_uLong ) ); 364 } 365 366 Hash::~Hash() 367 { 368 delete[] pHashArr; 369 delete[] pDataArr; 370 } 371 372 void Hash::CalcHashValue( CompareData& rData ) 373 { 374 if( pHashArr ) 375 { 376 for( sal_uLong n = 0; n < rData.GetLineCount(); ++n ) 377 { 378 const CompareLine* pLine = rData.GetLine( n ); 379 ASSERT( pLine, "wo ist die Line?" ); 380 sal_uLong nH = pLine->GetHashValue(); 381 382 sal_uLong* pFound = &pHashArr[ nH % nPrime ]; 383 sal_uLong i; 384 for( i = *pFound; ; i = pDataArr[i].nNext ) 385 if( !i ) 386 { 387 i = nCount++; 388 pDataArr[i].nNext = *pFound; 389 pDataArr[i].nHash = nH; 390 pDataArr[i].pLine = pLine; 391 *pFound = i; 392 break; 393 } 394 else if( pDataArr[i].nHash == nH && 395 pDataArr[i].pLine->Compare( *pLine )) 396 break; 397 398 rData.SetIndex( n, i ); 399 } 400 } 401 } 402 403 // ---------------------------------------------------------------------- 404 405 Compare::Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 ) 406 { 407 MovedData *pMD1, *pMD2; 408 // Suche die unterschiedlichen Lines 409 { 410 sal_Char* pDiscard1 = new sal_Char[ rData1.GetLineCount() ]; 411 sal_Char* pDiscard2 = new sal_Char[ rData2.GetLineCount() ]; 412 413 sal_uLong* pCount1 = new sal_uLong[ nDiff ]; 414 sal_uLong* pCount2 = new sal_uLong[ nDiff ]; 415 memset( pCount1, 0, nDiff * sizeof( sal_uLong )); 416 memset( pCount2, 0, nDiff * sizeof( sal_uLong )); 417 418 // stelle fest, welche Indizies in den CompareData mehrfach vergeben wurden 419 CountDifference( rData1, pCount1 ); 420 CountDifference( rData2, pCount2 ); 421 422 // alle die jetzt nur einmal vorhanden sind, sind eingefuegt oder 423 // geloescht worden. Alle die im anderen auch vorhanden sind, sind 424 // verschoben worden 425 SetDiscard( rData1, pDiscard1, pCount2 ); 426 SetDiscard( rData2, pDiscard2, pCount1 ); 427 428 // die Arrays koennen wir wieder vergessen 429 delete [] pCount1; delete [] pCount2; 430 431 CheckDiscard( rData1.GetLineCount(), pDiscard1 ); 432 CheckDiscard( rData2.GetLineCount(), pDiscard2 ); 433 434 pMD1 = new MovedData( rData1, pDiscard1 ); 435 pMD2 = new MovedData( rData2, pDiscard2 ); 436 437 // die Arrays koennen wir wieder vergessen 438 delete [] pDiscard1; delete [] pDiscard2; 439 } 440 441 { 442 CompareSequence aTmp( rData1, rData2, *pMD1, *pMD2 ); 443 } 444 445 ShiftBoundaries( rData1, rData2 ); 446 447 delete pMD1; 448 delete pMD2; 449 } 450 451 452 453 void Compare::CountDifference( const CompareData& rData, sal_uLong* pCounts ) 454 { 455 sal_uLong nLen = rData.GetLineCount(); 456 for( sal_uLong n = 0; n < nLen; ++n ) 457 { 458 sal_uLong nIdx = rData.GetIndex( n ); 459 ++pCounts[ nIdx ]; 460 } 461 } 462 463 void Compare::SetDiscard( const CompareData& rData, 464 sal_Char* pDiscard, sal_uLong* pCounts ) 465 { 466 sal_uLong nLen = rData.GetLineCount(); 467 468 // berechne Max in Abhanegigkeit zur LineAnzahl 469 sal_uInt16 nMax = 5; 470 sal_uLong n; 471 472 for( n = nLen / 64; ( n = n >> 2 ) > 0; ) 473 nMax <<= 1; 474 475 for( n = 0; n < nLen; ++n ) 476 { 477 sal_uLong nIdx = rData.GetIndex( n ); 478 if( nIdx ) 479 { 480 nIdx = pCounts[ nIdx ]; 481 pDiscard[ n ] = !nIdx ? 1 : nIdx > nMax ? 2 : 0; 482 } 483 else 484 pDiscard[ n ] = 0; 485 } 486 } 487 488 void Compare::CheckDiscard( sal_uLong nLen, sal_Char* pDiscard ) 489 { 490 for( sal_uLong n = 0; n < nLen; ++n ) 491 { 492 if( 2 == pDiscard[ n ] ) 493 pDiscard[n] = 0; 494 else if( pDiscard[ n ] ) 495 { 496 sal_uLong j; 497 sal_uLong length; 498 sal_uLong provisional = 0; 499 500 /* Find end of this run of discardable lines. 501 Count how many are provisionally discardable. */ 502 for (j = n; j < nLen; j++) 503 { 504 if( !pDiscard[j] ) 505 break; 506 if( 2 == pDiscard[j] ) 507 ++provisional; 508 } 509 510 /* Cancel provisional discards at end, and shrink the run. */ 511 while( j > n && 2 == pDiscard[j - 1] ) 512 pDiscard[ --j ] = 0, --provisional; 513 514 /* Now we have the length of a run of discardable lines 515 whose first and last are not provisional. */ 516 length = j - n; 517 518 /* If 1/4 of the lines in the run are provisional, 519 cancel discarding of all provisional lines in the run. */ 520 if (provisional * 4 > length) 521 { 522 while (j > n) 523 if (pDiscard[--j] == 2) 524 pDiscard[j] = 0; 525 } 526 else 527 { 528 sal_uLong consec; 529 sal_uLong minimum = 1; 530 sal_uLong tem = length / 4; 531 532 /* MINIMUM is approximate square root of LENGTH/4. 533 A subrun of two or more provisionals can stand 534 when LENGTH is at least 16. 535 A subrun of 4 or more can stand when LENGTH >= 64. */ 536 while ((tem = tem >> 2) > 0) 537 minimum *= 2; 538 minimum++; 539 540 /* Cancel any subrun of MINIMUM or more provisionals 541 within the larger run. */ 542 for (j = 0, consec = 0; j < length; j++) 543 if (pDiscard[n + j] != 2) 544 consec = 0; 545 else if (minimum == ++consec) 546 /* Back up to start of subrun, to cancel it all. */ 547 j -= consec; 548 else if (minimum < consec) 549 pDiscard[n + j] = 0; 550 551 /* Scan from beginning of run 552 until we find 3 or more nonprovisionals in a row 553 or until the first nonprovisional at least 8 lines in. 554 Until that point, cancel any provisionals. */ 555 for (j = 0, consec = 0; j < length; j++) 556 { 557 if (j >= 8 && pDiscard[n + j] == 1) 558 break; 559 if (pDiscard[n + j] == 2) 560 consec = 0, pDiscard[n + j] = 0; 561 else if (pDiscard[n + j] == 0) 562 consec = 0; 563 else 564 consec++; 565 if (consec == 3) 566 break; 567 } 568 569 /* I advances to the last line of the run. */ 570 n += length - 1; 571 572 /* Same thing, from end. */ 573 for (j = 0, consec = 0; j < length; j++) 574 { 575 if (j >= 8 && pDiscard[n - j] == 1) 576 break; 577 if (pDiscard[n - j] == 2) 578 consec = 0, pDiscard[n - j] = 0; 579 else if (pDiscard[n - j] == 0) 580 consec = 0; 581 else 582 consec++; 583 if (consec == 3) 584 break; 585 } 586 } 587 } 588 } 589 } 590 591 // ---------------------------------------------------------------------- 592 593 Compare::MovedData::MovedData( CompareData& rData, sal_Char* pDiscard ) 594 : pIndex( 0 ), pLineNum( 0 ), nCount( 0 ) 595 { 596 sal_uLong nLen = rData.GetLineCount(); 597 sal_uLong n; 598 599 for( n = 0; n < nLen; ++n ) 600 if( pDiscard[ n ] ) 601 rData.SetChanged( n ); 602 else 603 ++nCount; 604 605 if( nCount ) 606 { 607 pIndex = new sal_uLong[ nCount ]; 608 pLineNum = new sal_uLong[ nCount ]; 609 610 for( n = 0, nCount = 0; n < nLen; ++n ) 611 if( !pDiscard[ n ] ) 612 { 613 pIndex[ nCount ] = rData.GetIndex( n ); 614 pLineNum[ nCount++ ] = n; 615 } 616 } 617 } 618 619 Compare::MovedData::~MovedData() 620 { 621 delete [] pIndex; 622 delete [] pLineNum; 623 } 624 625 // ---------------------------------------------------------------------- 626 627 // Suche die verschobenen Lines 628 Compare::CompareSequence::CompareSequence( 629 CompareData& rD1, CompareData& rD2, 630 const MovedData& rMD1, const MovedData& rMD2 ) 631 : rData1( rD1 ), rData2( rD2 ), rMoved1( rMD1 ), rMoved2( rMD2 ) 632 { 633 sal_uLong nSize = rMD1.GetCount() + rMD2.GetCount() + 3; 634 pMemory = new long[ nSize * 2 ]; 635 pFDiag = pMemory + ( rMD2.GetCount() + 1 ); 636 pBDiag = pMemory + ( nSize + rMD2.GetCount() + 1 ); 637 638 Compare( 0, rMD1.GetCount(), 0, rMD2.GetCount() ); 639 } 640 641 Compare::CompareSequence::~CompareSequence() 642 { 643 delete [] pMemory; 644 } 645 646 void Compare::CompareSequence::Compare( sal_uLong nStt1, sal_uLong nEnd1, 647 sal_uLong nStt2, sal_uLong nEnd2 ) 648 { 649 /* Slide down the bottom initial diagonal. */ 650 while( nStt1 < nEnd1 && nStt2 < nEnd2 && 651 rMoved1.GetIndex( nStt1 ) == rMoved2.GetIndex( nStt2 )) 652 ++nStt1, ++nStt2; 653 654 /* Slide up the top initial diagonal. */ 655 while( nEnd1 > nStt1 && nEnd2 > nStt2 && 656 rMoved1.GetIndex( nEnd1 - 1 ) == rMoved2.GetIndex( nEnd2 - 1 )) 657 --nEnd1, --nEnd2; 658 659 /* Handle simple cases. */ 660 if( nStt1 == nEnd1 ) 661 while( nStt2 < nEnd2 ) 662 rData2.SetChanged( rMoved2.GetLineNum( nStt2++ )); 663 664 else if (nStt2 == nEnd2) 665 while (nStt1 < nEnd1) 666 rData1.SetChanged( rMoved1.GetLineNum( nStt1++ )); 667 668 else 669 { 670 sal_uLong c, d, b; 671 672 /* Find a point of correspondence in the middle of the files. */ 673 674 d = CheckDiag( nStt1, nEnd1, nStt2, nEnd2, &c ); 675 b = pBDiag[ d ]; 676 677 if( 1 != c ) 678 { 679 /* Use that point to split this problem into two subproblems. */ 680 Compare( nStt1, b, nStt2, b - d ); 681 /* This used to use f instead of b, 682 but that is incorrect! 683 It is not necessarily the case that diagonal d 684 has a snake from b to f. */ 685 Compare( b, nEnd1, b - d, nEnd2 ); 686 } 687 } 688 } 689 690 sal_uLong Compare::CompareSequence::CheckDiag( sal_uLong nStt1, sal_uLong nEnd1, 691 sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost ) 692 { 693 const long dmin = nStt1 - nEnd2; /* Minimum valid diagonal. */ 694 const long dmax = nEnd1 - nStt2; /* Maximum valid diagonal. */ 695 const long fmid = nStt1 - nStt2; /* Center diagonal of top-down search. */ 696 const long bmid = nEnd1 - nEnd2; /* Center diagonal of bottom-up search. */ 697 698 long fmin = fmid, fmax = fmid; /* Limits of top-down search. */ 699 long bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */ 700 701 long c; /* Cost. */ 702 long odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd 703 diagonal with respect to the northwest. */ 704 705 pFDiag[fmid] = nStt1; 706 pBDiag[bmid] = nEnd1; 707 708 for (c = 1;; ++c) 709 { 710 long d; /* Active diagonal. */ 711 long big_snake = 0; 712 713 /* Extend the top-down search by an edit step in each diagonal. */ 714 fmin > dmin ? pFDiag[--fmin - 1] = -1 : ++fmin; 715 fmax < dmax ? pFDiag[++fmax + 1] = -1 : --fmax; 716 for (d = fmax; d >= fmin; d -= 2) 717 { 718 long x, y, oldx, tlo = pFDiag[d - 1], thi = pFDiag[d + 1]; 719 720 if (tlo >= thi) 721 x = tlo + 1; 722 else 723 x = thi; 724 oldx = x; 725 y = x - d; 726 while( sal_uLong(x) < nEnd1 && sal_uLong(y) < nEnd2 && 727 rMoved1.GetIndex( x ) == rMoved2.GetIndex( y )) 728 ++x, ++y; 729 if (x - oldx > 20) 730 big_snake = 1; 731 pFDiag[d] = x; 732 if( odd && bmin <= d && d <= bmax && pBDiag[d] <= pFDiag[d] ) 733 { 734 *pCost = 2 * c - 1; 735 return d; 736 } 737 } 738 739 /* Similar extend the bottom-up search. */ 740 bmin > dmin ? pBDiag[--bmin - 1] = INT_MAX : ++bmin; 741 bmax < dmax ? pBDiag[++bmax + 1] = INT_MAX : --bmax; 742 for (d = bmax; d >= bmin; d -= 2) 743 { 744 long x, y, oldx, tlo = pBDiag[d - 1], thi = pBDiag[d + 1]; 745 746 if (tlo < thi) 747 x = tlo; 748 else 749 x = thi - 1; 750 oldx = x; 751 y = x - d; 752 while( sal_uLong(x) > nStt1 && sal_uLong(y) > nStt2 && 753 rMoved1.GetIndex( x - 1 ) == rMoved2.GetIndex( y - 1 )) 754 --x, --y; 755 if (oldx - x > 20) 756 big_snake = 1; 757 pBDiag[d] = x; 758 if (!odd && fmin <= d && d <= fmax && pBDiag[d] <= pFDiag[d]) 759 { 760 *pCost = 2 * c; 761 return d; 762 } 763 } 764 } 765 } 766 767 void Compare::ShiftBoundaries( CompareData& rData1, CompareData& rData2 ) 768 { 769 for( int iz = 0; iz < 2; ++iz ) 770 { 771 CompareData* pData = &rData1; 772 CompareData* pOtherData = &rData2; 773 774 sal_uLong i = 0; 775 sal_uLong j = 0; 776 sal_uLong i_end = pData->GetLineCount(); 777 sal_uLong preceding = ULONG_MAX; 778 sal_uLong other_preceding = ULONG_MAX; 779 780 while (1) 781 { 782 sal_uLong start, other_start; 783 784 /* Scan forwards to find beginning of another run of changes. 785 Also keep track of the corresponding point in the other file. */ 786 787 while( i < i_end && !pData->GetChanged( i ) ) 788 { 789 while( pOtherData->GetChanged( j++ )) 790 /* Non-corresponding lines in the other file 791 will count as the preceding batch of changes. */ 792 other_preceding = j; 793 i++; 794 } 795 796 if (i == i_end) 797 break; 798 799 start = i; 800 other_start = j; 801 802 while (1) 803 { 804 /* Now find the end of this run of changes. */ 805 806 while( pData->GetChanged( ++i )) 807 ; 808 809 /* If the first changed line matches the following unchanged one, 810 and this run does not follow right after a previous run, 811 and there are no lines deleted from the other file here, 812 then classify the first changed line as unchanged 813 and the following line as changed in its place. */ 814 815 /* You might ask, how could this run follow right after another? 816 Only because the previous run was shifted here. */ 817 818 if( i != i_end && 819 pData->GetIndex( start ) == pData->GetIndex( i ) && 820 !pOtherData->GetChanged( j ) && 821 !( start == preceding || other_start == other_preceding )) 822 { 823 pData->SetChanged( start++, 0 ); 824 pData->SetChanged( i ); 825 /* Since one line-that-matches is now before this run 826 instead of after, we must advance in the other file 827 to keep in synch. */ 828 ++j; 829 } 830 else 831 break; 832 } 833 834 preceding = i; 835 other_preceding = j; 836 } 837 838 pData = &rData2; 839 pOtherData = &rData1; 840 } 841 } 842 843 /* */ 844 845 class SwCompareLine : public CompareLine 846 { 847 const SwNode& rNode; 848 public: 849 SwCompareLine( const SwNode& rNd ); 850 virtual ~SwCompareLine(); 851 852 virtual sal_uLong GetHashValue() const; 853 virtual sal_Bool Compare( const CompareLine& rLine ) const; 854 855 static sal_uLong GetTxtNodeHashValue( const SwTxtNode& rNd, sal_uLong nVal ); 856 static sal_Bool CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd ); 857 static sal_Bool CompareTxtNd( const SwTxtNode& rDstNd, 858 const SwTxtNode& rSrcNd ); 859 860 sal_Bool ChangesInLine( const SwCompareLine& rLine, 861 SwPaM *& rpInsRing, SwPaM*& rpDelRing ) const; 862 863 const SwNode& GetNode() const { return rNode; } 864 865 const SwNode& GetEndNode() const; 866 867 // fuers Debugging! 868 String GetText() const; 869 }; 870 871 class SwCompareData : public CompareData 872 { 873 SwDoc& rDoc; 874 SwPaM *pInsRing, *pDelRing; 875 876 sal_uLong PrevIdx( const SwNode* pNd ); 877 sal_uLong NextIdx( const SwNode* pNd ); 878 879 virtual void CheckRanges( CompareData& ); 880 virtual void ShowInsert( sal_uLong nStt, sal_uLong nEnd ); 881 virtual void ShowDelete( const CompareData& rData, sal_uLong nStt, 882 sal_uLong nEnd, sal_uLong nInsPos ); 883 884 virtual void CheckForChangesInLine( const CompareData& rData, 885 sal_uLong& nStt, sal_uLong& nEnd, 886 sal_uLong& nThisStt, sal_uLong& nThisEnd ); 887 888 public: 889 SwCompareData( SwDoc& rD ) : rDoc( rD ), pInsRing(0), pDelRing(0) {} 890 virtual ~SwCompareData(); 891 892 void SetRedlinesToDoc( sal_Bool bUseDocInfo ); 893 }; 894 895 // ---------------------------------------------------------------- 896 897 SwCompareLine::SwCompareLine( const SwNode& rNd ) 898 : rNode( rNd ) 899 { 900 } 901 902 SwCompareLine::~SwCompareLine() 903 { 904 } 905 906 sal_uLong SwCompareLine::GetHashValue() const 907 { 908 sal_uLong nRet = 0; 909 switch( rNode.GetNodeType() ) 910 { 911 case ND_TEXTNODE: 912 nRet = GetTxtNodeHashValue( (SwTxtNode&)rNode, nRet ); 913 break; 914 915 case ND_TABLENODE: 916 { 917 const SwNode* pEndNd = rNode.EndOfSectionNode(); 918 SwNodeIndex aIdx( rNode ); 919 while( &aIdx.GetNode() != pEndNd ) 920 { 921 if( aIdx.GetNode().IsTxtNode() ) 922 nRet = GetTxtNodeHashValue( (SwTxtNode&)aIdx.GetNode(), nRet ); 923 aIdx++; 924 } 925 } 926 break; 927 928 case ND_SECTIONNODE: 929 { 930 String sStr( GetText() ); 931 for( xub_StrLen n = 0; n < sStr.Len(); ++n ) 932 ( nRet <<= 1 ) += sStr.GetChar( n ); 933 } 934 break; 935 936 case ND_GRFNODE: 937 case ND_OLENODE: 938 // feste Id ? sollte aber nie auftauchen 939 break; 940 } 941 return nRet; 942 } 943 944 const SwNode& SwCompareLine::GetEndNode() const 945 { 946 const SwNode* pNd = &rNode; 947 switch( rNode.GetNodeType() ) 948 { 949 case ND_TABLENODE: 950 pNd = rNode.EndOfSectionNode(); 951 break; 952 953 case ND_SECTIONNODE: 954 { 955 const SwSectionNode& rSNd = (SwSectionNode&)rNode; 956 const SwSection& rSect = rSNd.GetSection(); 957 if( CONTENT_SECTION != rSect.GetType() || rSect.IsProtect() ) 958 pNd = rNode.EndOfSectionNode(); 959 } 960 break; 961 } 962 return *pNd; 963 } 964 965 sal_Bool SwCompareLine::Compare( const CompareLine& rLine ) const 966 { 967 return CompareNode( rNode, ((SwCompareLine&)rLine).rNode ); 968 } 969 970 namespace 971 { 972 static String SimpleTableToText(const SwNode &rNode) 973 { 974 String sRet; 975 const SwNode* pEndNd = rNode.EndOfSectionNode(); 976 SwNodeIndex aIdx( rNode ); 977 while (&aIdx.GetNode() != pEndNd) 978 { 979 if (aIdx.GetNode().IsTxtNode()) 980 { 981 if (sRet.Len()) 982 { 983 sRet.Append( '\n' ); 984 } 985 sRet.Append( aIdx.GetNode().GetTxtNode()->GetExpandTxt() ); 986 } 987 aIdx++; 988 } 989 return sRet; 990 } 991 } 992 993 sal_Bool SwCompareLine::CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd ) 994 { 995 if( rSrcNd.GetNodeType() != rDstNd.GetNodeType() ) 996 return sal_False; 997 998 sal_Bool bRet = sal_False; 999 1000 switch( rDstNd.GetNodeType() ) 1001 { 1002 case ND_TEXTNODE: 1003 bRet = CompareTxtNd( (SwTxtNode&)rDstNd, (SwTxtNode&)rSrcNd ); 1004 break; 1005 1006 case ND_TABLENODE: 1007 { 1008 const SwTableNode& rTSrcNd = (SwTableNode&)rSrcNd; 1009 const SwTableNode& rTDstNd = (SwTableNode&)rDstNd; 1010 1011 bRet = ( rTSrcNd.EndOfSectionIndex() - rTSrcNd.GetIndex() ) == 1012 ( rTDstNd.EndOfSectionIndex() - rTDstNd.GetIndex() ); 1013 1014 // --> #i107826#: compare actual table content 1015 if (bRet) 1016 { 1017 bRet = (SimpleTableToText(rSrcNd) == SimpleTableToText(rDstNd)); 1018 } 1019 // <-- 1020 } 1021 break; 1022 1023 case ND_SECTIONNODE: 1024 { 1025 const SwSectionNode& rSSrcNd = (SwSectionNode&)rSrcNd, 1026 & rSDstNd = (SwSectionNode&)rDstNd; 1027 const SwSection& rSrcSect = rSSrcNd.GetSection(), 1028 & rDstSect = rSDstNd.GetSection(); 1029 SectionType eSrcSectType = rSrcSect.GetType(), 1030 eDstSectType = rDstSect.GetType(); 1031 switch( eSrcSectType ) 1032 { 1033 case CONTENT_SECTION: 1034 bRet = CONTENT_SECTION == eDstSectType && 1035 rSrcSect.IsProtect() == rDstSect.IsProtect(); 1036 if( bRet && rSrcSect.IsProtect() ) 1037 { 1038 // the only have they both the same size 1039 bRet = ( rSSrcNd.EndOfSectionIndex() - rSSrcNd.GetIndex() ) == 1040 ( rSDstNd.EndOfSectionIndex() - rSDstNd.GetIndex() ); 1041 } 1042 break; 1043 1044 case TOX_HEADER_SECTION: 1045 case TOX_CONTENT_SECTION: 1046 if( TOX_HEADER_SECTION == eDstSectType || 1047 TOX_CONTENT_SECTION == eDstSectType ) 1048 { 1049 // the same type of TOX? 1050 const SwTOXBase* pSrcTOX = rSrcSect.GetTOXBase(); 1051 const SwTOXBase* pDstTOX = rDstSect.GetTOXBase(); 1052 bRet = pSrcTOX && pDstTOX 1053 && pSrcTOX->GetType() == pDstTOX->GetType() 1054 && pSrcTOX->GetTitle() == pDstTOX->GetTitle() 1055 && pSrcTOX->GetTypeName() == pDstTOX->GetTypeName() 1056 // && pSrcTOX->GetTOXName() == pDstTOX->GetTOXName() 1057 ; 1058 } 1059 break; 1060 1061 case DDE_LINK_SECTION: 1062 case FILE_LINK_SECTION: 1063 bRet = eSrcSectType == eDstSectType && 1064 rSrcSect.GetLinkFileName() == 1065 rDstSect.GetLinkFileName(); 1066 break; 1067 } 1068 } 1069 break; 1070 1071 case ND_ENDNODE: 1072 bRet = rSrcNd.StartOfSectionNode()->GetNodeType() == 1073 rDstNd.StartOfSectionNode()->GetNodeType(); 1074 1075 // --> #i107826#: compare actual table content 1076 if (bRet && rSrcNd.StartOfSectionNode()->GetNodeType() == ND_TABLENODE) 1077 { 1078 bRet = CompareNode( 1079 *rSrcNd.StartOfSectionNode(), *rDstNd.StartOfSectionNode()); 1080 } 1081 // <-- 1082 1083 break; 1084 } 1085 return bRet; 1086 } 1087 1088 String SwCompareLine::GetText() const 1089 { 1090 String sRet; 1091 switch( rNode.GetNodeType() ) 1092 { 1093 case ND_TEXTNODE: 1094 sRet = ((SwTxtNode&)rNode).GetExpandTxt(); 1095 break; 1096 1097 case ND_TABLENODE: 1098 { 1099 sRet = SimpleTableToText(rNode); 1100 sRet.InsertAscii( "Tabelle: ", 0 ); 1101 } 1102 break; 1103 1104 case ND_SECTIONNODE: 1105 { 1106 sRet.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "Section - Node:" )); 1107 1108 const SwSectionNode& rSNd = (SwSectionNode&)rNode; 1109 const SwSection& rSect = rSNd.GetSection(); 1110 switch( rSect.GetType() ) 1111 { 1112 case CONTENT_SECTION: 1113 if( rSect.IsProtect() ) 1114 sRet.Append( String::CreateFromInt32( 1115 rSNd.EndOfSectionIndex() - rSNd.GetIndex() )); 1116 break; 1117 1118 case TOX_HEADER_SECTION: 1119 case TOX_CONTENT_SECTION: 1120 { 1121 const SwTOXBase* pTOX = rSect.GetTOXBase(); 1122 if( pTOX ) 1123 sRet.Append( pTOX->GetTitle() ) 1124 .Append( pTOX->GetTypeName() ) 1125 // .Append( pTOX->GetTOXName() ) 1126 .Append( String::CreateFromInt32( pTOX->GetType() )); 1127 } 1128 break; 1129 1130 case DDE_LINK_SECTION: 1131 case FILE_LINK_SECTION: 1132 sRet += rSect.GetLinkFileName(); 1133 break; 1134 } 1135 } 1136 break; 1137 1138 case ND_GRFNODE: 1139 sRet.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "Grafik - Node:" )); 1140 break; 1141 case ND_OLENODE: 1142 sRet.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "OLE - Node:" )); 1143 break; 1144 } 1145 return sRet; 1146 } 1147 1148 sal_uLong SwCompareLine::GetTxtNodeHashValue( const SwTxtNode& rNd, sal_uLong nVal ) 1149 { 1150 String sStr( rNd.GetExpandTxt() ); 1151 for( xub_StrLen n = 0; n < sStr.Len(); ++n ) 1152 ( nVal <<= 1 ) += sStr.GetChar( n ); 1153 return nVal; 1154 } 1155 1156 sal_Bool SwCompareLine::CompareTxtNd( const SwTxtNode& rDstNd, 1157 const SwTxtNode& rSrcNd ) 1158 { 1159 sal_Bool bRet = sal_False; 1160 // erstmal ganz einfach! 1161 if( rDstNd.GetTxt() == rSrcNd.GetTxt() ) 1162 { 1163 // der Text ist gleich, aber sind die "Sonderattribute" (0xFF) auch 1164 // dieselben?? 1165 bRet = sal_True; 1166 } 1167 return bRet; 1168 } 1169 1170 sal_Bool SwCompareLine::ChangesInLine( const SwCompareLine& rLine, 1171 SwPaM *& rpInsRing, SwPaM*& rpDelRing ) const 1172 { 1173 sal_Bool bRet = sal_False; 1174 if( ND_TEXTNODE == rNode.GetNodeType() && 1175 ND_TEXTNODE == rLine.GetNode().GetNodeType() ) 1176 { 1177 SwTxtNode& rDestNd = *(SwTxtNode*)rNode.GetTxtNode(); 1178 const SwTxtNode& rSrcNd = *rLine.GetNode().GetTxtNode(); 1179 1180 xub_StrLen nDEnd = rDestNd.GetTxt().Len(), nSEnd = rSrcNd.GetTxt().Len(); 1181 xub_StrLen nStt; 1182 xub_StrLen nEnd; 1183 1184 for( nStt = 0, nEnd = Min( nDEnd, nSEnd ); nStt < nEnd; ++nStt ) 1185 if( rDestNd.GetTxt().GetChar( nStt ) != 1186 rSrcNd.GetTxt().GetChar( nStt ) ) 1187 break; 1188 1189 while( nStt < nDEnd && nStt < nSEnd ) 1190 { 1191 --nDEnd, --nSEnd; 1192 if( rDestNd.GetTxt().GetChar( nDEnd ) != 1193 rSrcNd.GetTxt().GetChar( nSEnd ) ) 1194 { 1195 ++nDEnd, ++nSEnd; 1196 break; 1197 } 1198 } 1199 1200 if( nStt || !nDEnd || !nSEnd || nDEnd < rDestNd.GetTxt().Len() || 1201 nSEnd < rSrcNd.GetTxt().Len() ) 1202 { 1203 // jetzt ist zwischen nStt bis nDEnd das neu eingefuegte 1204 // und zwischen nStt und nSEnd das geloeschte 1205 SwDoc* pDoc = rDestNd.GetDoc(); 1206 SwPaM aPam( rDestNd, nDEnd ); 1207 if( nStt != nDEnd ) 1208 { 1209 SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpInsRing ); 1210 if( !rpInsRing ) 1211 rpInsRing = pTmp; 1212 1213 pTmp->SetMark(); 1214 pTmp->GetMark()->nContent = nStt; 1215 } 1216 1217 if( nStt != nSEnd ) 1218 { 1219 { 1220 ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); 1221 SwPaM aCpyPam( rSrcNd, nStt ); 1222 aCpyPam.SetMark(); 1223 aCpyPam.GetPoint()->nContent = nSEnd; 1224 aCpyPam.GetDoc()->CopyRange( aCpyPam, *aPam.GetPoint(), 1225 false ); 1226 } 1227 1228 SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpDelRing ); 1229 if( !rpDelRing ) 1230 rpDelRing = pTmp; 1231 1232 pTmp->SetMark(); 1233 pTmp->GetMark()->nContent = nDEnd; 1234 1235 if( rpInsRing ) 1236 { 1237 SwPaM* pCorr = (SwPaM*)rpInsRing->GetPrev(); 1238 if( *pCorr->GetPoint() == *pTmp->GetPoint() ) 1239 *pCorr->GetPoint() = *pTmp->GetMark(); 1240 } 1241 } 1242 bRet = sal_True; 1243 } 1244 } 1245 return bRet; 1246 } 1247 1248 // ---------------------------------------------------------------- 1249 1250 SwCompareData::~SwCompareData() 1251 { 1252 if( pDelRing ) 1253 { 1254 while( pDelRing->GetNext() != pDelRing ) 1255 delete pDelRing->GetNext(); 1256 delete pDelRing; 1257 } 1258 if( pInsRing ) 1259 { 1260 while( pInsRing->GetNext() != pInsRing ) 1261 delete pInsRing->GetNext(); 1262 delete pInsRing; 1263 } 1264 } 1265 1266 sal_uLong SwCompareData::NextIdx( const SwNode* pNd ) 1267 { 1268 if( pNd->IsStartNode() ) 1269 { 1270 const SwSectionNode* pSNd; 1271 if( pNd->IsTableNode() || 1272 ( 0 != (pSNd = pNd->GetSectionNode() ) && 1273 ( CONTENT_SECTION != pSNd->GetSection().GetType() || 1274 pSNd->GetSection().IsProtect() ) ) ) 1275 pNd = pNd->EndOfSectionNode(); 1276 } 1277 return pNd->GetIndex() + 1; 1278 } 1279 1280 sal_uLong SwCompareData::PrevIdx( const SwNode* pNd ) 1281 { 1282 if( pNd->IsEndNode() ) 1283 { 1284 const SwSectionNode* pSNd; 1285 if( pNd->StartOfSectionNode()->IsTableNode() || 1286 ( 0 != (pSNd = pNd->StartOfSectionNode()->GetSectionNode() ) && 1287 ( CONTENT_SECTION != pSNd->GetSection().GetType() || 1288 pSNd->GetSection().IsProtect() ) ) ) 1289 pNd = pNd->StartOfSectionNode(); 1290 } 1291 return pNd->GetIndex() - 1; 1292 } 1293 1294 1295 void SwCompareData::CheckRanges( CompareData& rData ) 1296 { 1297 const SwNodes& rSrcNds = ((SwCompareData&)rData).rDoc.GetNodes(); 1298 const SwNodes& rDstNds = rDoc.GetNodes(); 1299 1300 const SwNode& rSrcEndNd = rSrcNds.GetEndOfContent(); 1301 const SwNode& rDstEndNd = rDstNds.GetEndOfContent(); 1302 1303 sal_uLong nSrcSttIdx = NextIdx( rSrcEndNd.StartOfSectionNode() ); 1304 sal_uLong nSrcEndIdx = rSrcEndNd.GetIndex(); 1305 1306 sal_uLong nDstSttIdx = NextIdx( rDstEndNd.StartOfSectionNode() ); 1307 sal_uLong nDstEndIdx = rDstEndNd.GetIndex(); 1308 1309 while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx ) 1310 { 1311 const SwNode* pSrcNd = rSrcNds[ nSrcSttIdx ]; 1312 const SwNode* pDstNd = rDstNds[ nDstSttIdx ]; 1313 if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd )) 1314 break; 1315 1316 nSrcSttIdx = NextIdx( pSrcNd ); 1317 nDstSttIdx = NextIdx( pDstNd ); 1318 } 1319 1320 nSrcEndIdx = PrevIdx( &rSrcEndNd ); 1321 nDstEndIdx = PrevIdx( &rDstEndNd ); 1322 while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx ) 1323 { 1324 const SwNode* pSrcNd = rSrcNds[ nSrcEndIdx ]; 1325 const SwNode* pDstNd = rDstNds[ nDstEndIdx ]; 1326 if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd )) 1327 break; 1328 1329 nSrcEndIdx = PrevIdx( pSrcNd ); 1330 nDstEndIdx = PrevIdx( pDstNd ); 1331 } 1332 1333 while( nSrcSttIdx <= nSrcEndIdx ) 1334 { 1335 const SwNode* pNd = rSrcNds[ nSrcSttIdx ]; 1336 rData.InsertLine( new SwCompareLine( *pNd ) ); 1337 nSrcSttIdx = NextIdx( pNd ); 1338 } 1339 1340 while( nDstSttIdx <= nDstEndIdx ) 1341 { 1342 const SwNode* pNd = rDstNds[ nDstSttIdx ]; 1343 InsertLine( new SwCompareLine( *pNd ) ); 1344 nDstSttIdx = NextIdx( pNd ); 1345 } 1346 } 1347 1348 1349 void SwCompareData::ShowInsert( sal_uLong nStt, sal_uLong nEnd ) 1350 { 1351 SwPaM* pTmp = new SwPaM( ((SwCompareLine*)GetLine( nStt ))->GetNode(), 0, 1352 ((SwCompareLine*)GetLine( nEnd-1 ))->GetEndNode(), 0, 1353 pInsRing ); 1354 if( !pInsRing ) 1355 pInsRing = pTmp; 1356 1357 // #i65201#: These SwPaMs are calculated smaller than needed, see comment below 1358 1359 } 1360 1361 void SwCompareData::ShowDelete( const CompareData& rData, sal_uLong nStt, 1362 sal_uLong nEnd, sal_uLong nInsPos ) 1363 { 1364 SwNodeRange aRg( 1365 ((SwCompareLine*)rData.GetLine( nStt ))->GetNode(), 0, 1366 ((SwCompareLine*)rData.GetLine( nEnd-1 ))->GetEndNode(), 1 ); 1367 1368 sal_uInt16 nOffset = 0; 1369 const CompareLine* pLine; 1370 if( GetLineCount() == nInsPos ) 1371 { 1372 pLine = GetLine( nInsPos-1 ); 1373 nOffset = 1; 1374 } 1375 else 1376 pLine = GetLine( nInsPos ); 1377 1378 const SwNode* pLineNd; 1379 if( pLine ) 1380 { 1381 if( nOffset ) 1382 pLineNd = &((SwCompareLine*)pLine)->GetEndNode(); 1383 else 1384 pLineNd = &((SwCompareLine*)pLine)->GetNode(); 1385 } 1386 else 1387 { 1388 pLineNd = &rDoc.GetNodes().GetEndOfContent(); 1389 nOffset = 0; 1390 } 1391 1392 SwNodeIndex aInsPos( *pLineNd, nOffset ); 1393 SwNodeIndex aSavePos( aInsPos, -1 ); 1394 1395 ((SwCompareData&)rData).rDoc.CopyWithFlyInFly( aRg, 0, aInsPos ); 1396 rDoc.SetModified(); 1397 aSavePos++; 1398 1399 // #i65201#: These SwPaMs are calculated when the (old) delete-redlines are hidden, 1400 // they will be inserted when the delete-redlines are shown again. 1401 // To avoid unwanted insertions of delete-redlines into these new redlines, what happens 1402 // especially at the end of the document, I reduce the SwPaM by one node. 1403 // Before the new redlines are inserted, they have to expand again. 1404 SwPaM* pTmp = new SwPaM( aSavePos.GetNode(), aInsPos.GetNode(), 0, -1, pDelRing ); 1405 if( !pDelRing ) 1406 pDelRing = pTmp; 1407 1408 if( pInsRing ) 1409 { 1410 SwPaM* pCorr = (SwPaM*)pInsRing->GetPrev(); 1411 if( *pCorr->GetPoint() == *pTmp->GetPoint() ) 1412 { 1413 SwNodeIndex aTmpPos( pTmp->GetMark()->nNode, -1 ); 1414 *pCorr->GetPoint() = SwPosition( aTmpPos ); 1415 } 1416 } 1417 } 1418 1419 void SwCompareData::CheckForChangesInLine( const CompareData& rData, 1420 sal_uLong& rStt, sal_uLong& rEnd, 1421 sal_uLong& rThisStt, sal_uLong& rThisEnd ) 1422 { 1423 while( rStt < rEnd && rThisStt < rThisEnd ) 1424 { 1425 SwCompareLine* pDstLn = (SwCompareLine*)GetLine( rThisStt ); 1426 SwCompareLine* pSrcLn = (SwCompareLine*)rData.GetLine( rStt ); 1427 if( !pDstLn->ChangesInLine( *pSrcLn, pInsRing, pDelRing ) ) 1428 break; 1429 1430 ++rStt; 1431 ++rThisStt; 1432 } 1433 } 1434 1435 void SwCompareData::SetRedlinesToDoc( sal_Bool bUseDocInfo ) 1436 { 1437 SwPaM* pTmp = pDelRing; 1438 1439 // Bug #83296#: get the Author / TimeStamp from the "other" 1440 // document info 1441 sal_uInt16 nAuthor = rDoc.GetRedlineAuthor(); 1442 DateTime aTimeStamp; 1443 SwDocShell *pDocShell(rDoc.GetDocShell()); 1444 DBG_ASSERT(pDocShell, "no SwDocShell"); 1445 if (pDocShell) { 1446 uno::Reference<document::XDocumentPropertiesSupplier> xDPS( 1447 pDocShell->GetModel(), uno::UNO_QUERY_THROW); 1448 uno::Reference<document::XDocumentProperties> xDocProps( 1449 xDPS->getDocumentProperties()); 1450 DBG_ASSERT(xDocProps.is(), "Doc has no DocumentProperties"); 1451 1452 if( bUseDocInfo && xDocProps.is() ) { 1453 String aTmp( 1 == xDocProps->getEditingCycles() 1454 ? xDocProps->getAuthor() 1455 : xDocProps->getModifiedBy() ); 1456 util::DateTime uDT( 1 == xDocProps->getEditingCycles() 1457 ? xDocProps->getCreationDate() 1458 : xDocProps->getModificationDate() ); 1459 Date d(uDT.Day, uDT.Month, uDT.Year); 1460 Time t(uDT.Hours, uDT.Minutes, uDT.Seconds, uDT.HundredthSeconds); 1461 DateTime aDT(d,t); 1462 1463 if( aTmp.Len() ) 1464 { 1465 nAuthor = rDoc.InsertRedlineAuthor( aTmp ); 1466 aTimeStamp = aDT; 1467 } 1468 } 1469 } 1470 1471 if( pTmp ) 1472 { 1473 SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_DELETE, nAuthor, aTimeStamp, 1474 aEmptyStr, 0, 0 ); 1475 do { 1476 // #i65201#: Expand again, see comment above. 1477 if( pTmp->GetPoint()->nContent == 0 ) 1478 { 1479 pTmp->GetPoint()->nNode++; 1480 pTmp->GetPoint()->nContent.Assign( pTmp->GetCntntNode(), 0 ); 1481 } 1482 // --> mst 2010-05-17 #i101009# 1483 // prevent redlines that end on structural end node 1484 if (& rDoc.GetNodes().GetEndOfContent() == 1485 & pTmp->GetPoint()->nNode.GetNode()) 1486 { 1487 pTmp->GetPoint()->nNode--; 1488 SwCntntNode *const pContentNode( pTmp->GetCntntNode() ); 1489 pTmp->GetPoint()->nContent.Assign( pContentNode, 1490 (pContentNode) ? pContentNode->Len() : 0 ); 1491 } 1492 // <-- 1493 1494 rDoc.DeleteRedline( *pTmp, false, USHRT_MAX ); 1495 1496 if (rDoc.GetIDocumentUndoRedo().DoesUndo()) 1497 { 1498 SwUndo *const pUndo(new SwUndoCompDoc( *pTmp, sal_False )) ; 1499 rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo); 1500 } 1501 rDoc.AppendRedline( new SwRedline( aRedlnData, *pTmp ), true ); 1502 1503 } while( pDelRing != ( pTmp = (SwPaM*)pTmp->GetNext() )); 1504 } 1505 1506 pTmp = pInsRing; 1507 if( pTmp ) 1508 { 1509 do { 1510 if( pTmp->GetPoint()->nContent == 0 ) 1511 { 1512 pTmp->GetPoint()->nNode++; 1513 pTmp->GetPoint()->nContent.Assign( pTmp->GetCntntNode(), 0 ); 1514 } 1515 // --> mst 2010-05-17 #i101009# 1516 // prevent redlines that end on structural end node 1517 if (& rDoc.GetNodes().GetEndOfContent() == 1518 & pTmp->GetPoint()->nNode.GetNode()) 1519 { 1520 pTmp->GetPoint()->nNode--; 1521 SwCntntNode *const pContentNode( pTmp->GetCntntNode() ); 1522 pTmp->GetPoint()->nContent.Assign( pContentNode, 1523 (pContentNode) ? pContentNode->Len() : 0 ); 1524 } 1525 // <-- 1526 } while( pInsRing != ( pTmp = (SwPaM*)pTmp->GetNext() )); 1527 SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_INSERT, nAuthor, aTimeStamp, 1528 aEmptyStr, 0, 0 ); 1529 1530 // zusammenhaengende zusammenfassen 1531 if( pTmp->GetNext() != pInsRing ) 1532 { 1533 const SwCntntNode* pCNd; 1534 do { 1535 SwPosition& rSttEnd = *pTmp->End(), 1536 & rEndStt = *((SwPaM*)pTmp->GetNext())->Start(); 1537 if( rSttEnd == rEndStt || 1538 (!rEndStt.nContent.GetIndex() && 1539 rEndStt.nNode.GetIndex() - 1 == rSttEnd.nNode.GetIndex() && 1540 0 != ( pCNd = rSttEnd.nNode.GetNode().GetCntntNode() ) 1541 ? rSttEnd.nContent.GetIndex() == pCNd->Len() 1542 : 0 )) 1543 { 1544 if( pTmp->GetNext() == pInsRing ) 1545 { 1546 // liegen hintereinander also zusammen fassen 1547 rEndStt = *pTmp->Start(); 1548 delete pTmp; 1549 pTmp = pInsRing; 1550 } 1551 else 1552 { 1553 // liegen hintereinander also zusammen fassen 1554 rSttEnd = *((SwPaM*)pTmp->GetNext())->End(); 1555 delete pTmp->GetNext(); 1556 } 1557 } 1558 else 1559 pTmp = (SwPaM*)pTmp->GetNext(); 1560 } while( pInsRing != pTmp ); 1561 } 1562 1563 do { 1564 if( rDoc.AppendRedline( new SwRedline( aRedlnData, *pTmp ), true) && 1565 rDoc.GetIDocumentUndoRedo().DoesUndo()) 1566 { 1567 SwUndo *const pUndo(new SwUndoCompDoc( *pTmp, sal_True )); 1568 rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo); 1569 } 1570 } while( pInsRing != ( pTmp = (SwPaM*)pTmp->GetNext() )); 1571 } 1572 } 1573 1574 /* */ 1575 1576 1577 1578 // returnt (?die Anzahl der Unterschiede?) ob etwas unterschiedlich ist 1579 long SwDoc::CompareDoc( const SwDoc& rDoc ) 1580 { 1581 if( &rDoc == this ) 1582 return 0; 1583 1584 long nRet = 0; 1585 1586 GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL); 1587 sal_Bool bDocWasModified = IsModified(); 1588 SwDoc& rSrcDoc = (SwDoc&)rDoc; 1589 sal_Bool bSrcModified = rSrcDoc.IsModified(); 1590 1591 RedlineMode_t eSrcRedlMode = rSrcDoc.GetRedlineMode(); 1592 rSrcDoc.SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_INSERT ); 1593 SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT)); 1594 1595 SwCompareData aD0( rSrcDoc ); 1596 SwCompareData aD1( *this ); 1597 1598 aD1.CompareLines( aD0 ); 1599 1600 nRet = aD1.ShowDiffs( aD0 ); 1601 1602 if( nRet ) 1603 { 1604 SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | 1605 nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); 1606 1607 aD1.SetRedlinesToDoc( !bDocWasModified ); 1608 SetModified(); 1609 } 1610 1611 rSrcDoc.SetRedlineMode( eSrcRedlMode ); 1612 SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); 1613 1614 if( !bSrcModified ) 1615 rSrcDoc.ResetModified(); 1616 1617 GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL); 1618 1619 return nRet; 1620 } 1621 1622 1623 class _SaveMergeRedlines : public Ring 1624 { 1625 const SwRedline* pSrcRedl; 1626 SwRedline* pDestRedl; 1627 public: 1628 _SaveMergeRedlines( const SwNode& rDstNd, 1629 const SwRedline& rSrcRedl, Ring* pRing ); 1630 sal_uInt16 InsertRedline(); 1631 1632 SwRedline* GetDestRedline() { return pDestRedl; } 1633 }; 1634 1635 _SaveMergeRedlines::_SaveMergeRedlines( const SwNode& rDstNd, 1636 const SwRedline& rSrcRedl, Ring* pRing ) 1637 : Ring( pRing ), pSrcRedl( &rSrcRedl ) 1638 { 1639 SwPosition aPos( rDstNd ); 1640 1641 const SwPosition* pStt = rSrcRedl.Start(); 1642 if( rDstNd.IsCntntNode() ) 1643 aPos.nContent.Assign( ((SwCntntNode*)&rDstNd), pStt->nContent.GetIndex() ); 1644 pDestRedl = new SwRedline( rSrcRedl.GetRedlineData(), aPos ); 1645 1646 if( nsRedlineType_t::REDLINE_DELETE == pDestRedl->GetType() ) 1647 { 1648 // den Bereich als geloescht kennzeichnen 1649 const SwPosition* pEnd = pStt == rSrcRedl.GetPoint() 1650 ? rSrcRedl.GetMark() 1651 : rSrcRedl.GetPoint(); 1652 1653 pDestRedl->SetMark(); 1654 pDestRedl->GetPoint()->nNode += pEnd->nNode.GetIndex() - 1655 pStt->nNode.GetIndex(); 1656 pDestRedl->GetPoint()->nContent.Assign( pDestRedl->GetCntntNode(), 1657 pEnd->nContent.GetIndex() ); 1658 } 1659 } 1660 1661 sal_uInt16 _SaveMergeRedlines::InsertRedline() 1662 { 1663 sal_uInt16 nIns = 0; 1664 SwDoc* pDoc = pDestRedl->GetDoc(); 1665 1666 if( nsRedlineType_t::REDLINE_INSERT == pDestRedl->GetType() ) 1667 { 1668 // der Teil wurde eingefuegt, also kopiere ihn aus dem SourceDoc 1669 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); 1670 1671 SwNodeIndex aSaveNd( pDestRedl->GetPoint()->nNode, -1 ); 1672 xub_StrLen nSaveCnt = pDestRedl->GetPoint()->nContent.GetIndex(); 1673 1674 RedlineMode_t eOld = pDoc->GetRedlineMode(); 1675 pDoc->SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_IGNORE)); 1676 1677 pSrcRedl->GetDoc()->CopyRange( 1678 *const_cast<SwPaM*>(static_cast<const SwPaM*>(pSrcRedl)), 1679 *pDestRedl->GetPoint(), false ); 1680 1681 pDoc->SetRedlineMode_intern( eOld ); 1682 1683 pDestRedl->SetMark(); 1684 aSaveNd++; 1685 pDestRedl->GetMark()->nNode = aSaveNd; 1686 pDestRedl->GetMark()->nContent.Assign( aSaveNd.GetNode().GetCntntNode(), 1687 nSaveCnt ); 1688 1689 if( GetPrev() != this ) 1690 { 1691 SwPaM* pTmpPrev = ((_SaveMergeRedlines*)GetPrev())->pDestRedl; 1692 if( pTmpPrev && *pTmpPrev->GetPoint() == *pDestRedl->GetPoint() ) 1693 *pTmpPrev->GetPoint() = *pDestRedl->GetMark(); 1694 } 1695 } 1696 else 1697 { 1698 //JP 21.09.98: Bug 55909 1699 // falls im Doc auf gleicher Pos aber schon ein geloeschter oder 1700 // eingefuegter ist, dann muss dieser gesplittet werden! 1701 SwPosition* pDStt = pDestRedl->GetMark(), 1702 * pDEnd = pDestRedl->GetPoint(); 1703 sal_uInt16 n = 0; 1704 1705 // zur StartPos das erste Redline suchen 1706 if( !pDoc->GetRedline( *pDStt, &n ) && n ) 1707 --n; 1708 1709 const SwRedlineTbl& rRedlineTbl = pDoc->GetRedlineTbl(); 1710 for( ; n < rRedlineTbl.Count(); ++n ) 1711 { 1712 SwRedline* pRedl = rRedlineTbl[ n ]; 1713 SwPosition* pRStt = pRedl->Start(), 1714 * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() 1715 : pRedl->GetPoint(); 1716 if( nsRedlineType_t::REDLINE_DELETE == pRedl->GetType() || 1717 nsRedlineType_t::REDLINE_INSERT == pRedl->GetType() ) 1718 { 1719 SwComparePosition eCmpPos = ComparePosition( *pDStt, *pDEnd, *pRStt, *pREnd ); 1720 switch( eCmpPos ) 1721 { 1722 case POS_COLLIDE_START: 1723 case POS_BEHIND: 1724 break; 1725 1726 case POS_INSIDE: 1727 case POS_EQUAL: 1728 delete pDestRedl, pDestRedl = 0; 1729 // break; -> kein break !!!! 1730 1731 case POS_COLLIDE_END: 1732 case POS_BEFORE: 1733 n = rRedlineTbl.Count(); 1734 break; 1735 1736 case POS_OUTSIDE: 1737 { 1738 SwRedline* pCpyRedl = new SwRedline( 1739 pDestRedl->GetRedlineData(), *pDStt ); 1740 pCpyRedl->SetMark(); 1741 *pCpyRedl->GetPoint() = *pRStt; 1742 1743 SwUndoCompDoc *const pUndo = 1744 (pDoc->GetIDocumentUndoRedo().DoesUndo()) 1745 ? new SwUndoCompDoc( *pCpyRedl ) : 0; 1746 1747 // now modify doc: append redline, undo (and count) 1748 pDoc->AppendRedline( pCpyRedl, true ); 1749 if( pUndo ) 1750 { 1751 pDoc->GetIDocumentUndoRedo().AppendUndo(pUndo); 1752 } 1753 ++nIns; 1754 1755 *pDStt = *pREnd; 1756 1757 // dann solle man neu anfangen 1758 n = USHRT_MAX; 1759 } 1760 break; 1761 1762 case POS_OVERLAP_BEFORE: 1763 *pDEnd = *pRStt; 1764 break; 1765 1766 case POS_OVERLAP_BEHIND: 1767 *pDStt = *pREnd; 1768 break; 1769 } 1770 } 1771 else if( *pDEnd <= *pRStt ) 1772 break; 1773 } 1774 1775 } 1776 1777 if( pDestRedl ) 1778 { 1779 SwUndoCompDoc *const pUndo = (pDoc->GetIDocumentUndoRedo().DoesUndo()) 1780 ? new SwUndoCompDoc( *pDestRedl ) : 0; 1781 1782 // now modify doc: append redline, undo (and count) 1783 bool bRedlineAccepted = pDoc->AppendRedline( pDestRedl, true ); 1784 if( pUndo ) 1785 { 1786 pDoc->GetIDocumentUndoRedo().AppendUndo( pUndo ); 1787 } 1788 ++nIns; 1789 1790 // if AppendRedline has deleted our redline, we may not keep a 1791 // reference to it 1792 if( ! bRedlineAccepted ) 1793 pDestRedl = NULL; 1794 } 1795 return nIns; 1796 } 1797 1798 // merge zweier Dokumente 1799 long SwDoc::MergeDoc( const SwDoc& rDoc ) 1800 { 1801 if( &rDoc == this ) 1802 return 0; 1803 1804 long nRet = 0; 1805 1806 GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL); 1807 1808 SwDoc& rSrcDoc = (SwDoc&)rDoc; 1809 sal_Bool bSrcModified = rSrcDoc.IsModified(); 1810 1811 RedlineMode_t eSrcRedlMode = rSrcDoc.GetRedlineMode(); 1812 rSrcDoc.SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE ); 1813 SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE ); 1814 1815 SwCompareData aD0( rSrcDoc ); 1816 SwCompareData aD1( *this ); 1817 1818 aD1.CompareLines( aD0 ); 1819 1820 if( !aD1.HasDiffs( aD0 ) ) 1821 { 1822 // jetzt wollen wir alle Redlines aus dem SourceDoc zu uns bekommen 1823 1824 // suche alle Insert - Redlines aus dem SourceDoc und bestimme 1825 // deren Position im DestDoc 1826 _SaveMergeRedlines* pRing = 0; 1827 const SwRedlineTbl& rSrcRedlTbl = rSrcDoc.GetRedlineTbl(); 1828 sal_uLong nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex(); 1829 sal_uLong nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex(); 1830 for( sal_uInt16 n = 0; n < rSrcRedlTbl.Count(); ++n ) 1831 { 1832 const SwRedline* pRedl = rSrcRedlTbl[ n ]; 1833 sal_uLong nNd = pRedl->GetPoint()->nNode.GetIndex(); 1834 RedlineType_t eType = pRedl->GetType(); 1835 if( nEndOfExtra < nNd && 1836 ( nsRedlineType_t::REDLINE_INSERT == eType || nsRedlineType_t::REDLINE_DELETE == eType )) 1837 { 1838 const SwNode* pDstNd = GetNodes()[ 1839 nMyEndOfExtra + nNd - nEndOfExtra ]; 1840 1841 // Position gefunden. Dann muss im DestDoc auch 1842 // in der Line das Redline eingefuegt werden 1843 _SaveMergeRedlines* pTmp = new _SaveMergeRedlines( 1844 *pDstNd, *pRedl, pRing ); 1845 if( !pRing ) 1846 pRing = pTmp; 1847 } 1848 } 1849 1850 if( pRing ) 1851 { 1852 // dann alle ins DestDoc ueber nehmen 1853 rSrcDoc.SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); 1854 1855 SetRedlineMode((RedlineMode_t)( 1856 nsRedlineMode_t::REDLINE_ON | 1857 nsRedlineMode_t::REDLINE_SHOW_INSERT | 1858 nsRedlineMode_t::REDLINE_SHOW_DELETE)); 1859 1860 _SaveMergeRedlines* pTmp = pRing; 1861 1862 do { 1863 nRet += pTmp->InsertRedline(); 1864 } while( pRing != ( pTmp = (_SaveMergeRedlines*)pTmp->GetNext() )); 1865 1866 while( pRing != pRing->GetNext() ) 1867 delete pRing->GetNext(); 1868 delete pRing; 1869 } 1870 } 1871 1872 rSrcDoc.SetRedlineMode( eSrcRedlMode ); 1873 if( !bSrcModified ) 1874 rSrcDoc.ResetModified(); 1875 1876 SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); 1877 1878 GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL); 1879 1880 return nRet; 1881 } 1882 1883 1884