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