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