diff options
Diffstat (limited to 'sw/source/core/doc/doccomp.cxx')
-rw-r--r-- | sw/source/core/doc/doccomp.cxx | 1876 |
1 files changed, 1876 insertions, 0 deletions
diff --git a/sw/source/core/doc/doccomp.cxx b/sw/source/core/doc/doccomp.cxx new file mode 100644 index 000000000000..170193778421 --- /dev/null +++ b/sw/source/core/doc/doccomp.cxx @@ -0,0 +1,1876 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <hintids.hxx> +#include <tools/list.hxx> +#include <vcl/vclenum.hxx> +#include <editeng/crsditem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/udlnitem.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <redline.hxx> +#include <undobj.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <docsh.hxx> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> + +using namespace ::com::sun::star; + + +class CompareLine +{ +public: + CompareLine() {} + virtual ~CompareLine(); + + virtual ULONG GetHashValue() const = 0; + virtual BOOL Compare( const CompareLine& rLine ) const = 0; +}; + +DECLARE_LIST( CompareList, CompareLine* ) + +class CompareData +{ + ULONG* pIndex; + BOOL* pChangedFlag; + +protected: + CompareList aLines; + ULONG nSttLineNum; + + // Anfang und Ende beschneiden und alle anderen in das + // LinesArray setzen + virtual void CheckRanges( CompareData& ) = 0; + +public: + CompareData(); + virtual ~CompareData(); + + // gibt es unterschiede? + BOOL HasDiffs( const CompareData& rData ) const; + + // startet das Vergleichen und Erzeugen der Unterschiede zweier + // Dokumente + void CompareLines( CompareData& rData ); + // lasse die Unterschiede anzeigen - ruft die beiden Methoden + // ShowInsert / ShowDelete. Diese bekommen die Start und EndLine-Nummer + // uebergeben. Die Abbildung auf den tatsaechline Inhalt muss die + // Ableitung uebernehmen! + ULONG ShowDiffs( const CompareData& rData ); + + virtual void ShowInsert( ULONG nStt, ULONG nEnd ); + virtual void ShowDelete( const CompareData& rData, ULONG nStt, + ULONG nEnd, ULONG nInsPos ); + virtual void CheckForChangesInLine( const CompareData& rData, + ULONG& nStt, ULONG& nEnd, + ULONG& nThisStt, ULONG& nThisEnd ); + + // Eindeutigen Index fuer eine Line setzen. Gleiche Lines haben den + // selben Index; auch in den anderen CompareData! + void SetIndex( ULONG nLine, ULONG nIndex ); + ULONG GetIndex( ULONG nLine ) const + { return nLine < aLines.Count() ? pIndex[ nLine ] : 0; } + + // setze/erfrage ob eine Zeile veraendert ist + void SetChanged( ULONG nLine, BOOL bFlag = TRUE ); + BOOL GetChanged( ULONG nLine ) const + { + return (pChangedFlag && nLine < aLines.Count()) + ? pChangedFlag[ nLine ] + : 0; + } + + ULONG GetLineCount() const { return aLines.Count(); } + ULONG GetLineOffset() const { return nSttLineNum; } + const CompareLine* GetLine( ULONG nLine ) const + { return aLines.GetObject( nLine ); } + void InsertLine( CompareLine* pLine ) + { aLines.Insert( pLine, LIST_APPEND ); } +}; + +class Hash +{ + struct _HashData + { + ULONG nNext, nHash; + const CompareLine* pLine; + + _HashData() + : nNext( 0 ), nHash( 0 ), pLine(0) {} + }; + + ULONG* pHashArr; + _HashData* pDataArr; + ULONG nCount, nPrime; + +public: + Hash( ULONG nSize ); + ~Hash(); + + void CalcHashValue( CompareData& rData ); + + ULONG GetCount() const { return nCount; } +}; + +class Compare +{ +public: + class MovedData + { + ULONG* pIndex; + ULONG* pLineNum; + ULONG nCount; + + public: + MovedData( CompareData& rData, sal_Char* pDiscard ); + ~MovedData(); + + ULONG GetIndex( ULONG n ) const { return pIndex[ n ]; } + ULONG GetLineNum( ULONG n ) const { return pLineNum[ n ]; } + ULONG GetCount() const { return nCount; } + }; + +private: + // Suche die verschobenen Lines + class CompareSequence + { + CompareData &rData1, &rData2; + const MovedData &rMoved1, &rMoved2; + long *pMemory, *pFDiag, *pBDiag; + + void Compare( ULONG nStt1, ULONG nEnd1, ULONG nStt2, ULONG nEnd2 ); + ULONG CheckDiag( ULONG nStt1, ULONG nEnd1, + ULONG nStt2, ULONG nEnd2, ULONG* pCost ); + public: + CompareSequence( CompareData& rData1, CompareData& rData2, + const MovedData& rD1, const MovedData& rD2 ); + ~CompareSequence(); + }; + + + static void CountDifference( const CompareData& rData, ULONG* pCounts ); + static void SetDiscard( const CompareData& rData, + sal_Char* pDiscard, ULONG* pCounts ); + static void CheckDiscard( ULONG nLen, sal_Char* pDiscard ); + static ULONG SetChangedFlag( CompareData& rData, sal_Char* pDiscard, int bFirst ); + static void ShiftBoundaries( CompareData& rData1, CompareData& rData2 ); + +public: + Compare( ULONG nDiff, CompareData& rData1, CompareData& rData2 ); +}; + +// ==================================================================== + +CompareLine::~CompareLine() {} + +// ---------------------------------------------------------------------- + +CompareData::CompareData() + : pIndex( 0 ), pChangedFlag( 0 ), nSttLineNum( 0 ) +{ +} + +CompareData::~CompareData() +{ + delete[] pIndex; + delete[] pChangedFlag; +} + +void CompareData::SetIndex( ULONG nLine, ULONG nIndex ) +{ + if( !pIndex ) + { + pIndex = new ULONG[ aLines.Count() ]; + memset( pIndex, 0, aLines.Count() * sizeof( ULONG ) ); + } + if( nLine < aLines.Count() ) + pIndex[ nLine ] = nIndex; +} + +void CompareData::SetChanged( ULONG nLine, BOOL bFlag ) +{ + if( !pChangedFlag ) + { + pChangedFlag = new BOOL[ aLines.Count() +1 ]; + memset( pChangedFlag, 0, aLines.Count() +1 * sizeof( BOOL ) ); + } + if( nLine < aLines.Count() ) + pChangedFlag[ nLine ] = bFlag; +} + +void CompareData::CompareLines( CompareData& rData ) +{ + CheckRanges( rData ); + + ULONG nDifferent; + { + Hash aH( GetLineCount() + rData.GetLineCount() + 1 ); + aH.CalcHashValue( *this ); + aH.CalcHashValue( rData ); + nDifferent = aH.GetCount(); + } + { + Compare aComp( nDifferent, *this, rData ); + } +} + +ULONG CompareData::ShowDiffs( const CompareData& rData ) +{ + ULONG nLen1 = rData.GetLineCount(), nLen2 = GetLineCount(); + ULONG nStt1 = 0, nStt2 = 0; + ULONG nCnt = 0; + + while( nStt1 < nLen1 || nStt2 < nLen2 ) + { + if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) ) + { + ULONG nSav1 = nStt1, nSav2 = nStt2; + while( nStt1 < nLen1 && rData.GetChanged( nStt1 )) ++nStt1; + while( nStt2 < nLen2 && GetChanged( nStt2 )) ++nStt2; + + // rData ist das Original, + // this ist das, in das die Veraenderungen sollen + if( nSav2 != nStt2 && nSav1 != nStt1 ) + CheckForChangesInLine( rData, nSav1, nStt1, nSav2, nStt2 ); + + if( nSav2 != nStt2 ) + ShowInsert( nSav2, nStt2 ); + + if( nSav1 != nStt1 ) + ShowDelete( rData, nSav1, nStt1, nStt2 ); + ++nCnt; + } + ++nStt1, ++nStt2; + } + return nCnt; +} + +BOOL CompareData::HasDiffs( const CompareData& rData ) const +{ + BOOL bRet = FALSE; + ULONG nLen1 = rData.GetLineCount(), nLen2 = GetLineCount(); + ULONG nStt1 = 0, nStt2 = 0; + + while( nStt1 < nLen1 || nStt2 < nLen2 ) + { + if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) ) + { + bRet = TRUE; + break; + } + ++nStt1, ++nStt2; + } + return bRet; +} + +void CompareData::ShowInsert( ULONG, ULONG ) +{ +} + +void CompareData::ShowDelete( const CompareData&, ULONG, ULONG, ULONG ) +{ +} + +void CompareData::CheckForChangesInLine( const CompareData& , + ULONG&, ULONG&, ULONG&, ULONG& ) +{ +} + +// ---------------------------------------------------------------------- + +Hash::Hash( ULONG nSize ) + : nCount( 1 ) +{ + +static const ULONG primes[] = +{ + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, /* Preposterously large . . . */ + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647, + 0 +}; + int i; + + pDataArr = new _HashData[ nSize ]; + pDataArr[0].nNext = 0; + pDataArr[0].nHash = 0, + pDataArr[0].pLine = 0; + + for( i = 0; primes[i] < nSize / 3; i++) + if( !primes[i] ) + { + pHashArr = 0; + return; + } + nPrime = primes[ i ]; + pHashArr = new ULONG[ nPrime ]; + memset( pHashArr, 0, nPrime * sizeof( ULONG ) ); +} + +Hash::~Hash() +{ + delete[] pHashArr; + delete[] pDataArr; +} + +void Hash::CalcHashValue( CompareData& rData ) +{ + if( pHashArr ) + { + for( ULONG n = 0; n < rData.GetLineCount(); ++n ) + { + const CompareLine* pLine = rData.GetLine( n ); + ASSERT( pLine, "wo ist die Line?" ); + ULONG nH = pLine->GetHashValue(); + + ULONG* pFound = &pHashArr[ nH % nPrime ]; + ULONG i; + for( i = *pFound; ; i = pDataArr[i].nNext ) + if( !i ) + { + i = nCount++; + pDataArr[i].nNext = *pFound; + pDataArr[i].nHash = nH; + pDataArr[i].pLine = pLine; + *pFound = i; + break; + } + else if( pDataArr[i].nHash == nH && + pDataArr[i].pLine->Compare( *pLine )) + break; + + rData.SetIndex( n, i ); + } + } +} + +// ---------------------------------------------------------------------- + +Compare::Compare( ULONG nDiff, CompareData& rData1, CompareData& rData2 ) +{ + MovedData *pMD1, *pMD2; + // Suche die unterschiedlichen Lines + { + sal_Char* pDiscard1 = new sal_Char[ rData1.GetLineCount() ]; + sal_Char* pDiscard2 = new sal_Char[ rData2.GetLineCount() ]; + + ULONG* pCount1 = new ULONG[ nDiff ]; + ULONG* pCount2 = new ULONG[ nDiff ]; + memset( pCount1, 0, nDiff * sizeof( ULONG )); + memset( pCount2, 0, nDiff * sizeof( ULONG )); + + // stelle fest, welche Indizies in den CompareData mehrfach vergeben wurden + CountDifference( rData1, pCount1 ); + CountDifference( rData2, pCount2 ); + + // alle die jetzt nur einmal vorhanden sind, sind eingefuegt oder + // geloescht worden. Alle die im anderen auch vorhanden sind, sind + // verschoben worden + SetDiscard( rData1, pDiscard1, pCount2 ); + SetDiscard( rData2, pDiscard2, pCount1 ); + + // die Arrays koennen wir wieder vergessen + delete [] pCount1; delete [] pCount2; + + CheckDiscard( rData1.GetLineCount(), pDiscard1 ); + CheckDiscard( rData2.GetLineCount(), pDiscard2 ); + + pMD1 = new MovedData( rData1, pDiscard1 ); + pMD2 = new MovedData( rData2, pDiscard2 ); + + // die Arrays koennen wir wieder vergessen + delete [] pDiscard1; delete [] pDiscard2; + } + + { + CompareSequence aTmp( rData1, rData2, *pMD1, *pMD2 ); + } + + ShiftBoundaries( rData1, rData2 ); + + delete pMD1; + delete pMD2; +} + + + +void Compare::CountDifference( const CompareData& rData, ULONG* pCounts ) +{ + ULONG nLen = rData.GetLineCount(); + for( ULONG n = 0; n < nLen; ++n ) + { + ULONG nIdx = rData.GetIndex( n ); + ++pCounts[ nIdx ]; + } +} + +void Compare::SetDiscard( const CompareData& rData, + sal_Char* pDiscard, ULONG* pCounts ) +{ + ULONG nLen = rData.GetLineCount(); + + // berechne Max in Abhanegigkeit zur LineAnzahl + USHORT nMax = 5; + ULONG n; + + for( n = nLen / 64; ( n = n >> 2 ) > 0; ) + nMax <<= 1; + + for( n = 0; n < nLen; ++n ) + { + ULONG nIdx = rData.GetIndex( n ); + if( nIdx ) + { + nIdx = pCounts[ nIdx ]; + pDiscard[ n ] = !nIdx ? 1 : nIdx > nMax ? 2 : 0; + } + else + pDiscard[ n ] = 0; + } +} + +void Compare::CheckDiscard( ULONG nLen, sal_Char* pDiscard ) +{ + for( ULONG n = 0; n < nLen; ++n ) + { + if( 2 == pDiscard[ n ] ) + pDiscard[n] = 0; + else if( pDiscard[ n ] ) + { + ULONG j; + ULONG length; + ULONG provisional = 0; + + /* Find end of this run of discardable lines. + Count how many are provisionally discardable. */ + for (j = n; j < nLen; j++) + { + if( !pDiscard[j] ) + break; + if( 2 == pDiscard[j] ) + ++provisional; + } + + /* Cancel provisional discards at end, and shrink the run. */ + while( j > n && 2 == pDiscard[j - 1] ) + pDiscard[ --j ] = 0, --provisional; + + /* Now we have the length of a run of discardable lines + whose first and last are not provisional. */ + length = j - n; + + /* If 1/4 of the lines in the run are provisional, + cancel discarding of all provisional lines in the run. */ + if (provisional * 4 > length) + { + while (j > n) + if (pDiscard[--j] == 2) + pDiscard[j] = 0; + } + else + { + ULONG consec; + ULONG minimum = 1; + ULONG tem = length / 4; + + /* MINIMUM is approximate square root of LENGTH/4. + A subrun of two or more provisionals can stand + when LENGTH is at least 16. + A subrun of 4 or more can stand when LENGTH >= 64. */ + while ((tem = tem >> 2) > 0) + minimum *= 2; + minimum++; + + /* Cancel any subrun of MINIMUM or more provisionals + within the larger run. */ + for (j = 0, consec = 0; j < length; j++) + if (pDiscard[n + j] != 2) + consec = 0; + else if (minimum == ++consec) + /* Back up to start of subrun, to cancel it all. */ + j -= consec; + else if (minimum < consec) + pDiscard[n + j] = 0; + + /* Scan from beginning of run + until we find 3 or more nonprovisionals in a row + or until the first nonprovisional at least 8 lines in. + Until that point, cancel any provisionals. */ + for (j = 0, consec = 0; j < length; j++) + { + if (j >= 8 && pDiscard[n + j] == 1) + break; + if (pDiscard[n + j] == 2) + consec = 0, pDiscard[n + j] = 0; + else if (pDiscard[n + j] == 0) + consec = 0; + else + consec++; + if (consec == 3) + break; + } + + /* I advances to the last line of the run. */ + n += length - 1; + + /* Same thing, from end. */ + for (j = 0, consec = 0; j < length; j++) + { + if (j >= 8 && pDiscard[n - j] == 1) + break; + if (pDiscard[n - j] == 2) + consec = 0, pDiscard[n - j] = 0; + else if (pDiscard[n - j] == 0) + consec = 0; + else + consec++; + if (consec == 3) + break; + } + } + } + } +} + +// ---------------------------------------------------------------------- + +Compare::MovedData::MovedData( CompareData& rData, sal_Char* pDiscard ) + : pIndex( 0 ), pLineNum( 0 ), nCount( 0 ) +{ + ULONG nLen = rData.GetLineCount(); + ULONG n; + + for( n = 0; n < nLen; ++n ) + if( pDiscard[ n ] ) + rData.SetChanged( n ); + else + ++nCount; + + if( nCount ) + { + pIndex = new ULONG[ nCount ]; + pLineNum = new ULONG[ nCount ]; + + for( n = 0, nCount = 0; n < nLen; ++n ) + if( !pDiscard[ n ] ) + { + pIndex[ nCount ] = rData.GetIndex( n ); + pLineNum[ nCount++ ] = n; + } + } +} + +Compare::MovedData::~MovedData() +{ + delete pIndex; + delete pLineNum; +} + +// ---------------------------------------------------------------------- + + // Suche die verschobenen Lines +Compare::CompareSequence::CompareSequence( + CompareData& rD1, CompareData& rD2, + const MovedData& rMD1, const MovedData& rMD2 ) + : rData1( rD1 ), rData2( rD2 ), rMoved1( rMD1 ), rMoved2( rMD2 ) +{ + ULONG nSize = rMD1.GetCount() + rMD2.GetCount() + 3; + pMemory = new long[ nSize * 2 ]; + pFDiag = pMemory + ( rMD2.GetCount() + 1 ); + pBDiag = pMemory + ( nSize + rMD2.GetCount() + 1 ); + + Compare( 0, rMD1.GetCount(), 0, rMD2.GetCount() ); +} + +Compare::CompareSequence::~CompareSequence() +{ + delete pMemory; +} + +void Compare::CompareSequence::Compare( ULONG nStt1, ULONG nEnd1, + ULONG nStt2, ULONG nEnd2 ) +{ + /* Slide down the bottom initial diagonal. */ + while( nStt1 < nEnd1 && nStt2 < nEnd2 && + rMoved1.GetIndex( nStt1 ) == rMoved2.GetIndex( nStt2 )) + ++nStt1, ++nStt2; + + /* Slide up the top initial diagonal. */ + while( nEnd1 > nStt1 && nEnd2 > nStt2 && + rMoved1.GetIndex( nEnd1 - 1 ) == rMoved2.GetIndex( nEnd2 - 1 )) + --nEnd1, --nEnd2; + + /* Handle simple cases. */ + if( nStt1 == nEnd1 ) + while( nStt2 < nEnd2 ) + rData2.SetChanged( rMoved2.GetLineNum( nStt2++ )); + + else if (nStt2 == nEnd2) + while (nStt1 < nEnd1) + rData1.SetChanged( rMoved1.GetLineNum( nStt1++ )); + + else + { + ULONG c, d, b; + + /* Find a point of correspondence in the middle of the files. */ + + d = CheckDiag( nStt1, nEnd1, nStt2, nEnd2, &c ); + b = pBDiag[ d ]; + + if( 1 != c ) + { + /* Use that point to split this problem into two subproblems. */ + Compare( nStt1, b, nStt2, b - d ); + /* This used to use f instead of b, + but that is incorrect! + It is not necessarily the case that diagonal d + has a snake from b to f. */ + Compare( b, nEnd1, b - d, nEnd2 ); + } + } +} + +ULONG Compare::CompareSequence::CheckDiag( ULONG nStt1, ULONG nEnd1, + ULONG nStt2, ULONG nEnd2, ULONG* pCost ) +{ + const long dmin = nStt1 - nEnd2; /* Minimum valid diagonal. */ + const long dmax = nEnd1 - nStt2; /* Maximum valid diagonal. */ + const long fmid = nStt1 - nStt2; /* Center diagonal of top-down search. */ + const long bmid = nEnd1 - nEnd2; /* Center diagonal of bottom-up search. */ + + long fmin = fmid, fmax = fmid; /* Limits of top-down search. */ + long bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */ + + long c; /* Cost. */ + long odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd + diagonal with respect to the northwest. */ + + pFDiag[fmid] = nStt1; + pBDiag[bmid] = nEnd1; + + for (c = 1;; ++c) + { + long d; /* Active diagonal. */ + long big_snake = 0; + + /* Extend the top-down search by an edit step in each diagonal. */ + fmin > dmin ? pFDiag[--fmin - 1] = -1 : ++fmin; + fmax < dmax ? pFDiag[++fmax + 1] = -1 : --fmax; + for (d = fmax; d >= fmin; d -= 2) + { + long x, y, oldx, tlo = pFDiag[d - 1], thi = pFDiag[d + 1]; + + if (tlo >= thi) + x = tlo + 1; + else + x = thi; + oldx = x; + y = x - d; + while( ULONG(x) < nEnd1 && ULONG(y) < nEnd2 && + rMoved1.GetIndex( x ) == rMoved2.GetIndex( y )) + ++x, ++y; + if (x - oldx > 20) + big_snake = 1; + pFDiag[d] = x; + if( odd && bmin <= d && d <= bmax && pBDiag[d] <= pFDiag[d] ) + { + *pCost = 2 * c - 1; + return d; + } + } + + /* Similar extend the bottom-up search. */ + bmin > dmin ? pBDiag[--bmin - 1] = INT_MAX : ++bmin; + bmax < dmax ? pBDiag[++bmax + 1] = INT_MAX : --bmax; + for (d = bmax; d >= bmin; d -= 2) + { + long x, y, oldx, tlo = pBDiag[d - 1], thi = pBDiag[d + 1]; + + if (tlo < thi) + x = tlo; + else + x = thi - 1; + oldx = x; + y = x - d; + while( ULONG(x) > nStt1 && ULONG(y) > nStt2 && + rMoved1.GetIndex( x - 1 ) == rMoved2.GetIndex( y - 1 )) + --x, --y; + if (oldx - x > 20) + big_snake = 1; + pBDiag[d] = x; + if (!odd && fmin <= d && d <= fmax && pBDiag[d] <= pFDiag[d]) + { + *pCost = 2 * c; + return d; + } + } + } +} + +void Compare::ShiftBoundaries( CompareData& rData1, CompareData& rData2 ) +{ + for( int iz = 0; iz < 2; ++iz ) + { + CompareData* pData = &rData1; + CompareData* pOtherData = &rData2; + + ULONG i = 0; + ULONG j = 0; + ULONG i_end = pData->GetLineCount(); + ULONG preceding = ULONG_MAX; + ULONG other_preceding = ULONG_MAX; + + while (1) + { + ULONG start, other_start; + + /* Scan forwards to find beginning of another run of changes. + Also keep track of the corresponding point in the other file. */ + + while( i < i_end && !pData->GetChanged( i ) ) + { + while( pOtherData->GetChanged( j++ )) + /* Non-corresponding lines in the other file + will count as the preceding batch of changes. */ + other_preceding = j; + i++; + } + + if (i == i_end) + break; + + start = i; + other_start = j; + + while (1) + { + /* Now find the end of this run of changes. */ + + while( pData->GetChanged( ++i )) + ; + + /* If the first changed line matches the following unchanged one, + and this run does not follow right after a previous run, + and there are no lines deleted from the other file here, + then classify the first changed line as unchanged + and the following line as changed in its place. */ + + /* You might ask, how could this run follow right after another? + Only because the previous run was shifted here. */ + + if( i != i_end && + pData->GetIndex( start ) == pData->GetIndex( i ) && + !pOtherData->GetChanged( j ) && + !( start == preceding || other_start == other_preceding )) + { + pData->SetChanged( start++, 0 ); + pData->SetChanged( i ); + /* Since one line-that-matches is now before this run + instead of after, we must advance in the other file + to keep in synch. */ + ++j; + } + else + break; + } + + preceding = i; + other_preceding = j; + } + + pData = &rData2; + pOtherData = &rData1; + } +} + +/* */ + +class SwCompareLine : public CompareLine +{ + const SwNode& rNode; +public: + SwCompareLine( const SwNode& rNd ); + virtual ~SwCompareLine(); + + virtual ULONG GetHashValue() const; + virtual BOOL Compare( const CompareLine& rLine ) const; + + static ULONG GetTxtNodeHashValue( const SwTxtNode& rNd, ULONG nVal ); + static BOOL CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd ); + static BOOL CompareTxtNd( const SwTxtNode& rDstNd, + const SwTxtNode& rSrcNd ); + + BOOL ChangesInLine( const SwCompareLine& rLine, + SwPaM *& rpInsRing, SwPaM*& rpDelRing ) const; + + const SwNode& GetNode() const { return rNode; } + + const SwNode& GetEndNode() const; + + // fuers Debugging! + String GetText() const; +}; + +class SwCompareData : public CompareData +{ + SwDoc& rDoc; + SwPaM *pInsRing, *pDelRing; + + ULONG PrevIdx( const SwNode* pNd ); + ULONG NextIdx( const SwNode* pNd ); + + virtual void CheckRanges( CompareData& ); + virtual void ShowInsert( ULONG nStt, ULONG nEnd ); + virtual void ShowDelete( const CompareData& rData, ULONG nStt, + ULONG nEnd, ULONG nInsPos ); + + virtual void CheckForChangesInLine( const CompareData& rData, + ULONG& nStt, ULONG& nEnd, + ULONG& nThisStt, ULONG& nThisEnd ); + +public: + SwCompareData( SwDoc& rD ) : rDoc( rD ), pInsRing(0), pDelRing(0) {} + virtual ~SwCompareData(); + + void SetRedlinesToDoc( BOOL bUseDocInfo ); +}; + +// ---------------------------------------------------------------- + +SwCompareLine::SwCompareLine( const SwNode& rNd ) + : rNode( rNd ) +{ +} + +SwCompareLine::~SwCompareLine() +{ +} + +ULONG SwCompareLine::GetHashValue() const +{ + ULONG nRet = 0; + switch( rNode.GetNodeType() ) + { + case ND_TEXTNODE: + nRet = GetTxtNodeHashValue( (SwTxtNode&)rNode, nRet ); + break; + + case ND_TABLENODE: + { + const SwNode* pEndNd = rNode.EndOfSectionNode(); + SwNodeIndex aIdx( rNode ); + while( &aIdx.GetNode() != pEndNd ) + { + if( aIdx.GetNode().IsTxtNode() ) + nRet = GetTxtNodeHashValue( (SwTxtNode&)aIdx.GetNode(), nRet ); + aIdx++; + } + } + break; + + case ND_SECTIONNODE: + { + String sStr( GetText() ); + for( xub_StrLen n = 0; n < sStr.Len(); ++n ) + ( nRet <<= 1 ) += sStr.GetChar( n ); + } + break; + + case ND_GRFNODE: + case ND_OLENODE: + // feste Id ? sollte aber nie auftauchen + break; + } + return nRet; +} + +const SwNode& SwCompareLine::GetEndNode() const +{ + const SwNode* pNd = &rNode; + switch( rNode.GetNodeType() ) + { + case ND_TABLENODE: + pNd = rNode.EndOfSectionNode(); + break; + + case ND_SECTIONNODE: + { + const SwSectionNode& rSNd = (SwSectionNode&)rNode; + const SwSection& rSect = rSNd.GetSection(); + if( CONTENT_SECTION != rSect.GetType() || rSect.IsProtect() ) + pNd = rNode.EndOfSectionNode(); + } + break; + } + return *pNd; +} + +BOOL SwCompareLine::Compare( const CompareLine& rLine ) const +{ + return CompareNode( rNode, ((SwCompareLine&)rLine).rNode ); +} + +namespace +{ + static String SimpleTableToText(const SwNode &rNode) + { + String sRet; + const SwNode* pEndNd = rNode.EndOfSectionNode(); + SwNodeIndex aIdx( rNode ); + while (&aIdx.GetNode() != pEndNd) + { + if (aIdx.GetNode().IsTxtNode()) + { + if (sRet.Len()) + { + sRet.Append( '\n' ); + } + sRet.Append( aIdx.GetNode().GetTxtNode()->GetExpandTxt() ); + } + aIdx++; + } + return sRet; + } +} + +BOOL SwCompareLine::CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd ) +{ + if( rSrcNd.GetNodeType() != rDstNd.GetNodeType() ) + return FALSE; + + BOOL bRet = FALSE; + + switch( rDstNd.GetNodeType() ) + { + case ND_TEXTNODE: + bRet = CompareTxtNd( (SwTxtNode&)rDstNd, (SwTxtNode&)rSrcNd ); + break; + + case ND_TABLENODE: + { + const SwTableNode& rTSrcNd = (SwTableNode&)rSrcNd; + const SwTableNode& rTDstNd = (SwTableNode&)rDstNd; + + bRet = ( rTSrcNd.EndOfSectionIndex() - rTSrcNd.GetIndex() ) == + ( rTDstNd.EndOfSectionIndex() - rTDstNd.GetIndex() ); + + // --> #i107826#: compare actual table content + if (bRet) + { + bRet = (SimpleTableToText(rSrcNd) == SimpleTableToText(rDstNd)); + } + // <-- + } + break; + + case ND_SECTIONNODE: + { + const SwSectionNode& rSSrcNd = (SwSectionNode&)rSrcNd, + & rSDstNd = (SwSectionNode&)rDstNd; + const SwSection& rSrcSect = rSSrcNd.GetSection(), + & rDstSect = rSDstNd.GetSection(); + SectionType eSrcSectType = rSrcSect.GetType(), + eDstSectType = rDstSect.GetType(); + switch( eSrcSectType ) + { + case CONTENT_SECTION: + bRet = CONTENT_SECTION == eDstSectType && + rSrcSect.IsProtect() == rDstSect.IsProtect(); + if( bRet && rSrcSect.IsProtect() ) + { + // the only have they both the same size + bRet = ( rSSrcNd.EndOfSectionIndex() - rSSrcNd.GetIndex() ) == + ( rSDstNd.EndOfSectionIndex() - rSDstNd.GetIndex() ); + } + break; + + case TOX_HEADER_SECTION: + case TOX_CONTENT_SECTION: + if( TOX_HEADER_SECTION == eDstSectType || + TOX_CONTENT_SECTION == eDstSectType ) + { + // the same type of TOX? + const SwTOXBase* pSrcTOX = rSrcSect.GetTOXBase(); + const SwTOXBase* pDstTOX = rDstSect.GetTOXBase(); + bRet = pSrcTOX && pDstTOX + && pSrcTOX->GetType() == pDstTOX->GetType() + && pSrcTOX->GetTitle() == pDstTOX->GetTitle() + && pSrcTOX->GetTypeName() == pDstTOX->GetTypeName() +// && pSrcTOX->GetTOXName() == pDstTOX->GetTOXName() + ; + } + break; + + case DDE_LINK_SECTION: + case FILE_LINK_SECTION: + bRet = eSrcSectType == eDstSectType && + rSrcSect.GetLinkFileName() == + rDstSect.GetLinkFileName(); + break; + } + } + break; + + case ND_ENDNODE: + bRet = rSrcNd.StartOfSectionNode()->GetNodeType() == + rDstNd.StartOfSectionNode()->GetNodeType(); + + // --> #i107826#: compare actual table content + if (bRet && rSrcNd.StartOfSectionNode()->GetNodeType() == ND_TABLENODE) + { + bRet = CompareNode( + *rSrcNd.StartOfSectionNode(), *rDstNd.StartOfSectionNode()); + } + // <-- + + break; + } + return bRet; +} + +String SwCompareLine::GetText() const +{ + String sRet; + switch( rNode.GetNodeType() ) + { + case ND_TEXTNODE: + sRet = ((SwTxtNode&)rNode).GetExpandTxt(); + break; + + case ND_TABLENODE: + { + sRet = SimpleTableToText(rNode); + sRet.InsertAscii( "Tabelle: ", 0 ); + } + break; + + case ND_SECTIONNODE: + { + sRet.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "Section - Node:" )); + + const SwSectionNode& rSNd = (SwSectionNode&)rNode; + const SwSection& rSect = rSNd.GetSection(); + switch( rSect.GetType() ) + { + case CONTENT_SECTION: + if( rSect.IsProtect() ) + sRet.Append( String::CreateFromInt32( + rSNd.EndOfSectionIndex() - rSNd.GetIndex() )); + break; + + case TOX_HEADER_SECTION: + case TOX_CONTENT_SECTION: + { + const SwTOXBase* pTOX = rSect.GetTOXBase(); + if( pTOX ) + sRet.Append( pTOX->GetTitle() ) + .Append( pTOX->GetTypeName() ) +// .Append( pTOX->GetTOXName() ) + .Append( String::CreateFromInt32( pTOX->GetType() )); + } + break; + + case DDE_LINK_SECTION: + case FILE_LINK_SECTION: + sRet += rSect.GetLinkFileName(); + break; + } + } + break; + + case ND_GRFNODE: + sRet.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "Grafik - Node:" )); + break; + case ND_OLENODE: + sRet.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "OLE - Node:" )); + break; + } + return sRet; +} + +ULONG SwCompareLine::GetTxtNodeHashValue( const SwTxtNode& rNd, ULONG nVal ) +{ + String sStr( rNd.GetExpandTxt() ); + for( xub_StrLen n = 0; n < sStr.Len(); ++n ) + ( nVal <<= 1 ) += sStr.GetChar( n ); + return nVal; +} + +BOOL SwCompareLine::CompareTxtNd( const SwTxtNode& rDstNd, + const SwTxtNode& rSrcNd ) +{ + BOOL bRet = FALSE; + // erstmal ganz einfach! + if( rDstNd.GetTxt() == rSrcNd.GetTxt() ) + { + // der Text ist gleich, aber sind die "Sonderattribute" (0xFF) auch + // dieselben?? + bRet = TRUE; + } + return bRet; +} + +BOOL SwCompareLine::ChangesInLine( const SwCompareLine& rLine, + SwPaM *& rpInsRing, SwPaM*& rpDelRing ) const +{ + BOOL bRet = FALSE; + if( ND_TEXTNODE == rNode.GetNodeType() && + ND_TEXTNODE == rLine.GetNode().GetNodeType() ) + { + SwTxtNode& rDestNd = *(SwTxtNode*)rNode.GetTxtNode(); + const SwTxtNode& rSrcNd = *rLine.GetNode().GetTxtNode(); + + xub_StrLen nDEnd = rDestNd.GetTxt().Len(), nSEnd = rSrcNd.GetTxt().Len(); + xub_StrLen nStt; + xub_StrLen nEnd; + + for( nStt = 0, nEnd = Min( nDEnd, nSEnd ); nStt < nEnd; ++nStt ) + if( rDestNd.GetTxt().GetChar( nStt ) != + rSrcNd.GetTxt().GetChar( nStt ) ) + break; + + while( nStt < nDEnd && nStt < nSEnd ) + { + --nDEnd, --nSEnd; + if( rDestNd.GetTxt().GetChar( nDEnd ) != + rSrcNd.GetTxt().GetChar( nSEnd ) ) + { + ++nDEnd, ++nSEnd; + break; + } + } + + if( nStt || !nDEnd || !nSEnd || nDEnd < rDestNd.GetTxt().Len() || + nSEnd < rSrcNd.GetTxt().Len() ) + { + // jetzt ist zwischen nStt bis nDEnd das neu eingefuegte + // und zwischen nStt und nSEnd das geloeschte + SwDoc* pDoc = rDestNd.GetDoc(); + SwPaM aPam( rDestNd, nDEnd ); + if( nStt != nDEnd ) + { + SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpInsRing ); + if( !rpInsRing ) + rpInsRing = pTmp; + + pTmp->SetMark(); + pTmp->GetMark()->nContent = nStt; + } + + if( nStt != nSEnd ) + { + { + BOOL bUndo = pDoc->DoesUndo(); + pDoc->DoUndo( FALSE ); + SwPaM aCpyPam( rSrcNd, nStt ); + aCpyPam.SetMark(); + aCpyPam.GetPoint()->nContent = nSEnd; + aCpyPam.GetDoc()->CopyRange( aCpyPam, *aPam.GetPoint(), + false ); + pDoc->DoUndo( bUndo ); + } + + SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpDelRing ); + if( !rpDelRing ) + rpDelRing = pTmp; + + pTmp->SetMark(); + pTmp->GetMark()->nContent = nDEnd; + + if( rpInsRing ) + { + SwPaM* pCorr = (SwPaM*)rpInsRing->GetPrev(); + if( *pCorr->GetPoint() == *pTmp->GetPoint() ) + *pCorr->GetPoint() = *pTmp->GetMark(); + } + } + bRet = TRUE; + } + } + return bRet; +} + +// ---------------------------------------------------------------- + +SwCompareData::~SwCompareData() +{ + if( pDelRing ) + { + while( pDelRing->GetNext() != pDelRing ) + delete pDelRing->GetNext(); + delete pDelRing; + } + if( pInsRing ) + { + while( pInsRing->GetNext() != pInsRing ) + delete pInsRing->GetNext(); + delete pInsRing; + } +} + +ULONG SwCompareData::NextIdx( const SwNode* pNd ) +{ + if( pNd->IsStartNode() ) + { + const SwSectionNode* pSNd; + if( pNd->IsTableNode() || + ( 0 != (pSNd = pNd->GetSectionNode() ) && + ( CONTENT_SECTION != pSNd->GetSection().GetType() || + pSNd->GetSection().IsProtect() ) ) ) + pNd = pNd->EndOfSectionNode(); + } + return pNd->GetIndex() + 1; +} + +ULONG SwCompareData::PrevIdx( const SwNode* pNd ) +{ + if( pNd->IsEndNode() ) + { + const SwSectionNode* pSNd; + if( pNd->StartOfSectionNode()->IsTableNode() || + ( 0 != (pSNd = pNd->StartOfSectionNode()->GetSectionNode() ) && + ( CONTENT_SECTION != pSNd->GetSection().GetType() || + pSNd->GetSection().IsProtect() ) ) ) + pNd = pNd->StartOfSectionNode(); + } + return pNd->GetIndex() - 1; +} + + +void SwCompareData::CheckRanges( CompareData& rData ) +{ + const SwNodes& rSrcNds = ((SwCompareData&)rData).rDoc.GetNodes(); + const SwNodes& rDstNds = rDoc.GetNodes(); + + const SwNode& rSrcEndNd = rSrcNds.GetEndOfContent(); + const SwNode& rDstEndNd = rDstNds.GetEndOfContent(); + + ULONG nSrcSttIdx = NextIdx( rSrcEndNd.StartOfSectionNode() ); + ULONG nSrcEndIdx = rSrcEndNd.GetIndex(); + + ULONG nDstSttIdx = NextIdx( rDstEndNd.StartOfSectionNode() ); + ULONG nDstEndIdx = rDstEndNd.GetIndex(); + + while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx ) + { + const SwNode* pSrcNd = rSrcNds[ nSrcSttIdx ]; + const SwNode* pDstNd = rDstNds[ nDstSttIdx ]; + if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd )) + break; + + nSrcSttIdx = NextIdx( pSrcNd ); + nDstSttIdx = NextIdx( pDstNd ); + } + + nSrcEndIdx = PrevIdx( &rSrcEndNd ); + nDstEndIdx = PrevIdx( &rDstEndNd ); + while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx ) + { + const SwNode* pSrcNd = rSrcNds[ nSrcEndIdx ]; + const SwNode* pDstNd = rDstNds[ nDstEndIdx ]; + if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd )) + break; + + nSrcEndIdx = PrevIdx( pSrcNd ); + nDstEndIdx = PrevIdx( pDstNd ); + } + + while( nSrcSttIdx <= nSrcEndIdx ) + { + const SwNode* pNd = rSrcNds[ nSrcSttIdx ]; + rData.InsertLine( new SwCompareLine( *pNd ) ); + nSrcSttIdx = NextIdx( pNd ); + } + + while( nDstSttIdx <= nDstEndIdx ) + { + const SwNode* pNd = rDstNds[ nDstSttIdx ]; + InsertLine( new SwCompareLine( *pNd ) ); + nDstSttIdx = NextIdx( pNd ); + } +} + + +void SwCompareData::ShowInsert( ULONG nStt, ULONG nEnd ) +{ + SwPaM* pTmp = new SwPaM( ((SwCompareLine*)GetLine( nStt ))->GetNode(), 0, + ((SwCompareLine*)GetLine( nEnd-1 ))->GetEndNode(), 0, + pInsRing ); + if( !pInsRing ) + pInsRing = pTmp; + + // #i65201#: These SwPaMs are calculated smaller than needed, see comment below + +} + +void SwCompareData::ShowDelete( const CompareData& rData, ULONG nStt, + ULONG nEnd, ULONG nInsPos ) +{ + SwNodeRange aRg( + ((SwCompareLine*)rData.GetLine( nStt ))->GetNode(), 0, + ((SwCompareLine*)rData.GetLine( nEnd-1 ))->GetEndNode(), 1 ); + + USHORT nOffset = 0; + const CompareLine* pLine; + if( GetLineCount() == nInsPos ) + { + pLine = GetLine( nInsPos-1 ); + nOffset = 1; + } + else + pLine = GetLine( nInsPos ); + + const SwNode* pLineNd; + if( pLine ) + { + if( nOffset ) + pLineNd = &((SwCompareLine*)pLine)->GetEndNode(); + else + pLineNd = &((SwCompareLine*)pLine)->GetNode(); + } + else + { + pLineNd = &rDoc.GetNodes().GetEndOfContent(); + nOffset = 0; + } + + SwNodeIndex aInsPos( *pLineNd, nOffset ); + SwNodeIndex aSavePos( aInsPos, -1 ); + + ((SwCompareData&)rData).rDoc.CopyWithFlyInFly( aRg, 0, aInsPos ); + rDoc.SetModified(); + aSavePos++; + + // #i65201#: These SwPaMs are calculated when the (old) delete-redlines are hidden, + // they will be inserted when the delete-redlines are shown again. + // To avoid unwanted insertions of delete-redlines into these new redlines, what happens + // especially at the end of the document, I reduce the SwPaM by one node. + // Before the new redlines are inserted, they have to expand again. + SwPaM* pTmp = new SwPaM( aSavePos.GetNode(), aInsPos.GetNode(), 0, -1, pDelRing ); + if( !pDelRing ) + pDelRing = pTmp; + + if( pInsRing ) + { + SwPaM* pCorr = (SwPaM*)pInsRing->GetPrev(); + if( *pCorr->GetPoint() == *pTmp->GetPoint() ) + { + SwNodeIndex aTmpPos( pTmp->GetMark()->nNode, -1 ); + *pCorr->GetPoint() = SwPosition( aTmpPos ); + } + } +} + +void SwCompareData::CheckForChangesInLine( const CompareData& rData, + ULONG& rStt, ULONG& rEnd, + ULONG& rThisStt, ULONG& rThisEnd ) +{ + while( rStt < rEnd && rThisStt < rThisEnd ) + { + SwCompareLine* pDstLn = (SwCompareLine*)GetLine( rThisStt ); + SwCompareLine* pSrcLn = (SwCompareLine*)rData.GetLine( rStt ); + if( !pDstLn->ChangesInLine( *pSrcLn, pInsRing, pDelRing ) ) + break; + + ++rStt; + ++rThisStt; + } +} + +void SwCompareData::SetRedlinesToDoc( BOOL bUseDocInfo ) +{ + SwPaM* pTmp = pDelRing; + + // Bug #83296#: get the Author / TimeStamp from the "other" + // document info + USHORT nAuthor = rDoc.GetRedlineAuthor(); + DateTime aTimeStamp; + SwDocShell *pDocShell(rDoc.GetDocShell()); + DBG_ASSERT(pDocShell, "no SwDocShell"); + if (pDocShell) { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + DBG_ASSERT(xDocProps.is(), "Doc has no DocumentProperties"); + + if( bUseDocInfo && xDocProps.is() ) { + String aTmp( 1 == xDocProps->getEditingCycles() + ? xDocProps->getAuthor() + : xDocProps->getModifiedBy() ); + util::DateTime uDT( 1 == xDocProps->getEditingCycles() + ? xDocProps->getCreationDate() + : xDocProps->getModificationDate() ); + Date d(uDT.Day, uDT.Month, uDT.Year); + Time t(uDT.Hours, uDT.Minutes, uDT.Seconds, uDT.HundredthSeconds); + DateTime aDT(d,t); + + if( aTmp.Len() ) + { + nAuthor = rDoc.InsertRedlineAuthor( aTmp ); + aTimeStamp = aDT; + } + } + } + + if( pTmp ) + { + SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_DELETE, nAuthor, aTimeStamp, + aEmptyStr, 0, 0 ); + do { + // #i65201#: Expand again, see comment above. + if( pTmp->GetPoint()->nContent == 0 ) + { + pTmp->GetPoint()->nNode++; + pTmp->GetPoint()->nContent.Assign( pTmp->GetCntntNode(), 0 ); + } + // --> mst 2010-05-17 #i101009# + // prevent redlines that end on structural end node + if (& rDoc.GetNodes().GetEndOfContent() == + & pTmp->GetPoint()->nNode.GetNode()) + { + pTmp->GetPoint()->nNode--; + SwCntntNode *const pContentNode( pTmp->GetCntntNode() ); + pTmp->GetPoint()->nContent.Assign( pContentNode, + (pContentNode) ? pContentNode->Len() : 0 ); + } + // <-- + + rDoc.DeleteRedline( *pTmp, false, USHRT_MAX ); + + if( rDoc.DoesUndo() ) + rDoc.AppendUndo( new SwUndoCompDoc( *pTmp, FALSE )); + rDoc.AppendRedline( new SwRedline( aRedlnData, *pTmp ), true ); + + } while( pDelRing != ( pTmp = (SwPaM*)pTmp->GetNext() )); + } + + pTmp = pInsRing; + if( pTmp ) + { + do { + if( pTmp->GetPoint()->nContent == 0 ) + { + pTmp->GetPoint()->nNode++; + pTmp->GetPoint()->nContent.Assign( pTmp->GetCntntNode(), 0 ); + } + // --> mst 2010-05-17 #i101009# + // prevent redlines that end on structural end node + if (& rDoc.GetNodes().GetEndOfContent() == + & pTmp->GetPoint()->nNode.GetNode()) + { + pTmp->GetPoint()->nNode--; + SwCntntNode *const pContentNode( pTmp->GetCntntNode() ); + pTmp->GetPoint()->nContent.Assign( pContentNode, + (pContentNode) ? pContentNode->Len() : 0 ); + } + // <-- + } while( pInsRing != ( pTmp = (SwPaM*)pTmp->GetNext() )); + SwRedlineData aRedlnData( nsRedlineType_t::REDLINE_INSERT, nAuthor, aTimeStamp, + aEmptyStr, 0, 0 ); + + // zusammenhaengende zusammenfassen + if( pTmp->GetNext() != pInsRing ) + { + const SwCntntNode* pCNd; + do { + SwPosition& rSttEnd = *pTmp->End(), + & rEndStt = *((SwPaM*)pTmp->GetNext())->Start(); + if( rSttEnd == rEndStt || + (!rEndStt.nContent.GetIndex() && + rEndStt.nNode.GetIndex() - 1 == rSttEnd.nNode.GetIndex() && + 0 != ( pCNd = rSttEnd.nNode.GetNode().GetCntntNode() ) + ? rSttEnd.nContent.GetIndex() == pCNd->Len() + : 0 )) + { + if( pTmp->GetNext() == pInsRing ) + { + // liegen hintereinander also zusammen fassen + rEndStt = *pTmp->Start(); + delete pTmp; + pTmp = pInsRing; + } + else + { + // liegen hintereinander also zusammen fassen + rSttEnd = *((SwPaM*)pTmp->GetNext())->End(); + delete pTmp->GetNext(); + } + } + else + pTmp = (SwPaM*)pTmp->GetNext(); + } while( pInsRing != pTmp ); + } + + do { + if( rDoc.AppendRedline( new SwRedline( aRedlnData, *pTmp ), true) && + rDoc.DoesUndo() ) + rDoc.AppendUndo( new SwUndoCompDoc( *pTmp, TRUE )); + } while( pInsRing != ( pTmp = (SwPaM*)pTmp->GetNext() )); + } +} + +/* */ + + + + // returnt (?die Anzahl der Unterschiede?) ob etwas unterschiedlich ist +long SwDoc::CompareDoc( const SwDoc& rDoc ) +{ + if( &rDoc == this ) + return 0; + + long nRet = 0; + + StartUndo(UNDO_EMPTY, NULL); + BOOL bDocWasModified = IsModified(); + SwDoc& rSrcDoc = (SwDoc&)rDoc; + BOOL bSrcModified = rSrcDoc.IsModified(); + + RedlineMode_t eSrcRedlMode = rSrcDoc.GetRedlineMode(); + rSrcDoc.SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_INSERT ); + SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT)); + + SwCompareData aD0( rSrcDoc ); + SwCompareData aD1( *this ); + + aD1.CompareLines( aD0 ); + + nRet = aD1.ShowDiffs( aD0 ); + + if( nRet ) + { + SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | + nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); + + aD1.SetRedlinesToDoc( !bDocWasModified ); + SetModified(); + } + + rSrcDoc.SetRedlineMode( eSrcRedlMode ); + SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); + + if( !bSrcModified ) + rSrcDoc.ResetModified(); + + EndUndo(UNDO_EMPTY, NULL); + + return nRet; +} + + +typedef void (SwDoc::*FNInsUndo)( SwUndo* ); + +class _SaveMergeRedlines : public Ring +{ + const SwRedline* pSrcRedl; + SwRedline* pDestRedl; +public: + _SaveMergeRedlines( const SwNode& rDstNd, + const SwRedline& rSrcRedl, Ring* pRing ); + USHORT InsertRedline( FNInsUndo pFn ); + + SwRedline* GetDestRedline() { return pDestRedl; } +}; + +_SaveMergeRedlines::_SaveMergeRedlines( const SwNode& rDstNd, + const SwRedline& rSrcRedl, Ring* pRing ) + : Ring( pRing ), pSrcRedl( &rSrcRedl ) +{ + SwPosition aPos( rDstNd ); + + const SwPosition* pStt = rSrcRedl.Start(); + if( rDstNd.IsCntntNode() ) + aPos.nContent.Assign( ((SwCntntNode*)&rDstNd), pStt->nContent.GetIndex() ); + pDestRedl = new SwRedline( rSrcRedl.GetRedlineData(), aPos ); + + if( nsRedlineType_t::REDLINE_DELETE == pDestRedl->GetType() ) + { + // den Bereich als geloescht kennzeichnen + const SwPosition* pEnd = pStt == rSrcRedl.GetPoint() + ? rSrcRedl.GetMark() + : rSrcRedl.GetPoint(); + + pDestRedl->SetMark(); + pDestRedl->GetPoint()->nNode += pEnd->nNode.GetIndex() - + pStt->nNode.GetIndex(); + pDestRedl->GetPoint()->nContent.Assign( pDestRedl->GetCntntNode(), + pEnd->nContent.GetIndex() ); + } +} + +USHORT _SaveMergeRedlines::InsertRedline( FNInsUndo pFn ) +{ + USHORT nIns = 0; + SwDoc* pDoc = pDestRedl->GetDoc(); + + if( nsRedlineType_t::REDLINE_INSERT == pDestRedl->GetType() ) + { + // der Teil wurde eingefuegt, also kopiere ihn aus dem SourceDoc + BOOL bUndo = pDoc->DoesUndo(); + pDoc->DoUndo( FALSE ); + + SwNodeIndex aSaveNd( pDestRedl->GetPoint()->nNode, -1 ); + xub_StrLen nSaveCnt = pDestRedl->GetPoint()->nContent.GetIndex(); + + RedlineMode_t eOld = pDoc->GetRedlineMode(); + pDoc->SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_IGNORE)); + + pSrcRedl->GetDoc()->CopyRange( + *const_cast<SwPaM*>(static_cast<const SwPaM*>(pSrcRedl)), + *pDestRedl->GetPoint(), false ); + + pDoc->SetRedlineMode_intern( eOld ); + pDoc->DoUndo( bUndo ); + + pDestRedl->SetMark(); + aSaveNd++; + pDestRedl->GetMark()->nNode = aSaveNd; + pDestRedl->GetMark()->nContent.Assign( aSaveNd.GetNode().GetCntntNode(), + nSaveCnt ); + + if( GetPrev() != this ) + { + SwPaM* pTmpPrev = ((_SaveMergeRedlines*)GetPrev())->pDestRedl; + if( pTmpPrev && *pTmpPrev->GetPoint() == *pDestRedl->GetPoint() ) + *pTmpPrev->GetPoint() = *pDestRedl->GetMark(); + } + } + else + { + //JP 21.09.98: Bug 55909 + // falls im Doc auf gleicher Pos aber schon ein geloeschter oder + // eingefuegter ist, dann muss dieser gesplittet werden! + SwPosition* pDStt = pDestRedl->GetMark(), + * pDEnd = pDestRedl->GetPoint(); + USHORT n = 0; + + // zur StartPos das erste Redline suchen + if( !pDoc->GetRedline( *pDStt, &n ) && n ) + --n; + + const SwRedlineTbl& rRedlineTbl = pDoc->GetRedlineTbl(); + for( ; n < rRedlineTbl.Count(); ++n ) + { + SwRedline* pRedl = rRedlineTbl[ n ]; + SwPosition* pRStt = pRedl->Start(), + * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() + : pRedl->GetPoint(); + if( nsRedlineType_t::REDLINE_DELETE == pRedl->GetType() || + nsRedlineType_t::REDLINE_INSERT == pRedl->GetType() ) + { + SwComparePosition eCmpPos = ComparePosition( *pDStt, *pDEnd, *pRStt, *pREnd ); + switch( eCmpPos ) + { + case POS_COLLIDE_START: + case POS_BEHIND: + break; + + case POS_INSIDE: + case POS_EQUAL: + delete pDestRedl, pDestRedl = 0; + // break; -> kein break !!!! + + case POS_COLLIDE_END: + case POS_BEFORE: + n = rRedlineTbl.Count(); + break; + + case POS_OUTSIDE: + { + SwRedline* pCpyRedl = new SwRedline( + pDestRedl->GetRedlineData(), *pDStt ); + pCpyRedl->SetMark(); + *pCpyRedl->GetPoint() = *pRStt; + + SwUndoCompDoc* pUndo = pDoc->DoesUndo() + ? new SwUndoCompDoc( *pCpyRedl ) : 0; + + // now modify doc: append redline, undo (and count) + pDoc->AppendRedline( pCpyRedl, true ); + if( pUndo ) + (pDoc->*pFn)( pUndo ); + ++nIns; + + *pDStt = *pREnd; + + // dann solle man neu anfangen + n = USHRT_MAX; + } + break; + + case POS_OVERLAP_BEFORE: + *pDEnd = *pRStt; + break; + + case POS_OVERLAP_BEHIND: + *pDStt = *pREnd; + break; + } + } + else if( *pDEnd <= *pRStt ) + break; + } + + } + + if( pDestRedl ) + { + SwUndoCompDoc* pUndo = pDoc->DoesUndo() ? new SwUndoCompDoc( *pDestRedl ) : 0; + + // now modify doc: append redline, undo (and count) + bool bRedlineAccepted = pDoc->AppendRedline( pDestRedl, true ); + if( pUndo ) + (pDoc->*pFn)( pUndo ); + ++nIns; + + // if AppendRedline has deleted our redline, we may not keep a + // reference to it + if( ! bRedlineAccepted ) + pDestRedl = NULL; + } + return nIns; +} + +// merge zweier Dokumente +long SwDoc::MergeDoc( const SwDoc& rDoc ) +{ + if( &rDoc == this ) + return 0; + + long nRet = 0; + + StartUndo(UNDO_EMPTY, NULL); + + SwDoc& rSrcDoc = (SwDoc&)rDoc; + BOOL bSrcModified = rSrcDoc.IsModified(); + + RedlineMode_t eSrcRedlMode = rSrcDoc.GetRedlineMode(); + rSrcDoc.SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE ); + SetRedlineMode( nsRedlineMode_t::REDLINE_SHOW_DELETE ); + + SwCompareData aD0( rSrcDoc ); + SwCompareData aD1( *this ); + + aD1.CompareLines( aD0 ); + + if( !aD1.HasDiffs( aD0 ) ) + { + // jetzt wollen wir alle Redlines aus dem SourceDoc zu uns bekommen + + // suche alle Insert - Redlines aus dem SourceDoc und bestimme + // deren Position im DestDoc + _SaveMergeRedlines* pRing = 0; + const SwRedlineTbl& rSrcRedlTbl = rSrcDoc.GetRedlineTbl(); + ULONG nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex(); + ULONG nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex(); + for( USHORT n = 0; n < rSrcRedlTbl.Count(); ++n ) + { + const SwRedline* pRedl = rSrcRedlTbl[ n ]; + ULONG nNd = pRedl->GetPoint()->nNode.GetIndex(); + RedlineType_t eType = pRedl->GetType(); + if( nEndOfExtra < nNd && + ( nsRedlineType_t::REDLINE_INSERT == eType || nsRedlineType_t::REDLINE_DELETE == eType )) + { + const SwNode* pDstNd = GetNodes()[ + nMyEndOfExtra + nNd - nEndOfExtra ]; + + // Position gefunden. Dann muss im DestDoc auch + // in der Line das Redline eingefuegt werden + _SaveMergeRedlines* pTmp = new _SaveMergeRedlines( + *pDstNd, *pRedl, pRing ); + if( !pRing ) + pRing = pTmp; + } + } + + if( pRing ) + { + // dann alle ins DestDoc ueber nehmen + rSrcDoc.SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); + + SetRedlineMode((RedlineMode_t)( + nsRedlineMode_t::REDLINE_ON | + nsRedlineMode_t::REDLINE_SHOW_INSERT | + nsRedlineMode_t::REDLINE_SHOW_DELETE)); + + _SaveMergeRedlines* pTmp = pRing; + + do { + nRet += pTmp->InsertRedline( &SwDoc::AppendUndo ); + } while( pRing != ( pTmp = (_SaveMergeRedlines*)pTmp->GetNext() )); + + while( pRing != pRing->GetNext() ) + delete pRing->GetNext(); + delete pRing; + } + } + + rSrcDoc.SetRedlineMode( eSrcRedlMode ); + if( !bSrcModified ) + rSrcDoc.ResetModified(); + + SetRedlineMode((RedlineMode_t)(nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE)); + + EndUndo(UNDO_EMPTY, NULL); + + return nRet; +} + + |