xref: /AOO41X/main/sw/source/core/doc/doccomp.cxx (revision dec99bbd1eb6ae693d6ee672c1a69e3a32d917e7)
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