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