xref: /AOO41X/main/sw/source/core/doc/doccomp.cxx (revision ff0525f24f03981d56b7579b645949f111420994)
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:
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 );
103     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 );
108     sal_Bool GetChanged( sal_uLong nLine ) const
109         {
110             return (pChangedFlag && nLine < aLines.Count())
111                 ? pChangedFlag[ nLine ]
112                 : 0;
113         }
114 
115     sal_uLong GetLineCount() const      { return aLines.Count(); }
116     sal_uLong GetLineOffset() const     { return nSttLineNum; }
117     const CompareLine* GetLine( sal_uLong nLine ) const
118             { return aLines.GetObject( nLine ); }
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 
130         _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 
144     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 
160         sal_uLong GetIndex( sal_uLong n ) const { return pIndex[ n ]; }
161         sal_uLong GetLineNum( sal_uLong n ) const { return pLineNum[ n ]; }
162         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 
196 CompareLine::~CompareLine() {}
197 
198 // ----------------------------------------------------------------------
199 
200 CompareData::CompareData()
201     : pIndex( 0 ), pChangedFlag( 0 ), nSttLineNum( 0 )
202 {
203 }
204 
205 CompareData::~CompareData()
206 {
207     delete[] pIndex;
208     delete[] pChangedFlag;
209 }
210 
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 
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 
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 
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 
280 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 
298 void CompareData::ShowInsert( sal_uLong, sal_uLong )
299 {
300 }
301 
302 void CompareData::ShowDelete( const CompareData&, sal_uLong, sal_uLong, sal_uLong )
303 {
304 }
305 
306 void CompareData::CheckForChangesInLine( const CompareData& ,
307                                     sal_uLong&, sal_uLong&, sal_uLong&, sal_uLong& )
308 {
309 }
310 
311 // ----------------------------------------------------------------------
312 
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 
362 Hash::~Hash()
363 {
364     delete[] pHashArr;
365     delete[] pDataArr;
366 }
367 
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 
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 
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 
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 
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 
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 
615 Compare::MovedData::~MovedData()
616 {
617     delete [] pIndex;
618     delete [] pLineNum;
619 }
620 
621 // ----------------------------------------------------------------------
622 
623     // Suche die verschobenen Lines
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 
637 Compare::CompareSequence::~CompareSequence()
638 {
639     delete [] pMemory;
640 }
641 
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 
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 
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 
859     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:
885     SwCompareData( SwDoc& rD ) : rDoc( rD ), pInsRing(0), pDelRing(0) {}
886     virtual ~SwCompareData();
887 
888     void SetRedlinesToDoc( sal_Bool bUseDocInfo );
889 };
890 
891 // ----------------------------------------------------------------
892 
893 SwCompareLine::SwCompareLine( const SwNode& rNd )
894     : rNode( rNd )
895 {
896 }
897 
898 SwCompareLine::~SwCompareLine()
899 {
900 }
901 
902 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 
940 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 
961 sal_Bool SwCompareLine::Compare( const CompareLine& rLine ) const
962 {
963     return CompareNode( rNode, ((SwCompareLine&)rLine).rNode );
964 }
965 
966 namespace
967 {
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 
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 
1084 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 
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 
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 
1166 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 
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 
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 
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 
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 
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 
1357 void SwCompareData::ShowDelete( const CompareData& rData, sal_uLong nStt,
1358                                 sal_uLong nEnd, sal_uLong nInsPos )
1359 {
1360     SwNodeRange aRg(
1361         ((SwCompareLine*)rData.GetLine( nStt ))->GetNode(), 0,
1362         ((SwCompareLine*)rData.GetLine( nEnd-1 ))->GetEndNode(), 1 );
1363 
1364     sal_uInt16 nOffset = 0;
1365     const CompareLine* pLine;
1366     if( GetLineCount() == nInsPos )
1367     {
1368         pLine = GetLine( nInsPos-1 );
1369         nOffset = 1;
1370     }
1371     else
1372         pLine = GetLine( nInsPos );
1373 
1374     const SwNode* pLineNd;
1375     if( pLine )
1376     {
1377         if( nOffset )
1378             pLineNd = &((SwCompareLine*)pLine)->GetEndNode();
1379         else
1380             pLineNd = &((SwCompareLine*)pLine)->GetNode();
1381     }
1382     else
1383     {
1384         pLineNd = &rDoc.GetNodes().GetEndOfContent();
1385         nOffset = 0;
1386     }
1387 
1388     SwNodeIndex aInsPos( *pLineNd, nOffset );
1389     SwNodeIndex aSavePos( aInsPos, -1 );
1390 
1391     ((SwCompareData&)rData).rDoc.CopyWithFlyInFly( aRg, 0, aInsPos );
1392     rDoc.SetModified();
1393     aSavePos++;
1394 
1395     // #i65201#: These SwPaMs are calculated when the (old) delete-redlines are hidden,
1396     // they will be inserted when the delete-redlines are shown again.
1397     // To avoid unwanted insertions of delete-redlines into these new redlines, what happens
1398     // especially at the end of the document, I reduce the SwPaM by one node.
1399     // Before the new redlines are inserted, they have to expand again.
1400     SwPaM* pTmp = new SwPaM( aSavePos.GetNode(), aInsPos.GetNode(), 0, -1, pDelRing );
1401     if( !pDelRing )
1402         pDelRing = pTmp;
1403 
1404     if( pInsRing )
1405     {
1406         SwPaM* pCorr = (SwPaM*)pInsRing->GetPrev();
1407         if( *pCorr->GetPoint() == *pTmp->GetPoint() )
1408         {
1409             SwNodeIndex aTmpPos( pTmp->GetMark()->nNode, -1 );
1410             *pCorr->GetPoint() = SwPosition( aTmpPos );
1411         }
1412     }
1413 }
1414 
1415 void SwCompareData::CheckForChangesInLine( const CompareData& rData,
1416                                     sal_uLong& rStt, sal_uLong& rEnd,
1417                                     sal_uLong& rThisStt, sal_uLong& rThisEnd )
1418 {
1419     while( rStt < rEnd && rThisStt < rThisEnd )
1420     {
1421         SwCompareLine* pDstLn = (SwCompareLine*)GetLine( rThisStt );
1422         SwCompareLine* pSrcLn = (SwCompareLine*)rData.GetLine( rStt );
1423         if( !pDstLn->ChangesInLine( *pSrcLn, pInsRing, pDelRing ) )
1424             break;
1425 
1426         ++rStt;
1427         ++rThisStt;
1428     }
1429 }
1430 
1431 void SwCompareData::SetRedlinesToDoc( sal_Bool bUseDocInfo )
1432 {
1433     SwPaM* pTmp = pDelRing;
1434 
1435     // Bug #83296#: get the Author / TimeStamp from the "other"
1436     //              document info
1437     sal_uInt16 nAuthor = rDoc.GetRedlineAuthor();
1438     DateTime aTimeStamp;
1439     SwDocShell *pDocShell(rDoc.GetDocShell());
1440     DBG_ASSERT(pDocShell, "no SwDocShell");
1441     if (pDocShell) {
1442         uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1443             pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1444         uno::Reference<document::XDocumentProperties> xDocProps(
1445             xDPS->getDocumentProperties());
1446         DBG_ASSERT(xDocProps.is(), "Doc has no DocumentProperties");
1447 
1448         if( bUseDocInfo && xDocProps.is() ) {
1449             String aTmp( 1 == xDocProps->getEditingCycles()
1450                                 ? xDocProps->getAuthor()
1451                                 : xDocProps->getModifiedBy() );
1452             util::DateTime uDT( 1 == xDocProps->getEditingCycles()
1453                                 ? xDocProps->getCreationDate()
1454                                 : xDocProps->getModificationDate() );
1455             Date d(uDT.Day, uDT.Month, uDT.Year);
1456             Time t(uDT.Hours, uDT.Minutes, uDT.Seconds, uDT.HundredthSeconds);
1457             DateTime aDT(d,t);
1458 
1459             if( aTmp.Len() )
1460             {
1461                 nAuthor = rDoc.InsertRedlineAuthor( aTmp );
1462                 aTimeStamp = aDT;
1463             }
1464         }
1465     }
1466 
1467     if( pTmp )
1468     {
1469         SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_DELETE, nAuthor, aTimeStamp,
1470                                     aEmptyStr, 0, 0 );
1471         do {
1472             // #i65201#: Expand again, see comment above.
1473             if( pTmp->GetPoint()->nContent == 0 )
1474             {
1475                 pTmp->GetPoint()->nNode++;
1476                 pTmp->GetPoint()->nContent.Assign( pTmp->GetCntntNode(), 0 );
1477             }
1478             // --> mst 2010-05-17 #i101009#
1479             // prevent redlines that end on structural end node
1480             if (& rDoc.GetNodes().GetEndOfContent() ==
1481                 & pTmp->GetPoint()->nNode.GetNode())
1482             {
1483                 pTmp->GetPoint()->nNode--;
1484                 SwCntntNode *const pContentNode( pTmp->GetCntntNode() );
1485                 pTmp->GetPoint()->nContent.Assign( pContentNode,
1486                         (pContentNode) ? pContentNode->Len() : 0 );
1487             }
1488             // <--
1489 
1490             rDoc.DeleteRedline( *pTmp, false, USHRT_MAX );
1491 
1492             if (rDoc.GetIDocumentUndoRedo().DoesUndo())
1493             {
1494                 SwUndo *const pUndo(new SwUndoCompDoc( *pTmp, sal_False )) ;
1495                 rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
1496             }
1497             rDoc.AppendRedline( new SwRedline( aRedlnData, *pTmp ), true );
1498 
1499         } while( pDelRing != ( pTmp = (SwPaM*)pTmp->GetNext() ));
1500     }
1501 
1502     pTmp = pInsRing;
1503     if( pTmp )
1504     {
1505         do {
1506             if( pTmp->GetPoint()->nContent == 0 )
1507             {
1508                 pTmp->GetPoint()->nNode++;
1509                 pTmp->GetPoint()->nContent.Assign( pTmp->GetCntntNode(), 0 );
1510             }
1511             // --> mst 2010-05-17 #i101009#
1512             // prevent redlines that end on structural end node
1513             if (& rDoc.GetNodes().GetEndOfContent() ==
1514                 & pTmp->GetPoint()->nNode.GetNode())
1515             {
1516                 pTmp->GetPoint()->nNode--;
1517                 SwCntntNode *const pContentNode( pTmp->GetCntntNode() );
1518                 pTmp->GetPoint()->nContent.Assign( pContentNode,
1519                         (pContentNode) ? pContentNode->Len() : 0 );
1520             }
1521             // <--
1522         } while( pInsRing != ( pTmp = (SwPaM*)pTmp->GetNext() ));
1523         SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_INSERT, nAuthor, aTimeStamp,
1524                                     aEmptyStr, 0, 0 );
1525 
1526         // zusammenhaengende zusammenfassen
1527         if( pTmp->GetNext() != pInsRing )
1528         {
1529             const SwCntntNode* pCNd;
1530             do {
1531                 SwPosition& rSttEnd = *pTmp->End(),
1532                           & rEndStt = *((SwPaM*)pTmp->GetNext())->Start();
1533                 if( rSttEnd == rEndStt ||
1534                     (!rEndStt.nContent.GetIndex() &&
1535                     rEndStt.nNode.GetIndex() - 1 == rSttEnd.nNode.GetIndex() &&
1536                     0 != ( pCNd = rSttEnd.nNode.GetNode().GetCntntNode() )
1537                         ? rSttEnd.nContent.GetIndex() == pCNd->Len()
1538                         : 0 ))
1539                 {
1540                     if( pTmp->GetNext() == pInsRing )
1541                     {
1542                         // liegen hintereinander also zusammen fassen
1543                         rEndStt = *pTmp->Start();
1544                         delete pTmp;
1545                         pTmp = pInsRing;
1546                     }
1547                     else
1548                     {
1549                         // liegen hintereinander also zusammen fassen
1550                         rSttEnd = *((SwPaM*)pTmp->GetNext())->End();
1551                         delete pTmp->GetNext();
1552                     }
1553                 }
1554                 else
1555                     pTmp = (SwPaM*)pTmp->GetNext();
1556             } while( pInsRing != pTmp );
1557         }
1558 
1559         do {
1560             if( rDoc.AppendRedline( new SwRedline( aRedlnData, *pTmp ), true) &&
1561                 rDoc.GetIDocumentUndoRedo().DoesUndo())
1562             {
1563                 SwUndo *const pUndo(new SwUndoCompDoc( *pTmp, sal_True ));
1564                 rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
1565             }
1566         } while( pInsRing != ( pTmp = (SwPaM*)pTmp->GetNext() ));
1567     }
1568 }
1569 
1570 /*  */
1571 
1572 
1573 
1574     // returnt (?die Anzahl der Unterschiede?) ob etwas unterschiedlich ist
1575 long SwDoc::CompareDoc( const SwDoc& rDoc )
1576 {
1577     if( &rDoc == this )
1578         return 0;
1579 
1580     long nRet = 0;
1581 
1582     GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
1583     sal_Bool bDocWasModified = IsModified();
1584     SwDoc& rSrcDoc = (SwDoc&)rDoc;
1585     sal_Bool bSrcModified = rSrcDoc.IsModified();
1586 
1587     RedlineMode_t eSrcRedlMode = rSrcDoc.GetRedlineMode();
1588     rSrcDoc.SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_INSERT );
1589     SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT));
1590 
1591     SwCompareData aD0( rSrcDoc );
1592     SwCompareData aD1( *this );
1593 
1594     aD1.CompareLines( aD0 );
1595 
1596     nRet = aD1.ShowDiffs( aD0 );
1597 
1598     if( nRet )
1599     {
1600       SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON |
1601                        nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1602 
1603         aD1.SetRedlinesToDoc( !bDocWasModified );
1604         SetModified();
1605     }
1606 
1607     rSrcDoc.SetRedlineMode( eSrcRedlMode );
1608     SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1609 
1610     if( !bSrcModified )
1611         rSrcDoc.ResetModified();
1612 
1613     GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
1614 
1615     return nRet;
1616 }
1617 
1618 
1619 class _SaveMergeRedlines : public Ring
1620 {
1621     const SwRedline* pSrcRedl;
1622     SwRedline* pDestRedl;
1623 public:
1624     _SaveMergeRedlines( const SwNode& rDstNd,
1625                         const SwRedline& rSrcRedl, Ring* pRing );
1626     sal_uInt16 InsertRedline();
1627 
1628     SwRedline* GetDestRedline() { return pDestRedl; }
1629 };
1630 
1631 _SaveMergeRedlines::_SaveMergeRedlines( const SwNode& rDstNd,
1632                         const SwRedline& rSrcRedl, Ring* pRing )
1633     : Ring( pRing ), pSrcRedl( &rSrcRedl )
1634 {
1635     SwPosition aPos( rDstNd );
1636 
1637     const SwPosition* pStt = rSrcRedl.Start();
1638     if( rDstNd.IsCntntNode() )
1639         aPos.nContent.Assign( ((SwCntntNode*)&rDstNd), pStt->nContent.GetIndex() );
1640     pDestRedl = new SwRedline( rSrcRedl.GetRedlineData(), aPos );
1641 
1642     if( nsRedlineType_t::REDLINE_DELETE == pDestRedl->GetType() )
1643     {
1644         // den Bereich als geloescht kennzeichnen
1645         const SwPosition* pEnd = pStt == rSrcRedl.GetPoint()
1646                                             ? rSrcRedl.GetMark()
1647                                             : rSrcRedl.GetPoint();
1648 
1649         pDestRedl->SetMark();
1650         pDestRedl->GetPoint()->nNode += pEnd->nNode.GetIndex() -
1651                                         pStt->nNode.GetIndex();
1652         pDestRedl->GetPoint()->nContent.Assign( pDestRedl->GetCntntNode(),
1653                                                 pEnd->nContent.GetIndex() );
1654     }
1655 }
1656 
1657 sal_uInt16 _SaveMergeRedlines::InsertRedline()
1658 {
1659     sal_uInt16 nIns = 0;
1660     SwDoc* pDoc = pDestRedl->GetDoc();
1661 
1662     if( nsRedlineType_t::REDLINE_INSERT == pDestRedl->GetType() )
1663     {
1664         // der Teil wurde eingefuegt, also kopiere ihn aus dem SourceDoc
1665         ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
1666 
1667         SwNodeIndex aSaveNd( pDestRedl->GetPoint()->nNode, -1 );
1668         xub_StrLen nSaveCnt = pDestRedl->GetPoint()->nContent.GetIndex();
1669 
1670         RedlineMode_t eOld = pDoc->GetRedlineMode();
1671         pDoc->SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_IGNORE));
1672 
1673         pSrcRedl->GetDoc()->CopyRange(
1674                 *const_cast<SwPaM*>(static_cast<const SwPaM*>(pSrcRedl)),
1675                 *pDestRedl->GetPoint(), false );
1676 
1677         pDoc->SetRedlineMode_intern( eOld );
1678 
1679         pDestRedl->SetMark();
1680         aSaveNd++;
1681         pDestRedl->GetMark()->nNode = aSaveNd;
1682         pDestRedl->GetMark()->nContent.Assign( aSaveNd.GetNode().GetCntntNode(),
1683                                                 nSaveCnt );
1684 
1685         if( GetPrev() != this )
1686         {
1687             SwPaM* pTmpPrev = ((_SaveMergeRedlines*)GetPrev())->pDestRedl;
1688             if( pTmpPrev && *pTmpPrev->GetPoint() == *pDestRedl->GetPoint() )
1689                 *pTmpPrev->GetPoint() = *pDestRedl->GetMark();
1690         }
1691     }
1692     else
1693     {
1694         //JP 21.09.98: Bug 55909
1695         // falls im Doc auf gleicher Pos aber schon ein geloeschter oder
1696         // eingefuegter ist, dann muss dieser gesplittet werden!
1697         SwPosition* pDStt = pDestRedl->GetMark(),
1698                   * pDEnd = pDestRedl->GetPoint();
1699         sal_uInt16 n = 0;
1700 
1701             // zur StartPos das erste Redline suchen
1702         if( !pDoc->GetRedline( *pDStt, &n ) && n )
1703             --n;
1704 
1705         const SwRedlineTbl& rRedlineTbl = pDoc->GetRedlineTbl();
1706         for( ; n < rRedlineTbl.Count(); ++n )
1707         {
1708             SwRedline* pRedl = rRedlineTbl[ n ];
1709             SwPosition* pRStt = pRedl->Start(),
1710                       * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
1711                                                            : pRedl->GetPoint();
1712             if( nsRedlineType_t::REDLINE_DELETE == pRedl->GetType() ||
1713                 nsRedlineType_t::REDLINE_INSERT == pRedl->GetType() )
1714             {
1715                 SwComparePosition eCmpPos = ComparePosition( *pDStt, *pDEnd, *pRStt, *pREnd );
1716                 switch( eCmpPos )
1717                 {
1718                 case POS_COLLIDE_START:
1719                 case POS_BEHIND:
1720                     break;
1721 
1722                 case POS_INSIDE:
1723                 case POS_EQUAL:
1724                     delete pDestRedl, pDestRedl = 0;
1725                     // break; -> kein break !!!!
1726 
1727                 case POS_COLLIDE_END:
1728                 case POS_BEFORE:
1729                     n = rRedlineTbl.Count();
1730                     break;
1731 
1732                 case POS_OUTSIDE:
1733                     {
1734                         SwRedline* pCpyRedl = new SwRedline(
1735                             pDestRedl->GetRedlineData(), *pDStt );
1736                         pCpyRedl->SetMark();
1737                         *pCpyRedl->GetPoint() = *pRStt;
1738 
1739                         SwUndoCompDoc *const pUndo =
1740                             (pDoc->GetIDocumentUndoRedo().DoesUndo())
1741                                     ? new SwUndoCompDoc( *pCpyRedl ) : 0;
1742 
1743                         // now modify doc: append redline, undo (and count)
1744                         pDoc->AppendRedline( pCpyRedl, true );
1745                         if( pUndo )
1746                         {
1747                             pDoc->GetIDocumentUndoRedo().AppendUndo(pUndo);
1748                         }
1749                         ++nIns;
1750 
1751                         *pDStt = *pREnd;
1752 
1753                         // dann solle man neu anfangen
1754                         n = USHRT_MAX;
1755                     }
1756                     break;
1757 
1758                 case POS_OVERLAP_BEFORE:
1759                     *pDEnd = *pRStt;
1760                     break;
1761 
1762                 case POS_OVERLAP_BEHIND:
1763                     *pDStt = *pREnd;
1764                     break;
1765                 }
1766             }
1767             else if( *pDEnd <= *pRStt )
1768                 break;
1769         }
1770 
1771     }
1772 
1773     if( pDestRedl )
1774     {
1775         SwUndoCompDoc *const pUndo = (pDoc->GetIDocumentUndoRedo().DoesUndo())
1776             ? new SwUndoCompDoc( *pDestRedl ) : 0;
1777 
1778         // now modify doc: append redline, undo (and count)
1779         bool bRedlineAccepted = pDoc->AppendRedline( pDestRedl, true );
1780         if( pUndo )
1781         {
1782             pDoc->GetIDocumentUndoRedo().AppendUndo( pUndo );
1783         }
1784         ++nIns;
1785 
1786         // if AppendRedline has deleted our redline, we may not keep a
1787         // reference to it
1788         if( ! bRedlineAccepted )
1789             pDestRedl = NULL;
1790     }
1791     return nIns;
1792 }
1793 
1794 // merge zweier Dokumente
1795 long SwDoc::MergeDoc( const SwDoc& rDoc )
1796 {
1797     if( &rDoc == this )
1798         return 0;
1799 
1800     long nRet = 0;
1801 
1802     GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
1803 
1804     SwDoc& rSrcDoc = (SwDoc&)rDoc;
1805     sal_Bool bSrcModified = rSrcDoc.IsModified();
1806 
1807     RedlineMode_t eSrcRedlMode = rSrcDoc.GetRedlineMode();
1808     rSrcDoc.SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE );
1809     SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE );
1810 
1811     SwCompareData aD0( rSrcDoc );
1812     SwCompareData aD1( *this );
1813 
1814     aD1.CompareLines( aD0 );
1815 
1816     if( !aD1.HasDiffs( aD0 ) )
1817     {
1818         // jetzt wollen wir alle Redlines aus dem SourceDoc zu uns bekommen
1819 
1820         // suche alle Insert - Redlines aus dem SourceDoc und bestimme
1821         // deren Position im DestDoc
1822         _SaveMergeRedlines* pRing = 0;
1823         const SwRedlineTbl& rSrcRedlTbl = rSrcDoc.GetRedlineTbl();
1824         sal_uLong nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex();
1825         sal_uLong nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex();
1826         for( sal_uInt16 n = 0; n < rSrcRedlTbl.Count(); ++n )
1827         {
1828             const SwRedline* pRedl = rSrcRedlTbl[ n ];
1829             sal_uLong nNd = pRedl->GetPoint()->nNode.GetIndex();
1830             RedlineType_t eType = pRedl->GetType();
1831             if( nEndOfExtra < nNd &&
1832                 ( nsRedlineType_t::REDLINE_INSERT == eType || nsRedlineType_t::REDLINE_DELETE == eType ))
1833             {
1834                 const SwNode* pDstNd = GetNodes()[
1835                                         nMyEndOfExtra + nNd - nEndOfExtra ];
1836 
1837                 // Position gefunden. Dann muss im DestDoc auch
1838                 // in der Line das Redline eingefuegt werden
1839                 _SaveMergeRedlines* pTmp = new _SaveMergeRedlines(
1840                                                     *pDstNd, *pRedl, pRing );
1841                 if( !pRing )
1842                     pRing = pTmp;
1843             }
1844         }
1845 
1846         if( pRing )
1847         {
1848             // dann alle ins DestDoc ueber nehmen
1849           rSrcDoc.SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1850 
1851           SetRedlineMode((RedlineMode_t)(
1852                                       nsRedlineMode_t::REDLINE_ON |
1853                                       nsRedlineMode_t::REDLINE_SHOW_INSERT |
1854                                       nsRedlineMode_t::REDLINE_SHOW_DELETE));
1855 
1856             _SaveMergeRedlines* pTmp = pRing;
1857 
1858             do {
1859                 nRet += pTmp->InsertRedline();
1860             } while( pRing != ( pTmp = (_SaveMergeRedlines*)pTmp->GetNext() ));
1861 
1862             while( pRing != pRing->GetNext() )
1863                 delete pRing->GetNext();
1864             delete pRing;
1865         }
1866     }
1867 
1868     rSrcDoc.SetRedlineMode( eSrcRedlMode );
1869     if( !bSrcModified )
1870         rSrcDoc.ResetModified();
1871 
1872     SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1873 
1874     GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
1875 
1876     return nRet;
1877 }
1878 
1879 
1880