From 76718abdb2138623102398a10f3228e576dd0ae8 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 10 Feb 2010 01:27:27 +0000 Subject: Added abandoned KDE3 version of kdiff3 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kdiff3@1088041 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/mergeresultwindow.cpp | 3222 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3222 insertions(+) create mode 100644 src/mergeresultwindow.cpp (limited to 'src/mergeresultwindow.cpp') diff --git a/src/mergeresultwindow.cpp b/src/mergeresultwindow.cpp new file mode 100644 index 0000000..0e0aad9 --- /dev/null +++ b/src/mergeresultwindow.cpp @@ -0,0 +1,3222 @@ +/*************************************************************************** + mergeresultwindow.cpp - description + ------------------- + begin : Sun Apr 14 2002 + copyright : (C) 2002-2007 by Joachim Eibl + email : joachim.eibl at gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "mergeresultwindow.h" +#include "optiondialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int g_bAutoSolve = true; + +#undef leftInfoWidth +#define leftInfoWidth 3 + +MergeResultWindow::MergeResultWindow( + QWidget* pParent, + OptionDialog* pOptionDialog, + QStatusBar* pStatusBar + ) +: QWidget( pParent, 0, WRepaintNoErase ) +{ + setFocusPolicy( QWidget::ClickFocus ); + + m_firstLine = 0; + m_firstColumn = 0; + m_nofColumns = 0; + m_nofLines = 0; + m_totalSize = 0; + m_bMyUpdate = false; + m_bInsertMode = true; + m_scrollDeltaX = 0; + m_scrollDeltaY = 0; + m_bModified = false; + m_eOverviewMode=Overview::eOMNormal; + + m_pldA = 0; + m_pldB = 0; + m_pldC = 0; + m_sizeA = 0; + m_sizeB = 0; + m_sizeC = 0; + + m_pDiff3LineList = 0; + m_pTotalDiffStatus = 0; + m_pStatusBar = pStatusBar; + + m_pOptionDialog = pOptionDialog; + m_bPaintingAllowed = false; + m_delayedDrawTimer = 0; + + m_cursorXPos=0; + m_cursorOldXPos=0; + m_cursorYPos=0; + m_bCursorOn = true; + m_bCursorUpdate = false; + connect( &m_cursorTimer, SIGNAL(timeout()), this, SLOT( slotCursorUpdate() ) ); + m_cursorTimer.start( 500 /*ms*/, true /*single shot*/ ); + m_selection.reset(); + + setMinimumSize( QSize(20,20) ); + setFont( m_pOptionDialog->m_font ); +} + +void MergeResultWindow::init( + const LineData* pLineDataA, int sizeA, + const LineData* pLineDataB, int sizeB, + const LineData* pLineDataC, int sizeC, + const Diff3LineList* pDiff3LineList, + TotalDiffStatus* pTotalDiffStatus + ) +{ + m_firstLine = 0; + m_firstColumn = 0; + m_nofColumns = 0; + m_nofLines = 0; + m_bMyUpdate = false; + m_bInsertMode = true; + m_scrollDeltaX = 0; + m_scrollDeltaY = 0; + setModified( false ); + + m_pldA = pLineDataA; + m_pldB = pLineDataB; + m_pldC = pLineDataC; + m_sizeA = sizeA; + m_sizeB = sizeB; + m_sizeC = sizeC; + + m_pDiff3LineList = pDiff3LineList; + m_pTotalDiffStatus = pTotalDiffStatus; + + m_selection.reset(); + m_cursorXPos=0; + m_cursorOldXPos=0; + m_cursorYPos=0; + + merge( g_bAutoSolve, -1 ); + g_bAutoSolve = true; + update(); + updateSourceMask(); + + int wsc; + int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); + if (m_pStatusBar) + m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") + .arg(nofUnsolved).arg(wsc) ); +} + +void MergeResultWindow::reset() +{ + m_pDiff3LineList = 0; + m_pTotalDiffStatus = 0; + m_pldA = 0; + m_pldB = 0; + m_pldC = 0; +} + +// Calculate the merge information for the given Diff3Line. +// Results will be stored in mergeDetails, bConflict, bLineRemoved and src. +void mergeOneLine( + const Diff3Line& d, e_MergeDetails& mergeDetails, bool& bConflict, + bool& bLineRemoved, int& src, bool bTwoInputs + ) +{ + mergeDetails = eDefault; + bConflict = false; + bLineRemoved = false; + src = 0; + + if ( bTwoInputs ) // Only two input files + { + if ( d.lineA!=-1 && d.lineB!=-1 ) + { + if ( d.pFineAB == 0 ) + { + mergeDetails = eNoChange; src = A; + } + else + { + mergeDetails = eBChanged; bConflict = true; + } + } + else + { + if ( d.lineA!=-1 && d.lineB==-1 ) + { + mergeDetails = eBDeleted; bConflict = true; + } + else if ( d.lineA==-1 && d.lineB!=-1 ) + { + mergeDetails = eBDeleted; bConflict = true; + } + } + return; + } + + // A is base. + if ( d.lineA!=-1 && d.lineB!=-1 && d.lineC!=-1 ) + { + if ( d.pFineAB == 0 && d.pFineBC == 0 && d.pFineCA == 0) + { + mergeDetails = eNoChange; src = A; + } + else if( d.pFineAB == 0 && d.pFineBC != 0 && d.pFineCA != 0 ) + { + mergeDetails = eCChanged; src = C; + } + else if( d.pFineAB != 0 && d.pFineBC != 0 && d.pFineCA == 0 ) + { + mergeDetails = eBChanged; src = B; + } + else if( d.pFineAB != 0 && d.pFineBC == 0 && d.pFineCA != 0 ) + { + mergeDetails = eBCChangedAndEqual; src = C; + } + else if( d.pFineAB != 0 && d.pFineBC != 0 && d.pFineCA != 0 ) + { + mergeDetails = eBCChanged; bConflict = true; + } + else + assert(false); + } + else if ( d.lineA!=-1 && d.lineB!=-1 && d.lineC==-1 ) + { + if( d.pFineAB != 0 ) + { + mergeDetails = eBChanged_CDeleted; bConflict = true; + } + else + { + mergeDetails = eCDeleted; bLineRemoved = true; src = C; + } + } + else if ( d.lineA!=-1 && d.lineB==-1 && d.lineC!=-1 ) + { + if( d.pFineCA != 0 ) + { + mergeDetails = eCChanged_BDeleted; bConflict = true; + } + else + { + mergeDetails = eBDeleted; bLineRemoved = true; src = B; + } + } + else if ( d.lineA==-1 && d.lineB!=-1 && d.lineC!=-1 ) + { + if( d.pFineBC != 0 ) + { + mergeDetails = eBCAdded; bConflict = true; + } + else // B==C + { + mergeDetails = eBCAddedAndEqual; src = C; + } + } + else if ( d.lineA==-1 && d.lineB==-1 && d.lineC!= -1 ) + { + mergeDetails = eCAdded; src = C; + } + else if ( d.lineA==-1 && d.lineB!=-1 && d.lineC== -1 ) + { + mergeDetails = eBAdded; src = B; + } + else if ( d.lineA!=-1 && d.lineB==-1 && d.lineC==-1 ) + { + mergeDetails = eBCDeleted; bLineRemoved = true; src = C; + } + else + assert(false); +} + +bool MergeResultWindow::sameKindCheck( const MergeLine& ml1, const MergeLine& ml2 ) +{ + if ( ml1.bConflict && ml2.bConflict ) + { + // Both lines have conflicts: If one is only a white space conflict and + // the other one is a real conflict, then this line returns false. + return ml1.id3l->bAEqC == ml2.id3l->bAEqC && ml1.id3l->bAEqB == ml2.id3l->bAEqB; + } + else + return ( + !ml1.bConflict && !ml2.bConflict && ml1.bDelta && ml2.bDelta && ml1.srcSelect == ml2.srcSelect || + !ml1.bDelta && !ml2.bDelta + ); +} + +void MergeResultWindow::merge(bool bAutoSolve, int defaultSelector, bool bConflictsOnly, bool bWhiteSpaceOnly ) +{ + if ( !bConflictsOnly ) + { + if(m_bModified) + { + int result = KMessageBox::warningYesNo(this, + i18n("The output has been modified.\n" + "If you continue your changes will be lost."), + i18n("Warning"), i18n("C&ontinue"), i18n("&Cancel")); + if ( result==KMessageBox::No ) + return; + } + + m_mergeLineList.clear(); + m_totalSize = 0; + int lineIdx = 0; + Diff3LineList::const_iterator it; + for( it=m_pDiff3LineList->begin(); it!=m_pDiff3LineList->end(); ++it, ++lineIdx ) + { + const Diff3Line& d = *it; + + MergeLine ml; + bool bLineRemoved; + mergeOneLine( d, ml.mergeDetails, ml.bConflict, bLineRemoved, ml.srcSelect, m_pldC==0 ); + + // Automatic solving for only whitespace changes. + if ( ml.bConflict && + ( m_pldC==0 && (d.bAEqB || d.bWhiteLineA && d.bWhiteLineB) || + m_pldC!=0 && (d.bAEqB && d.bAEqC || d.bWhiteLineA && d.bWhiteLineB && d.bWhiteLineC ) ) ) + { + ml.bWhiteSpaceConflict = true; + } + + ml.d3lLineIdx = lineIdx; + ml.bDelta = ml.srcSelect != A; + ml.id3l = it; + ml.srcRangeLength = 1; + + MergeLine* back = m_mergeLineList.empty() ? 0 : &m_mergeLineList.back(); + + bool bSame = back!=0 && sameKindCheck( ml, *back ); + if( bSame ) + { + ++back->srcRangeLength; + if ( back->bWhiteSpaceConflict && !ml.bWhiteSpaceConflict ) + back->bWhiteSpaceConflict = false; + } + else + { + if (back!=0 && back->bWhiteSpaceConflict ) + { + if ( m_pldC==0 && m_pOptionDialog->m_whiteSpace2FileMergeDefault != 0 ) // Only two inputs + { + back->srcSelect = m_pOptionDialog->m_whiteSpace2FileMergeDefault; + back->bConflict = false; + } + else if ( m_pldC!=0 && m_pOptionDialog->m_whiteSpace3FileMergeDefault != 0 ) + { + back->srcSelect = m_pOptionDialog->m_whiteSpace3FileMergeDefault; + back->bConflict = false; + } + } + ml.mergeEditLineList.setTotalSizePtr(&m_totalSize); + m_mergeLineList.push_back( ml ); + } + + if ( ! ml.bConflict ) + { + MergeLine& tmpBack = m_mergeLineList.back(); + MergeEditLine mel(ml.id3l); + mel.setSource( ml.srcSelect, bLineRemoved ); + tmpBack.mergeEditLineList.push_back(mel); + } + else if ( back==0 || ! back->bConflict || !bSame ) + { + MergeLine& tmpBack = m_mergeLineList.back(); + MergeEditLine mel(ml.id3l); + mel.setConflict(); + tmpBack.mergeEditLineList.push_back(mel); + } + } + } + + if ( !bAutoSolve ) + { + // Change all auto selections + MergeLineList::iterator mlIt; + for( mlIt=m_mergeLineList.begin(); mlIt!=m_mergeLineList.end(); ++mlIt ) + { + MergeLine& ml = *mlIt; + bool bConflict = ml.mergeEditLineList.empty() || ml.mergeEditLineList.begin()->isConflict(); + if ( ml.bDelta && ( !bConflictsOnly || bConflict ) && (!bWhiteSpaceOnly || ml.bWhiteSpaceConflict )) + { + ml.mergeEditLineList.clear(); + if ( defaultSelector==-1 && ml.bDelta ) + { + MergeEditLine mel(ml.id3l);; + mel.setConflict(); + ml.bConflict = true; + ml.mergeEditLineList.push_back(mel); + } + else + { + Diff3LineList::const_iterator d3llit=ml.id3l; + int j; + + for( j=0; jlineA : + defaultSelector==2 ? d3llit->lineB : + defaultSelector==3 ? d3llit->lineC : -1; + + if ( srcLine != -1 ) + { + ml.mergeEditLineList.push_back(mel); + } + + ++d3llit; + } + + if ( ml.mergeEditLineList.empty() ) // Make a line nevertheless + { + MergeEditLine mel(ml.id3l); + mel.setRemoved( defaultSelector ); + ml.mergeEditLineList.push_back(mel); + } + } + } + } + } + + MergeLineList::iterator mlIt; + for( mlIt=m_mergeLineList.begin(); mlIt!=m_mergeLineList.end(); ++mlIt ) + { + MergeLine& ml = *mlIt; + // Remove all lines that are empty, because no src lines are there. + + int oldSrcLine = -1; + int oldSrc = -1; + MergeEditLineList::iterator melIt; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ) + { + MergeEditLine& mel = *melIt; + int melsrc = mel.src(); + + int srcLine = mel.isRemoved() ? -1 : + melsrc==1 ? mel.id3l()->lineA : + melsrc==2 ? mel.id3l()->lineB : + melsrc==3 ? mel.id3l()->lineC : -1; + + // At least one line remains because oldSrc != melsrc for first line in list + // Other empty lines will be removed + if ( srcLine == -1 && oldSrcLine==-1 && oldSrc == melsrc ) + melIt = ml.mergeEditLineList.erase( melIt ); + else + ++melIt; + + oldSrcLine = srcLine; + oldSrc = melsrc; + } + } + + if ( bAutoSolve && !bConflictsOnly ) + { + if ( m_pOptionDialog->m_bRunHistoryAutoMergeOnMergeStart ) + slotMergeHistory(); + if ( m_pOptionDialog->m_bRunRegExpAutoMergeOnMergeStart ) + slotRegExpAutoMerge(); + if ( m_pldC != 0 && ! doRelevantChangesExist() ) + emit noRelevantChangesDetected(); + } + + int nrOfSolvedConflicts = 0; + int nrOfUnsolvedConflicts = 0; + int nrOfWhiteSpaceConflicts = 0; + + MergeLineList::iterator i; + for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + if ( i->bConflict ) + ++nrOfUnsolvedConflicts; + else if ( i->bDelta ) + ++nrOfSolvedConflicts; + + if ( i->bWhiteSpaceConflict ) + ++nrOfWhiteSpaceConflicts; + } + + m_pTotalDiffStatus->nofUnsolvedConflicts = nrOfUnsolvedConflicts; + m_pTotalDiffStatus->nofSolvedConflicts = nrOfSolvedConflicts; + m_pTotalDiffStatus->nofWhitespaceConflicts = nrOfWhiteSpaceConflicts; + + + m_cursorXPos=0; + m_cursorOldXPos=0; + m_cursorYPos=0; + //m_firstLine = 0; // Must not set line/column without scrolling there + //m_firstColumn = 0; + + setModified(false); + + m_currentMergeLineIt = m_mergeLineList.begin(); + slotGoTop(); + + updateAvailabilities(); + update(); +} + +void MergeResultWindow::setFirstLine(int firstLine) +{ + m_firstLine = max2(0,firstLine); + update(); +} + +void MergeResultWindow::setFirstColumn(int firstCol) +{ + m_firstColumn = max2(0,firstCol); + update(); +} + +int MergeResultWindow::getNofColumns() +{ + return m_nofColumns; +} + +int MergeResultWindow::getNofLines() +{ + return m_totalSize; +} + +int MergeResultWindow::getNofVisibleColumns() +{ + QFontMetrics fm = fontMetrics(); + return width()/fm.width('W')-4; +} + +int MergeResultWindow::getNofVisibleLines() +{ + QFontMetrics fm = fontMetrics(); + return (height()-3)/fm.height()-2; +} + +void MergeResultWindow::resizeEvent( QResizeEvent* e ) +{ + QWidget::resizeEvent(e); + emit resizeSignal(); +} + +Overview::e_OverviewMode MergeResultWindow::getOverviewMode() +{ + return m_eOverviewMode; +} + +void MergeResultWindow::setOverviewMode( Overview::e_OverviewMode eOverviewMode ) +{ + m_eOverviewMode = eOverviewMode; +} + +// Check whether we should ignore current delta when moving to next/previous delta +bool MergeResultWindow::checkOverviewIgnore(MergeLineList::iterator &i) +{ + if (m_eOverviewMode == Overview::eOMNormal) return false; + if (m_eOverviewMode == Overview::eOMAvsB) + return i->mergeDetails == eCAdded || i->mergeDetails == eCDeleted || i->mergeDetails == eCChanged; + if (m_eOverviewMode == Overview::eOMAvsC) + return i->mergeDetails == eBAdded || i->mergeDetails == eBDeleted || i->mergeDetails == eBChanged; + if (m_eOverviewMode == Overview::eOMBvsC) + return i->mergeDetails == eBCAddedAndEqual || i->mergeDetails == eBCDeleted || i->mergeDetails == eBCChangedAndEqual; + return false; +} + +// Go to prev/next delta/conflict or first/last delta. +void MergeResultWindow::go( e_Direction eDir, e_EndPoint eEndPoint ) +{ + assert( eDir==eUp || eDir==eDown ); + MergeLineList::iterator i = m_currentMergeLineIt; + bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; + if( eEndPoint==eEnd ) + { + if (eDir==eUp) i = m_mergeLineList.begin(); // first mergeline + else i = --m_mergeLineList.end(); // last mergeline + + while ( isItAtEnd(eDir==eUp, i) && ! i->bDelta ) + { + if ( eDir==eUp ) ++i; // search downwards + else --i; // search upwards + } + } + else if ( eEndPoint == eDelta && isItAtEnd(eDir!=eUp, i) ) + { + do + { + if ( eDir==eUp ) --i; + else ++i; + } + while ( isItAtEnd(eDir!=eUp, i) && ( i->bDelta == false || checkOverviewIgnore(i) || bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ); + } + else if ( eEndPoint == eConflict && isItAtEnd(eDir!=eUp, i) ) + { + do + { + if ( eDir==eUp ) --i; + else ++i; + } + while ( isItAtEnd(eDir!=eUp, i) && (i->bConflict == false || bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ); + } + else if ( isItAtEnd(eDir!=eUp, i) && eEndPoint == eUnsolvedConflict ) + { + do + { + if ( eDir==eUp ) --i; + else ++i; + } + while ( isItAtEnd(eDir!=eUp, i) && ! i->mergeEditLineList.begin()->isConflict() ); + } + + if ( isVisible() ) + setFocus(); + + setFastSelector( i ); +} + +bool MergeResultWindow::isDeltaAboveCurrent() +{ + bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; + if (m_mergeLineList.empty()) return false; + MergeLineList::iterator i = m_currentMergeLineIt; + if (i == m_mergeLineList.begin()) return false; + do + { + --i; + if ( i->bDelta && !checkOverviewIgnore(i) && !( bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ) return true; + } + while (i!=m_mergeLineList.begin()); + + return false; +} + +bool MergeResultWindow::isDeltaBelowCurrent() +{ + bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; + if (m_mergeLineList.empty()) return false; + + MergeLineList::iterator i = m_currentMergeLineIt; + if (i!=m_mergeLineList.end()) + { + ++i; + for( ; i!=m_mergeLineList.end(); ++i ) + { + if ( i->bDelta && !checkOverviewIgnore(i) && !( bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ) return true; + } + } + return false; +} + +bool MergeResultWindow::isConflictAboveCurrent() +{ + if (m_mergeLineList.empty()) return false; + MergeLineList::iterator i = m_currentMergeLineIt; + if (i == m_mergeLineList.begin()) return false; + + bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; + + do + { + --i; + if ( i->bConflict && !(bSkipWhiteConflicts && i->bWhiteSpaceConflict) ) return true; + } + while (i!=m_mergeLineList.begin()); + + return false; +} + +bool MergeResultWindow::isConflictBelowCurrent() +{ + MergeLineList::iterator i = m_currentMergeLineIt; + if (m_mergeLineList.empty()) return false; + + bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; + + if (i!=m_mergeLineList.end()) + { + ++i; + for( ; i!=m_mergeLineList.end(); ++i ) + { + if ( i->bConflict && !(bSkipWhiteConflicts && i->bWhiteSpaceConflict) ) return true; + } + } + return false; +} + +bool MergeResultWindow::isUnsolvedConflictAtCurrent() +{ + if (m_mergeLineList.empty()) return false; + MergeLineList::iterator i = m_currentMergeLineIt; + return i->mergeEditLineList.begin()->isConflict(); +} + +bool MergeResultWindow::isUnsolvedConflictAboveCurrent() +{ + if (m_mergeLineList.empty()) return false; + MergeLineList::iterator i = m_currentMergeLineIt; + if (i == m_mergeLineList.begin()) return false; + + do + { + --i; + if ( i->mergeEditLineList.begin()->isConflict() ) return true; + } + while (i!=m_mergeLineList.begin()); + + return false; +} + +bool MergeResultWindow::isUnsolvedConflictBelowCurrent() +{ + MergeLineList::iterator i = m_currentMergeLineIt; + if (m_mergeLineList.empty()) return false; + + if (i!=m_mergeLineList.end()) + { + ++i; + for( ; i!=m_mergeLineList.end(); ++i ) + { + if ( i->mergeEditLineList.begin()->isConflict() ) return true; + } + } + return false; +} + +void MergeResultWindow::slotGoTop() +{ + go( eUp, eEnd ); +} + +void MergeResultWindow::slotGoCurrent() +{ + setFastSelector( m_currentMergeLineIt ); +} + +void MergeResultWindow::slotGoBottom() +{ + go( eDown, eEnd ); +} + +void MergeResultWindow::slotGoPrevDelta() +{ + go( eUp, eDelta ); +} + +void MergeResultWindow::slotGoNextDelta() +{ + go( eDown, eDelta ); +} + +void MergeResultWindow::slotGoPrevConflict() +{ + go( eUp, eConflict ); +} + +void MergeResultWindow::slotGoNextConflict() +{ + go( eDown, eConflict ); +} + +void MergeResultWindow::slotGoPrevUnsolvedConflict() +{ + go( eUp, eUnsolvedConflict ); +} + +void MergeResultWindow::slotGoNextUnsolvedConflict() +{ + go( eDown, eUnsolvedConflict ); +} + +/** The line is given as a index in the Diff3LineList. + The function calculates the corresponding iterator. */ +void MergeResultWindow::slotSetFastSelectorLine( int line ) +{ + MergeLineList::iterator i; + for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + if ( line>=i->d3lLineIdx && line < i->d3lLineIdx + i->srcRangeLength ) + { + //if ( i->bDelta ) + { + setFastSelector( i ); + } + break; + } + } +} + +int MergeResultWindow::getNrOfUnsolvedConflicts( int* pNrOfWhiteSpaceConflicts ) +{ + int nrOfUnsolvedConflicts = 0; + if (pNrOfWhiteSpaceConflicts!=0) + *pNrOfWhiteSpaceConflicts = 0; + + MergeLineList::iterator mlIt = m_mergeLineList.begin(); + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + MergeEditLineList::iterator melIt = ml.mergeEditLineList.begin(); + if ( melIt->isConflict() ) + { + ++nrOfUnsolvedConflicts; + if ( ml.bWhiteSpaceConflict && pNrOfWhiteSpaceConflicts!=0 ) + ++ *pNrOfWhiteSpaceConflicts; + } + } + + return nrOfUnsolvedConflicts; +} + +void MergeResultWindow::showNrOfConflicts() +{ + int nrOfConflicts = 0; + MergeLineList::iterator i; + for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + if ( i->bConflict || i->bDelta ) + ++nrOfConflicts; + } + QString totalInfo; + if ( m_pTotalDiffStatus->bBinaryAEqB && m_pTotalDiffStatus->bBinaryAEqC ) + totalInfo += i18n("All input files are binary equal."); + else if ( m_pTotalDiffStatus->bTextAEqB && m_pTotalDiffStatus->bTextAEqC ) + totalInfo += i18n("All input files contain the same text."); + else { + if ( m_pTotalDiffStatus->bBinaryAEqB ) totalInfo += i18n("Files %1 and %2 are binary equal.\n").arg("A").arg("B"); + else if ( m_pTotalDiffStatus->bTextAEqB ) totalInfo += i18n("Files %1 and %2 have equal text.\n").arg("A").arg("B"); + if ( m_pTotalDiffStatus->bBinaryAEqC ) totalInfo += i18n("Files %1 and %2 are binary equal.\n").arg("A").arg("C"); + else if ( m_pTotalDiffStatus->bTextAEqC ) totalInfo += i18n("Files %1 and %2 have equal text.\n").arg("A").arg("C"); + if ( m_pTotalDiffStatus->bBinaryBEqC ) totalInfo += i18n("Files %1 and %2 are binary equal.\n").arg("B").arg("C"); + else if ( m_pTotalDiffStatus->bTextBEqC ) totalInfo += i18n("Files %1 and %2 have equal text.\n").arg("B").arg("C"); + } + + int nrOfUnsolvedConflicts = getNrOfUnsolvedConflicts(); + + KMessageBox::information( this, + i18n("Total number of conflicts: ") + QString::number(nrOfConflicts) + + i18n("\nNr of automatically solved conflicts: ") + QString::number(nrOfConflicts-nrOfUnsolvedConflicts) + + i18n("\nNr of unsolved conflicts: ") + QString::number(nrOfUnsolvedConflicts) + + "\n"+totalInfo, + i18n("Conflicts") + ); +} + +void MergeResultWindow::setFastSelector(MergeLineList::iterator i) +{ + if ( i==m_mergeLineList.end() ) + return; + m_currentMergeLineIt = i; + emit setFastSelectorRange( i->d3lLineIdx, i->srcRangeLength ); + + int line1 = 0; + + MergeLineList::iterator mlIt = m_mergeLineList.begin(); + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + if(mlIt==m_currentMergeLineIt) + break; + line1 += mlIt->mergeEditLineList.size(); + } + + int nofLines = m_currentMergeLineIt->mergeEditLineList.size(); + int newFirstLine = getBestFirstLine( line1, nofLines, m_firstLine, getNofVisibleLines() ); + if ( newFirstLine != m_firstLine ) + { + scroll( 0, newFirstLine - m_firstLine ); + } + + if ( m_selection.isEmpty() ) + { + m_cursorXPos = 0; + m_cursorOldXPos = 0; + m_cursorYPos = line1; + } + + update(); + updateSourceMask(); + emit updateAvailabilities(); +} + +void MergeResultWindow::choose( int selector ) +{ + if ( m_currentMergeLineIt==m_mergeLineList.end() ) + return; + + setModified(); + + // First find range for which this change works. + MergeLine& ml = *m_currentMergeLineIt; + + MergeEditLineList::iterator melIt; + + // Now check if selector is active for this range already. + bool bActive = false; + + // Remove unneeded lines in the range. + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ) + { + MergeEditLine& mel = *melIt; + if ( mel.src()==selector ) + bActive = true; + + if ( mel.src()==selector || !mel.isEditableText() || mel.isModified() ) + melIt = ml.mergeEditLineList.erase( melIt ); + else + ++melIt; + } + + if ( !bActive ) // Selected source wasn't active. + { // Append the lines from selected source here at rangeEnd. + Diff3LineList::const_iterator d3llit=ml.id3l; + int j; + + for( j=0; jlineA : + mel.src()==2 ? mel.id3l()->lineB : + mel.src()==3 ? mel.id3l()->lineC : -1; + + if ( srcLine == -1 ) + melIt = ml.mergeEditLineList.erase( melIt ); + else + ++melIt; + } + } + + if ( ml.mergeEditLineList.empty() ) + { + // Insert a dummy line: + MergeEditLine mel(ml.id3l); + + if ( bActive ) mel.setConflict(); // All src entries deleted => conflict + else mel.setRemoved(selector); // No lines in corresponding src found. + + ml.mergeEditLineList.push_back(mel); + } + + if ( m_cursorYPos >= m_totalSize ) + { + m_cursorYPos = m_totalSize-1; + m_cursorXPos = 0; + } + + update(); + updateSourceMask(); + emit updateAvailabilities(); + int wsc; + int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); + m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") + .arg(nofUnsolved).arg(wsc) ); +} + +// bConflictsOnly: automatically choose for conflicts only (true) or for everywhere (false) +void MergeResultWindow::chooseGlobal(int selector, bool bConflictsOnly, bool bWhiteSpaceOnly ) +{ + resetSelection(); + + merge( false, selector, bConflictsOnly, bWhiteSpaceOnly ); + setModified( true ); + update(); + int wsc; + int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); + m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") + .arg(nofUnsolved).arg(wsc) ); +} + +void MergeResultWindow::slotAutoSolve() +{ + resetSelection(); + merge( true, -1 ); + setModified( true ); + update(); + int wsc; + int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); + m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") + .arg(nofUnsolved).arg(wsc) ); +} + +void MergeResultWindow::slotUnsolve() +{ + resetSelection(); + merge( false, -1 ); + setModified( true ); + update(); + int wsc; + int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); + m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") + .arg(nofUnsolved).arg(wsc) ); +} + +static QString calcHistoryLead(const QString& s ) +{ + // Return the start of the line until the first white char after the first non white char. + unsigned int i; + for( i=0; ibegin(), idxBegin=0; iBegin!=pD3LList->end(); ++iBegin, ++idxBegin ) + { + if ( historyStart.exactMatch( iBegin->getString(A) ) && + historyStart.exactMatch( iBegin->getString(B) ) && + ( !bThreeFiles || historyStart.exactMatch( iBegin->getString(C) ) ) ) + { + historyLead = calcHistoryLead( iBegin->getString(A) ); + break; + } + } + // Search for end of history + for( iEnd = iBegin, idxEnd = idxBegin; iEnd!=pD3LList->end(); ++iEnd, ++idxEnd ) + { + QString sA = iEnd->getString(A); + QString sB = iEnd->getString(B); + QString sC = iEnd->getString(C); + if ( ! ((sA.isNull() || historyLead == calcHistoryLead(sA) ) && + (sB.isNull() || historyLead == calcHistoryLead(sB) ) && + (!bThreeFiles || sC.isNull() || historyLead == calcHistoryLead(sC) ) + )) + { + break; // End of the history + } + } +} + +bool findParenthesesGroups( const QString& s, QStringList& sl ) +{ + sl.clear(); + int i=0; + std::list startPosStack; + int length = s.length(); + for( i=0; i(int)parenthesesGroupList.size() ) + continue; + QString s = matchedRegExpr.cap( groupIdx ); + if ( groupIdx == 0 ) + { + key += s + " "; + continue; + } + + QString groupRegExp = parenthesesGroupList[groupIdx-1]; + if( groupRegExp.find('|')<0 || groupRegExp.find('(')>=0 ) + { + bool bOk = false; + int i = s.toInt( &bOk ); + if ( bOk && i>=0 && i<10000 ) + s.sprintf("%04d", i); // This should help for correct sorting of numbers. + key += s + " "; + } + else + { + // Assume that the groupRegExp consists of something like "Jan|Feb|Mar|Apr" + // s is the string that managed to match. + // Now we want to know at which position it occurred. e.g. Jan=0, Feb=1, Mar=2, etc. + QStringList sl = QStringList::split( '|', groupRegExp ); + int idx = sl.findIndex( s ); + if (idx<0) + { + // Didn't match + } + else + { + QString sIdx; + sIdx.sprintf("%02d", idx+1 ); // Up to 99 words in the groupRegExp (more than 12 aren't expected) + key += sIdx + " "; + } + } + } + return key; +} + +void MergeResultWindow::collectHistoryInformation( + int src, Diff3LineList::const_iterator iHistoryBegin, Diff3LineList::const_iterator iHistoryEnd, + HistoryMap& historyMap, + std::list< HistoryMap::iterator >& hitList // list of iterators + ) +{ + std::list< HistoryMap::iterator >::iterator itHitListFront = hitList.begin(); + Diff3LineList::const_iterator id3l = iHistoryBegin; + QString historyLead; + { + const LineData* pld = id3l->getLineData(src); + QString s( pld->pLine, pld->size ); + historyLead = calcHistoryLead(s); + } + QRegExp historyStart = m_pOptionDialog->m_historyStartRegExp; + ++id3l; // Skip line with "$Log ... $" + QRegExp newHistoryEntry = m_pOptionDialog->m_historyEntryStartRegExp; + QStringList parenthesesGroups; + findParenthesesGroups( m_pOptionDialog->m_historyEntryStartRegExp, parenthesesGroups ); + QString key; + MergeEditLineList melList; + bool bPrevLineIsEmpty = true; + bool bUseRegExp = !m_pOptionDialog->m_historyEntryStartRegExp.isEmpty(); + for(; id3l != iHistoryEnd; ++id3l ) + { + const LineData* pld = id3l->getLineData(src); + if ( !pld ) continue; + QString s( pld->pLine, pld->size ); + if (historyLead.isNull()) historyLead = calcHistoryLead(s); + QString sLine = s.mid(historyLead.length()); + if ( ( !bUseRegExp && !sLine.stripWhiteSpace().isEmpty() && bPrevLineIsEmpty ) + || bUseRegExp && newHistoryEntry.exactMatch( sLine ) + ) + { + if ( !key.isEmpty() && !melList.empty() ) + { + // Only insert new HistoryMapEntry if key not found; in either case p.first is a valid iterator to element key. + std::pair p = historyMap.insert(HistoryMap::value_type(key,HistoryMapEntry())); + HistoryMapEntry& hme = p.first->second; + if ( src==A ) hme.mellA = melList; + if ( src==B ) hme.mellB = melList; + if ( src==C ) hme.mellC = melList; + if ( p.second ) // Not in list yet? + { + hitList.insert( itHitListFront, p.first ); + } + } + + if ( ! bUseRegExp ) + key = sLine; + else + key = calcHistorySortKey(m_pOptionDialog->m_historyEntryStartSortKeyOrder,newHistoryEntry,parenthesesGroups); + + melList.clear(); + melList.push_back( MergeEditLine(id3l,src) ); + } + else if ( ! historyStart.exactMatch( s ) ) + { + melList.push_back( MergeEditLine(id3l,src) ); + } + + bPrevLineIsEmpty = sLine.stripWhiteSpace().isEmpty(); + } + if ( !key.isEmpty() ) + { + // Only insert new HistoryMapEntry if key not found; in either case p.first is a valid iterator to element key. + std::pair p = historyMap.insert(HistoryMap::value_type(key,HistoryMapEntry())); + HistoryMapEntry& hme = p.first->second; + if ( src==A ) hme.mellA = melList; + if ( src==B ) hme.mellB = melList; + if ( src==C ) hme.mellC = melList; + if ( p.second ) // Not in list yet? + { + hitList.insert( itHitListFront, p.first ); + } + } + // End of the history +} + +MergeResultWindow::MergeEditLineList& MergeResultWindow::HistoryMapEntry::choice( bool bThreeInputs ) +{ + if ( !bThreeInputs ) + return mellA.empty() ? mellB : mellA; + else + { + if ( mellA.empty() ) + return mellC.empty() ? mellB : mellC; // A doesn't exist, return one that exists + else if ( ! mellB.empty() && ! mellC.empty() ) + { // A, B and C exist + return mellA; + } + else + return mellB.empty() ? mellB : mellC; // A exists, return the one that doesn't exist + } +} + +bool MergeResultWindow::HistoryMapEntry::staysInPlace( bool bThreeInputs, Diff3LineList::const_iterator& iHistoryEnd ) +{ + // The entry should stay in place if the decision made by the automerger is correct. + Diff3LineList::const_iterator& iHistoryLast = iHistoryEnd; + --iHistoryLast; + if ( !bThreeInputs ) + { + if ( !mellA.empty() && !mellB.empty() && mellA.begin()->id3l()==mellB.begin()->id3l() && + mellA.back().id3l() == iHistoryLast && mellB.back().id3l() == iHistoryLast ) + { + iHistoryEnd = mellA.begin()->id3l(); + return true; + } + else + { + return false; + } + } + else + { + if ( !mellA.empty() && !mellB.empty() && !mellC.empty() + && mellA.begin()->id3l()==mellB.begin()->id3l() && mellA.begin()->id3l()==mellC.begin()->id3l() + && mellA.back().id3l() == iHistoryLast && mellB.back().id3l() == iHistoryLast && mellC.back().id3l() == iHistoryLast ) + { + iHistoryEnd = mellA.begin()->id3l(); + return true; + } + else + { + return false; + } + } +} + +void MergeResultWindow::slotMergeHistory() +{ + Diff3LineList::const_iterator iD3LHistoryBegin; + Diff3LineList::const_iterator iD3LHistoryEnd; + int d3lHistoryBeginLineIdx = -1; + int d3lHistoryEndLineIdx = -1; + + // Search for history start, history end in the diff3LineList + findHistoryRange( m_pOptionDialog->m_historyStartRegExp, m_pldC!=0, m_pDiff3LineList, iD3LHistoryBegin, iD3LHistoryEnd, d3lHistoryBeginLineIdx, d3lHistoryEndLineIdx ); + + if ( iD3LHistoryBegin != m_pDiff3LineList->end() ) + { + // Now collect the historyMap information + HistoryMap historyMap; + std::list< HistoryMap::iterator > hitList; + if (m_pldC==0) + { + collectHistoryInformation( A, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); + collectHistoryInformation( B, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); + } + else + { + collectHistoryInformation( A, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); + collectHistoryInformation( B, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); + collectHistoryInformation( C, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); + } + + Diff3LineList::const_iterator iD3LHistoryOrigEnd = iD3LHistoryEnd; + + bool bHistoryMergeSorting = m_pOptionDialog->m_bHistoryMergeSorting && ! m_pOptionDialog->m_historyEntryStartSortKeyOrder.isEmpty() && + ! m_pOptionDialog->m_historyEntryStartRegExp.isEmpty(); + + if ( m_pOptionDialog->m_maxNofHistoryEntries==-1 ) + { + // Remove parts from the historyMap and hitList that stay in place + if ( bHistoryMergeSorting ) + { + while ( ! historyMap.empty() ) + { + HistoryMap::iterator hMapIt = historyMap.begin(); + if( hMapIt->second.staysInPlace( m_pldC!=0, iD3LHistoryEnd ) ) + historyMap.erase(hMapIt); + else + break; + } + } + else + { + while ( ! hitList.empty() ) + { + HistoryMap::iterator hMapIt = hitList.back(); + if( hMapIt->second.staysInPlace( m_pldC!=0, iD3LHistoryEnd ) ) + hitList.pop_back(); + else + break; + } + } + while (iD3LHistoryOrigEnd != iD3LHistoryEnd) + { + --iD3LHistoryOrigEnd; + --d3lHistoryEndLineIdx; + } + } + + MergeLineList::iterator iMLLStart = splitAtDiff3LineIdx(d3lHistoryBeginLineIdx); + MergeLineList::iterator iMLLEnd = splitAtDiff3LineIdx(d3lHistoryEndLineIdx); + // Now join all MergeLines in the history + MergeLineList::iterator i = iMLLStart; + if ( i != iMLLEnd ) + { + ++i; + while ( i!=iMLLEnd ) + { + iMLLStart->join(*i); + i = m_mergeLineList.erase( i ); + } + } + iMLLStart->mergeEditLineList.clear(); + // Now insert the complete history into the first MergeLine of the history + iMLLStart->mergeEditLineList.push_back( MergeEditLine( iD3LHistoryBegin, m_pldC == 0 ? B : C ) ); + QString lead = calcHistoryLead( iD3LHistoryBegin->getString(A) ); + MergeEditLine mel( m_pDiff3LineList->end() ); + mel.setString( lead ); + iMLLStart->mergeEditLineList.push_back(mel); + + int historyCount = 0; + if ( bHistoryMergeSorting ) + { + // Create a sorted history + HistoryMap::reverse_iterator hmit; + for ( hmit = historyMap.rbegin(); hmit != historyMap.rend(); ++hmit ) + { + if ( historyCount==m_pOptionDialog->m_maxNofHistoryEntries ) + break; + ++historyCount; + HistoryMapEntry& hme = hmit->second; + MergeEditLineList& mell = hme.choice(m_pldC!=0); + if (!mell.empty()) + iMLLStart->mergeEditLineList.splice( iMLLStart->mergeEditLineList.end(), mell, mell.begin(), mell.end() ); + } + } + else + { + // Create history in order of appearance + std::list< HistoryMap::iterator >::iterator hlit; + for ( hlit = hitList.begin(); hlit != hitList.end(); ++hlit ) + { + if ( historyCount==m_pOptionDialog->m_maxNofHistoryEntries ) + break; + ++historyCount; + HistoryMapEntry& hme = (*hlit)->second; + MergeEditLineList& mell = hme.choice(m_pldC!=0); + if (!mell.empty()) + iMLLStart->mergeEditLineList.splice( iMLLStart->mergeEditLineList.end(), mell, mell.begin(), mell.end() ); + } + } + setFastSelector( iMLLStart ); + update(); + } +} + +void MergeResultWindow::slotRegExpAutoMerge() +{ + if ( m_pOptionDialog->m_autoMergeRegExp.isEmpty() ) + return; + + QRegExp vcsKeywords = m_pOptionDialog->m_autoMergeRegExp; + MergeLineList::iterator i; + for ( i=m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + if (i->bConflict ) + { + Diff3LineList::const_iterator id3l = i->id3l; + if ( vcsKeywords.exactMatch( id3l->getString(A) ) && + vcsKeywords.exactMatch( id3l->getString(B) ) && + (m_pldC==0 || vcsKeywords.exactMatch( id3l->getString(C) ))) + { + MergeEditLine& mel = *i->mergeEditLineList.begin(); + mel.setSource( m_pldC==0 ? B : C, false ); + splitAtDiff3LineIdx( i->d3lLineIdx+1 ); + } + } + } + update(); +} + +// This doesn't detect user modifications and should only be called after automatic merge +// This will only do something for three file merge. +// Irrelevant changes are those where all contributions from B are already contained in C. +// Also irrelevant are conflicts automatically solved (automerge regexp and history automerge) +// Precondition: The VCS-keyword would also be C. +bool MergeResultWindow::doRelevantChangesExist() +{ + if ( m_pldC==0 || m_mergeLineList.size() <= 1 ) + return true; + + MergeLineList::iterator i; + for ( i=m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + if ( ( i->bConflict && i->mergeEditLineList.begin()->src()!=C ) + || i->srcSelect == B ) + { + return true; + } + } + + return false; +} + +// Returns the iterator to the MergeLine after the split +MergeResultWindow::MergeLineList::iterator MergeResultWindow::splitAtDiff3LineIdx( int d3lLineIdx ) +{ + MergeLineList::iterator i; + for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + if ( i->d3lLineIdx==d3lLineIdx ) + { + // No split needed, this is the beginning of a MergeLine + return i; + } + else if ( i->d3lLineIdx > d3lLineIdx ) + { + // The split must be in the previous MergeLine + --i; + MergeLine& ml = *i; + MergeLine newML; + ml.split(newML,d3lLineIdx); + ++i; + return m_mergeLineList.insert( i, newML ); + } + } + // The split must be in the previous MergeLine + --i; + MergeLine& ml = *i; + MergeLine newML; + ml.split(newML,d3lLineIdx); + ++i; + return m_mergeLineList.insert( i, newML ); +} + +void MergeResultWindow::slotSplitDiff( int firstD3lLineIdx, int lastD3lLineIdx ) +{ + if (lastD3lLineIdx>=0) + splitAtDiff3LineIdx( lastD3lLineIdx + 1 ); + setFastSelector( splitAtDiff3LineIdx(firstD3lLineIdx) ); +} + +void MergeResultWindow::slotJoinDiffs( int firstD3lLineIdx, int lastD3lLineIdx ) +{ + MergeLineList::iterator i; + MergeLineList::iterator iMLLStart = m_mergeLineList.end(); + MergeLineList::iterator iMLLEnd = m_mergeLineList.end(); + for ( i=m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) + { + MergeLine& ml = *i; + if ( firstD3lLineIdx >= ml.d3lLineIdx && firstD3lLineIdx < ml.d3lLineIdx + ml.srcRangeLength ) + { + iMLLStart = i; + } + if ( lastD3lLineIdx >= ml.d3lLineIdx && lastD3lLineIdx < ml.d3lLineIdx + ml.srcRangeLength ) + { + iMLLEnd = i; + ++iMLLEnd; + break; + } + } + + bool bJoined = false; + for( i=iMLLStart; i!=iMLLEnd && i!=m_mergeLineList.end(); ) + { + if ( i==iMLLStart ) + { + ++i; + } + else + { + iMLLStart->join(*i); + i = m_mergeLineList.erase( i ); + bJoined = true; + } + } + if (bJoined) + { + iMLLStart->mergeEditLineList.clear(); + // Insert a conflict line as placeholder + iMLLStart->mergeEditLineList.push_back( MergeEditLine( iMLLStart->id3l ) ); + } + setFastSelector( iMLLStart ); +} + +void MergeResultWindow::myUpdate(int afterMilliSecs) +{ + killTimer(m_delayedDrawTimer); + m_bMyUpdate = true; + m_delayedDrawTimer = startTimer( afterMilliSecs ); +} + +void MergeResultWindow::timerEvent(QTimerEvent*) +{ + killTimer(m_delayedDrawTimer); + m_delayedDrawTimer = 0; + + if ( m_bMyUpdate ) + { + update(); + m_bMyUpdate = false; + } + + if ( m_scrollDeltaX != 0 || m_scrollDeltaY != 0 ) + { + m_selection.end( m_selection.lastLine + m_scrollDeltaY, m_selection.lastPos + m_scrollDeltaX ); + emit scroll( m_scrollDeltaX, m_scrollDeltaY ); + killTimer(m_delayedDrawTimer); + m_delayedDrawTimer = startTimer(50); + } +} + +QString MergeResultWindow::MergeEditLine::getString( const MergeResultWindow* mrw ) +{ + if ( isRemoved() ) { return QString(); } + + if ( ! isModified() ) + { + int src = m_src; + if ( src == 0 ) { return QString(); } + const Diff3Line& d3l = *m_id3l; + const LineData* pld = 0; + assert( src == A || src == B || src == C ); + if ( src == A && d3l.lineA!=-1 ) pld = &mrw->m_pldA[ d3l.lineA ]; + else if ( src == B && d3l.lineB!=-1 ) pld = &mrw->m_pldB[ d3l.lineB ]; + else if ( src == C && d3l.lineC!=-1 ) pld = &mrw->m_pldC[ d3l.lineC ]; + + if ( pld == 0 ) + { + // assert(false); This is no error. + return QString(); + } + + return QString( pld->pLine, pld->size ); + } + else + { + return m_str; + } + return 0; +} + +/// Converts the cursor-posOnScreen into a text index, considering tabulators. +int convertToPosInText( const QString& s, int posOnScreen, int tabSize ) +{ + int localPosOnScreen = 0; + int size=s.length(); + for ( int i=0; i=posOnScreen ) + return i; + + // All letters except tabulator have width one. + int letterWidth = s[i]!='\t' ? 1 : tabber( localPosOnScreen, tabSize ); + + localPosOnScreen += letterWidth; + + if ( localPosOnScreen>posOnScreen ) + return i; + } + return size; +} + + +/// Converts the index into the text to a cursor-posOnScreen considering tabulators. +int convertToPosOnScreen( const QString& p, int posInText, int tabSize ) +{ + int posOnScreen = 0; + for ( int i=0; i height() ) + return; + + yOffset += topLineYOffset; + + QString srcName = " "; + if ( bUserModified ) srcName = "m"; + else if ( srcSelect == A && mergeDetails != eNoChange ) srcName = "A"; + else if ( srcSelect == B ) srcName = "B"; + else if ( srcSelect == C ) srcName = "C"; + + if ( rangeMark & 4 ) + { + p.fillRect( xOffset, yOffset, width(), fontHeight, m_pOptionDialog->m_currentRangeBgColor ); + } + + if( (srcSelect > 0 || bUserModified ) && !bLineRemoved ) + { + int outPos = 0; + QString s; + int size = str.length(); + for ( int i=0; im_tabSize ); + for( int j=0; jm_tabSize ),m_pOptionDialog->m_tabSize ); + int lastPosInLine = convertToPosOnScreen( str, convertToPosInText( str, m_selection.lastPosInLine(line), m_pOptionDialog->m_tabSize ), m_pOptionDialog->m_tabSize ); + int lengthInLine = max2(0,lastPosInLine - firstPosInLine); + if (lengthInLine>0) m_selection.bSelectionContainsData = true; + + if ( lengthInLine < int(s.length()) ) + { // Draw a normal line first + p.setPen( m_pOptionDialog->m_fgColor ); + p.drawText( xOffset, yOffset+fontAscent, s.mid(m_firstColumn), true ); + } + int firstPosInLine2 = max2( firstPosInLine, m_firstColumn ); + int lengthInLine2 = max2(0,lastPosInLine - firstPosInLine2); + + if( m_selection.lineWithin( line+1 ) ) + p.fillRect( xOffset + fontWidth*(firstPosInLine2-m_firstColumn), yOffset, + width(), fontHeight, colorGroup().highlight() ); + else if ( lengthInLine2>0 ) + p.fillRect( xOffset + fontWidth*(firstPosInLine2-m_firstColumn), yOffset, + fontWidth*lengthInLine2, fontHeight, colorGroup().highlight() ); + + p.setPen( colorGroup().highlightedText() ); + p.drawText( xOffset + fontWidth*(firstPosInLine2-m_firstColumn), yOffset+fontAscent, + s.mid(firstPosInLine2,lengthInLine2), true ); + } + else + { + p.setPen( m_pOptionDialog->m_fgColor ); + p.drawText( xOffset, yOffset+fontAscent, s.mid(m_firstColumn), true ); + } + + p.setPen( m_pOptionDialog->m_fgColor ); + if ( m_cursorYPos==line ) + { + m_cursorXPos = minMaxLimiter( m_cursorXPos, 0, outPos ); + m_cursorXPos = convertToPosOnScreen( str, convertToPosInText( str, m_cursorXPos, m_pOptionDialog->m_tabSize ),m_pOptionDialog->m_tabSize ); + } + + p.drawText( 1, yOffset+fontAscent, srcName, true ); + } + else if ( bLineRemoved ) + { + p.setPen( m_pOptionDialog->m_colorForConflict ); + p.drawText( xOffset, yOffset+fontAscent, i18n("") ); + p.drawText( 1, yOffset+fontAscent, srcName ); + if ( m_cursorYPos==line ) m_cursorXPos = 0; + } + else if ( srcSelect == 0 ) + { + p.setPen( m_pOptionDialog->m_colorForConflict ); + if ( bWhiteSpaceConflict ) + p.drawText( xOffset, yOffset+fontAscent, i18n("") ); + else + p.drawText( xOffset, yOffset+fontAscent, i18n("") ); + p.drawText( 1, yOffset+fontAscent, "?" ); + if ( m_cursorYPos==line ) m_cursorXPos = 0; + } + else assert(false); + + xOffset -= fontWidth; + p.setPen( m_pOptionDialog->m_fgColor ); + if ( rangeMark & 1 ) // begin mark + { + p.drawLine( xOffset, yOffset+1, xOffset, yOffset+fontHeight/2 ); + p.drawLine( xOffset, yOffset+1, xOffset-2, yOffset+1 ); + } + else + { + p.drawLine( xOffset, yOffset, xOffset, yOffset+fontHeight/2 ); + } + + if ( rangeMark & 2 ) // end mark + { + p.drawLine( xOffset, yOffset+fontHeight/2, xOffset, yOffset+fontHeight-1 ); + p.drawLine( xOffset, yOffset+fontHeight-1, xOffset-2, yOffset+fontHeight-1 ); + } + else + { + p.drawLine( xOffset, yOffset+fontHeight/2, xOffset, yOffset+fontHeight ); + } + + if ( rangeMark & 4 ) + { + p.fillRect( xOffset + 3, yOffset, 3, fontHeight, m_pOptionDialog->m_fgColor ); +/* p.setPen( blue ); + p.drawLine( xOffset+2, yOffset, xOffset+2, yOffset+fontHeight-1 ); + p.drawLine( xOffset+3, yOffset, xOffset+3, yOffset+fontHeight-1 );*/ + } +} + +void MergeResultWindow::setPaintingAllowed(bool bPaintingAllowed) +{ + m_bPaintingAllowed = bPaintingAllowed; + if ( !m_bPaintingAllowed ) + { + m_currentMergeLineIt = m_mergeLineList.end(); + reset(); + } +} + +void MergeResultWindow::paintEvent( QPaintEvent* ) +{ + if (m_pDiff3LineList==0 || !m_bPaintingAllowed) return; + + bool bOldSelectionContainsData = m_selection.bSelectionContainsData; + const QFontMetrics& fm = fontMetrics(); + int fontHeight = fm.height(); + int fontWidth = fm.width("W"); + int fontAscent = fm.ascent(); + + if ( !m_bCursorUpdate ) // Don't redraw everything for blinking cursor? + { + m_selection.bSelectionContainsData = false; + if ( size() != m_pixmap.size() ) + m_pixmap.resize(size()); + + MyPainter p(&m_pixmap, m_pOptionDialog->m_bRightToLeftLanguage, width(), fontWidth); + p.setFont( font() ); + p.QPainter::fillRect( rect(), m_pOptionDialog->m_bgColor ); + + //int visibleLines = height() / fontHeight; + + int lastVisibleLine = m_firstLine + getNofVisibleLines() + 5; + int nofColumns = 0; + int line = 0; + MergeLineList::iterator mlIt = m_mergeLineList.begin(); + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + if ( line > lastVisibleLine || line + ml.mergeEditLineList.size() < m_firstLine) + { + line += ml.mergeEditLineList.size(); + } + else + { + MergeEditLineList::iterator melIt; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) + { + if (line>=m_firstLine && line<=lastVisibleLine) + { + MergeEditLine& mel = *melIt; + MergeEditLineList::iterator melIt1 = melIt; + ++melIt1; + + int rangeMark = 0; + if ( melIt==ml.mergeEditLineList.begin() ) rangeMark |= 1; // Begin range mark + if ( melIt1==ml.mergeEditLineList.end() ) rangeMark |= 2; // End range mark + + if ( mlIt == m_currentMergeLineIt ) rangeMark |= 4; // Mark of the current line + + QString s; + s = mel.getString( this ); + if ( convertToPosOnScreen(s,s.length(),m_pOptionDialog->m_tabSize) >nofColumns) + nofColumns = s.length(); + + writeLine( p, line, s, mel.src(), ml.mergeDetails, rangeMark, + mel.isModified(), mel.isRemoved(), ml.bWhiteSpaceConflict ); + } + ++line; + } + } + } + + if ( line != m_nofLines || nofColumns != m_nofColumns ) + { + m_nofLines = line; + assert( m_nofLines == m_totalSize ); + + m_nofColumns = nofColumns; + emit resizeSignal(); + } + + p.end(); + } + + QPainter painter(this); + + int topLineYOffset = 0; + int xOffset = fontWidth * leftInfoWidth; + int yOffset = ( m_cursorYPos - m_firstLine ) * fontHeight + topLineYOffset; + int xCursor = ( m_cursorXPos - m_firstColumn ) * fontWidth + xOffset; + + if ( !m_bCursorUpdate ) + painter.drawPixmap(0,0, m_pixmap); + else + { + if (!m_pOptionDialog->m_bRightToLeftLanguage) + painter.drawPixmap(xCursor-2, yOffset, m_pixmap, + xCursor-2, yOffset, 5, fontAscent+2 ); + else + painter.drawPixmap(width()-1-4-(xCursor-2), yOffset, m_pixmap, + width()-1-4-(xCursor-2), yOffset, 5, fontAscent+2 ); + m_bCursorUpdate = false; + } + painter.end(); + + if ( m_bCursorOn && hasFocus() && m_cursorYPos>=m_firstLine ) + { + MyPainter painter(this, m_pOptionDialog->m_bRightToLeftLanguage, width(), fontWidth); + int topLineYOffset = 0; + int xOffset = fontWidth * leftInfoWidth; + + int yOffset = ( m_cursorYPos-m_firstLine ) * fontHeight + topLineYOffset; + + int xCursor = ( m_cursorXPos - m_firstColumn ) * fontWidth + xOffset; + + painter.setPen( m_pOptionDialog->m_fgColor ); + + painter.drawLine( xCursor, yOffset, xCursor, yOffset+fontAscent ); + painter.drawLine( xCursor-2, yOffset, xCursor+2, yOffset ); + painter.drawLine( xCursor-2, yOffset+fontAscent+1, xCursor+2, yOffset+fontAscent+1 ); + } + + if( !bOldSelectionContainsData && m_selection.bSelectionContainsData ) + emit newSelection(); +} + +void MergeResultWindow::updateSourceMask() +{ + int srcMask=0; + int enabledMask = 0; + if( !hasFocus() || m_pDiff3LineList==0 || !m_bPaintingAllowed || m_currentMergeLineIt == m_mergeLineList.end() ) + { + srcMask = 0; + enabledMask = 0; + } + else + { + enabledMask = m_pldC==0 ? 3 : 7; + MergeLine& ml = *m_currentMergeLineIt; + + srcMask = 0; + bool bModified = false; + MergeEditLineList::iterator melIt; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) + { + MergeEditLine& mel = *melIt; + if ( mel.src()==1 ) srcMask |= 1; + if ( mel.src()==2 ) srcMask |= 2; + if ( mel.src()==3 ) srcMask |= 4; + if ( mel.isModified() || !mel.isEditableText() ) bModified = true; + } + + if ( ml.mergeDetails == eNoChange ) + { + srcMask = 0; + enabledMask = bModified ? 1 : 0; + } + } + + emit sourceMask( srcMask, enabledMask ); +} + +void MergeResultWindow::focusInEvent( QFocusEvent* e ) +{ + updateSourceMask(); + QWidget::focusInEvent(e); +} + +void MergeResultWindow::convertToLinePos( int x, int y, int& line, int& pos ) +{ + const QFontMetrics& fm = fontMetrics(); + int fontHeight = fm.height(); + int fontWidth = fm.width('W'); + int xOffset = (leftInfoWidth-m_firstColumn)*fontWidth; + int topLineYOffset = 0; + + int yOffset = topLineYOffset - m_firstLine * fontHeight; + + line = min2( ( y - yOffset ) / fontHeight, m_totalSize-1 ); + if ( ! m_pOptionDialog->m_bRightToLeftLanguage ) + pos = ( x - xOffset ) / fontWidth; + else + pos = ( (width() - 1 - x) - xOffset ) / fontWidth; +} + +void MergeResultWindow::mousePressEvent ( QMouseEvent* e ) +{ + m_bCursorOn = true; + + int line; + int pos; + convertToLinePos( e->x(), e->y(), line, pos ); + + bool bLMB = e->button() == Qt::LeftButton; + bool bMMB = e->button() == Qt::MidButton; + bool bRMB = e->button() == Qt::RightButton; + + if ( bLMB && pos < m_firstColumn || bRMB ) // Fast range selection + { + m_cursorXPos = 0; + m_cursorOldXPos = 0; + m_cursorYPos = max2(line,0); + int l = 0; + MergeLineList::iterator i = m_mergeLineList.begin(); + for(i = m_mergeLineList.begin();i!=m_mergeLineList.end(); ++i) + { + if (l==line) + break; + + l += i->mergeEditLineList.size(); + if (l>line) + break; + } + m_selection.reset(); // Disable current selection + + m_bCursorOn = true; + setFastSelector( i ); + + if (bRMB) + { + showPopupMenu( QCursor::pos() ); + } + } + else if ( bLMB ) // Normal cursor placement + { + pos = max2(pos,0); + line = max2(line,0); + if ( e->state() & Qt::ShiftButton ) + { + if (m_selection.firstLine==-1) + m_selection.start( line, pos ); + m_selection.end( line, pos ); + } + else + { + // Selection + m_selection.reset(); + m_selection.start( line, pos ); + m_selection.end( line, pos ); + } + m_cursorXPos = pos; + m_cursorOldXPos = pos; + m_cursorYPos = line; + + update(); + //showStatusLine( line, m_winIdx, m_pFilename, m_pDiff3LineList, m_pStatusBar ); + } + else if ( bMMB ) // Paste clipboard + { + pos = max2(pos,0); + line = max2(line,0); + + m_selection.reset(); + m_cursorXPos = pos; + m_cursorOldXPos = pos; + m_cursorYPos = line; + + pasteClipboard( true ); + } +} + +void MergeResultWindow::mouseDoubleClickEvent( QMouseEvent* e ) +{ + if ( e->button() == Qt::LeftButton ) + { + int line; + int pos; + convertToLinePos( e->x(), e->y(), line, pos ); + m_cursorXPos = pos; + m_cursorOldXPos = pos; + m_cursorYPos = line; + + // Get the string data of the current line + + MergeLineList::iterator mlIt; + MergeEditLineList::iterator melIt; + calcIteratorFromLineNr( line, mlIt, melIt ); + QString s = melIt->getString( this ); + + if ( !s.isEmpty() ) + { + int pos1, pos2; + + calcTokenPos( s, pos, pos1, pos2, m_pOptionDialog->m_tabSize ); + + resetSelection(); + m_selection.start( line, convertToPosOnScreen( s, pos1, m_pOptionDialog->m_tabSize ) ); + m_selection.end( line, convertToPosOnScreen( s, pos2, m_pOptionDialog->m_tabSize ) ); + + update(); + // emit selectionEnd() happens in the mouseReleaseEvent. + } + } +} + +void MergeResultWindow::mouseReleaseEvent ( QMouseEvent * e ) +{ + if ( e->button() == Qt::LeftButton ) + { + killTimer(m_delayedDrawTimer); + m_delayedDrawTimer = 0; + + if (m_selection.firstLine != -1 ) + { + emit selectionEnd(); + } + } +} + +void MergeResultWindow::mouseMoveEvent ( QMouseEvent * e ) +{ + int line; + int pos; + convertToLinePos( e->x(), e->y(), line, pos ); + m_cursorXPos = pos; + m_cursorOldXPos = pos; + m_cursorYPos = line; + if (m_selection.firstLine != -1 ) + { + m_selection.end( line, pos ); + myUpdate(0); + + //showStatusLine( line, m_winIdx, m_pFilename, m_pDiff3LineList, m_pStatusBar ); + + // Scroll because mouse moved out of the window + const QFontMetrics& fm = fontMetrics(); + int fontWidth = fm.width('W'); + int topLineYOffset = 0; + int deltaX=0; + int deltaY=0; + if ( ! m_pOptionDialog->m_bRightToLeftLanguage ) + { + if ( e->x() < leftInfoWidth*fontWidth ) deltaX=-1; + if ( e->x() > width() ) deltaX=+1; + } + else + { + if ( e->x() > width()-1-leftInfoWidth*fontWidth ) deltaX=-1; + if ( e->x() < fontWidth ) deltaX=+1; + } + if ( e->y() < topLineYOffset ) deltaY=-1; + if ( e->y() > height() ) deltaY=+1; + m_scrollDeltaX = deltaX; + m_scrollDeltaY = deltaY; + if ( deltaX != 0 || deltaY!= 0) + { + emit scroll( deltaX, deltaY ); + } + } +} + + +void MergeResultWindow::slotCursorUpdate() +{ + m_cursorTimer.stop(); + m_bCursorOn = !m_bCursorOn; + + if ( isVisible() ) + { + m_bCursorUpdate = true; + + const QFontMetrics& fm = fontMetrics(); + int fontWidth = fm.width("W"); + int topLineYOffset = 0; + int xOffset = fontWidth * leftInfoWidth; + int yOffset = ( m_cursorYPos - m_firstLine ) * fm.height() + topLineYOffset; + int xCursor = ( m_cursorXPos - m_firstColumn ) * fontWidth + xOffset; + + if (!m_pOptionDialog->m_bRightToLeftLanguage) + repaint( xCursor-2, yOffset, 5, fm.ascent()+2 ); + else + repaint( width()-1-4-(xCursor-2), yOffset, 5, fm.ascent()+2 ); + + m_bCursorUpdate=false; + } + + m_cursorTimer.start(500,true); +} + + +void MergeResultWindow::wheelEvent( QWheelEvent* e ) +{ + int d = -e->delta()*QApplication::wheelScrollLines()/120; + e->accept(); + scroll( 0, min2(d, getNofVisibleLines()) ); +} + + +void MergeResultWindow::keyPressEvent( QKeyEvent* e ) +{ + int y = m_cursorYPos; + MergeLineList::iterator mlIt; + MergeEditLineList::iterator melIt; + calcIteratorFromLineNr( y, mlIt, melIt ); + + QString str = melIt->getString( this ); + int x = convertToPosInText( str, m_cursorXPos, m_pOptionDialog->m_tabSize ); + + bool bCtrl = ( e->state() & Qt::ControlButton ) != 0 ; + bool bShift = ( e->state() & Qt::ShiftButton ) != 0 ; + #ifdef _WIN32 + bool bAlt = ( e->state() & Qt::AltButton ) != 0 ; + if ( bCtrl && bAlt ){ bCtrl=false; bAlt=false; } // AltGr-Key pressed. + #endif + + bool bYMoveKey = false; + // Special keys + switch ( e->key() ) + { + case Qt::Key_Escape: break; + //case Key_Tab: break; + case Qt::Key_Backtab: break; + case Qt::Key_Delete: + { + if ( deleteSelection2( str, x, y, mlIt, melIt )) break; + if( !melIt->isEditableText() ) break; + if (x>=(int)str.length()) + { + if ( yisEditableText() ) + { + QString s2 = melIt1->getString( this ); + melIt->setString( str + s2 ); + + // Remove the line + if ( mlIt1->mergeEditLineList.size()>1 ) + mlIt1->mergeEditLineList.erase( melIt1 ); + else + melIt1->setRemoved(); + } + } + } + else + { + QString s = str.left(x); + s += str.mid( x+1 ); + melIt->setString( s ); + setModified(); + } + break; + } + case Qt::Key_Backspace: + { + if ( deleteSelection2( str, x, y, mlIt, melIt )) break; + if( !melIt->isEditableText() ) break; + if (x==0) + { + if ( y>0 ) + { + setModified(); + MergeLineList::iterator mlIt1; + MergeEditLineList::iterator melIt1; + calcIteratorFromLineNr( y-1, mlIt1, melIt1 ); + if ( melIt1->isEditableText() ) + { + QString s1 = melIt1->getString( this ); + melIt1->setString( s1 + str ); + + // Remove the previous line + if ( mlIt->mergeEditLineList.size()>1 ) + mlIt->mergeEditLineList.erase( melIt ); + else + melIt->setRemoved(); + + --y; + x=str.length(); + } + } + } + else + { + QString s = str.left( x-1 ); + s += str.mid( x ); + --x; + melIt->setString( s ); + setModified(); + } + break; + } + case Qt::Key_Return: + case Qt::Key_Enter: + { + if( !melIt->isEditableText() ) break; + deleteSelection2( str, x, y, mlIt, melIt ); + setModified(); + QString indentation; + if ( m_pOptionDialog->m_bAutoIndentation ) + { // calc last indentation + MergeLineList::iterator mlIt1 = mlIt; + MergeEditLineList::iterator melIt1 = melIt; + for(;;) { + const QString s = melIt1->getString(this); + if ( !s.isEmpty() ) { + unsigned int i; + for( i=0; imergeEditLineList.begin() ) + --melIt1; + else + { + if ( mlIt1 == m_mergeLineList.begin() ) break; + --mlIt1; + melIt1 = mlIt1->mergeEditLineList.end(); + --melIt1; + } + } + } + MergeEditLine mel(mlIt->id3l); // Associate every mel with an id3l, even if not really valid. + mel.setString( indentation + str.mid(x) ); + + if ( x<(int)str.length() ) // Cut off the old line. + { + // Since ps possibly points into melIt->str, first copy it into a temporary. + QString temp = str.left(x); + melIt->setString( temp ); + } + + ++melIt; + mlIt->mergeEditLineList.insert( melIt, mel ); + x = indentation.length(); + ++y; + break; + } + case Qt::Key_Insert: m_bInsertMode = !m_bInsertMode; break; + case Qt::Key_Pause: break; + case Qt::Key_Print: break; + case Qt::Key_SysReq: break; + case Qt::Key_Home: x=0; if(bCtrl){y=0; } break; // cursor movement + case Qt::Key_End: x=INT_MAX; if(bCtrl){y=INT_MAX;} break; + + case Qt::Key_Left: + case Qt::Key_Right: + if ( (e->key()==Qt::Key_Left) ^ m_pOptionDialog->m_bRightToLeftLanguage ) // operator^: XOR + { + if ( !bCtrl ) + { + --x; + if(x<0 && y>0){--y; x=INT_MAX;} + } + else + { + while( x>0 && (str[x-1]==' ' || str[x-1]=='\t') ) --x; + while( x>0 && (str[x-1]!=' ' && str[x-1]!='\t') ) --x; + } + } + else + { + if ( !bCtrl ) + { + ++x; if(x>(int)str.length() && ytext(); + if( t.isEmpty() || bCtrl ) + { e->ignore(); return; } + else + { + if( bCtrl ) + { + e->ignore(); return; + } + else + { + if( !melIt->isEditableText() ) break; + deleteSelection2( str, x, y, mlIt, melIt ); + + setModified(); + // Characters to insert + QString s=str; + if ( t[0]=='\t' && m_pOptionDialog->m_bReplaceTabs ) + { + int spaces = (m_cursorXPos / m_pOptionDialog->m_tabSize + 1)*m_pOptionDialog->m_tabSize - m_cursorXPos; + t.fill( ' ', spaces ); + } + if ( m_bInsertMode ) + s.insert( x, t ); + else + s.replace( x, t.length(), t ); + + melIt->setString( s ); + x += t.length(); + bShift = false; + } + } + } + } + + y = minMaxLimiter( y, 0, m_totalSize-1 ); + + calcIteratorFromLineNr( y, mlIt, melIt ); + str = melIt->getString( this ); + + x = minMaxLimiter( x, 0, (int)str.length() ); + + int newFirstLine = m_firstLine; + int newFirstColumn = m_firstColumn; + + if ( y m_firstLine + getNofVisibleLines() ) + newFirstLine = y - getNofVisibleLines(); + + if (bYMoveKey) + x=convertToPosInText( str, m_cursorOldXPos, m_pOptionDialog->m_tabSize ); + + int xOnScreen = convertToPosOnScreen( str, x, m_pOptionDialog->m_tabSize ); + if ( xOnScreen m_firstColumn + getNofVisibleColumns() ) + newFirstColumn = xOnScreen - getNofVisibleColumns(); + + if ( bShift ) + { + if (m_selection.firstLine==-1) + m_selection.start( m_cursorYPos, m_cursorXPos ); + + m_selection.end( y, xOnScreen ); + } + else + m_selection.reset(); + + m_cursorYPos = y; + m_cursorXPos = xOnScreen; + if ( ! bYMoveKey ) + m_cursorOldXPos = m_cursorXPos; + + m_bCursorOn = false; + + if ( newFirstLine!=m_firstLine || newFirstColumn!=m_firstColumn ) + { + m_bCursorOn = true; + scroll( newFirstColumn-m_firstColumn, newFirstLine-m_firstLine ); + return; + } + + m_bCursorOn = true; + update(); +} + +void MergeResultWindow::calcIteratorFromLineNr( + int line, + MergeResultWindow::MergeLineList::iterator& mlIt, + MergeResultWindow::MergeEditLineList::iterator& melIt + ) +{ + for( mlIt = m_mergeLineList.begin(); mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + if ( line > ml.mergeEditLineList.size() ) + { + line -= ml.mergeEditLineList.size(); + } + else + { + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) + { + --line; + if (line<0) return; + } + } + } + assert(false); +} + + +QString MergeResultWindow::getSelection() +{ + QString selectionString; + + int line = 0; + MergeLineList::iterator mlIt = m_mergeLineList.begin(); + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + MergeEditLineList::iterator melIt; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) + { + MergeEditLine& mel = *melIt; + + if ( m_selection.lineWithin(line) ) + { + int outPos = 0; + if (mel.isEditableText()) + { + const QString str = mel.getString( this ); + + // Consider tabs + + for( unsigned int i=0; im_tabSize ); + } + + if( m_selection.within( line, outPos ) ) + { + selectionString += str[i]; + } + + outPos += spaces; + } + } + else if ( mel.isConflict() ) + { + selectionString += i18n(""); + } + + if( m_selection.within( line, outPos ) ) + { + #ifdef _WIN32 + selectionString += '\r'; + #endif + selectionString += '\n'; + } + } + + ++line; + } + } + + return selectionString; +} + +bool MergeResultWindow::deleteSelection2( QString& s, int& x, int& y, + MergeLineList::iterator& mlIt, MergeEditLineList::iterator& melIt ) +{ + if (m_selection.firstLine!=-1 && m_selection.bSelectionContainsData ) + { + deleteSelection(); + y = m_cursorYPos; + calcIteratorFromLineNr( y, mlIt, melIt ); + s = melIt->getString( this ); + x = convertToPosInText( s, m_cursorXPos, m_pOptionDialog->m_tabSize ); + return true; + } + return false; +} + +void MergeResultWindow::deleteSelection() +{ + if ( m_selection.firstLine==-1 || !m_selection.bSelectionContainsData ) + { + return; + } + setModified(); + + int line = 0; + MergeLineList::iterator mlItFirst; + MergeEditLineList::iterator melItFirst; + QString firstLineString; + + int firstLine = -1; + int lastLine = -1; + + MergeLineList::iterator mlIt; + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + MergeEditLineList::iterator melIt; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) + { + MergeEditLine& mel = *melIt; + + if ( mel.isEditableText() && m_selection.lineWithin(line) ) + { + if ( firstLine==-1 ) + firstLine = line; + lastLine = line; + } + + ++line; + } + } + + if ( firstLine == -1 ) + { + return; // Nothing to delete. + } + + line = 0; + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + MergeEditLineList::iterator melIt, melIt1; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ) + { + MergeEditLine& mel = *melIt; + melIt1 = melIt; + ++melIt1; + + if ( mel.isEditableText() && m_selection.lineWithin(line) ) + { + QString lineString = mel.getString( this ); + + int firstPosInLine = m_selection.firstPosInLine(line); + int lastPosInLine = m_selection.lastPosInLine(line); + + if ( line==firstLine ) + { + mlItFirst = mlIt; + melItFirst = melIt; + int pos = convertToPosInText( lineString, firstPosInLine, m_pOptionDialog->m_tabSize ); + firstLineString = lineString.left( pos ); + } + + if ( line==lastLine ) + { + // This is the last line in the selection + int pos = convertToPosInText( lineString, lastPosInLine, m_pOptionDialog->m_tabSize ); + firstLineString += lineString.mid( pos ); // rest of line + melItFirst->setString( firstLineString ); + } + + if ( line!=firstLine ) + { + // Remove the line + if ( mlIt->mergeEditLineList.size()>1 ) + mlIt->mergeEditLineList.erase( melIt ); + else + melIt->setRemoved(); + } + } + + ++line; + melIt = melIt1; + } + } + + m_cursorYPos = m_selection.beginLine(); + m_cursorXPos = m_selection.beginPos(); + m_cursorOldXPos = m_cursorXPos; + + m_selection.reset(); +} + +void MergeResultWindow::pasteClipboard( bool bFromSelection ) +{ + if (m_selection.firstLine != -1 ) + deleteSelection(); + + setModified(); + + int y = m_cursorYPos; + MergeLineList::iterator mlIt; + MergeEditLineList::iterator melIt, melItAfter; + calcIteratorFromLineNr( y, mlIt, melIt ); + melItAfter = melIt; + ++melItAfter; + QString str = melIt->getString( this ); + int x = convertToPosInText( str, m_cursorXPos, m_pOptionDialog->m_tabSize ); + + if ( !QApplication::clipboard()->supportsSelection() ) + bFromSelection = false; + + QString clipBoard = QApplication::clipboard()->text( bFromSelection ? QClipboard::Selection : QClipboard::Clipboard ); + + QString currentLine = str.left(x); + QString endOfLine = str.mid(x); + int i; + int len = clipBoard.length(); + for( i=0; isetString( currentLine ); + MergeEditLine mel(mlIt->id3l); // Associate every mel with an id3l, even if not really valid. + melIt = mlIt->mergeEditLineList.insert( melItAfter, mel ); + currentLine = ""; + x=0; + ++y; + } + else + { + currentLine += c; + ++x; + } + } + + currentLine += endOfLine; + melIt->setString( currentLine ); + + m_cursorYPos = y; + m_cursorXPos = convertToPosOnScreen( currentLine, x, m_pOptionDialog->m_tabSize ); + m_cursorOldXPos = m_cursorXPos; + + update(); +} + +void MergeResultWindow::resetSelection() +{ + m_selection.reset(); + update(); +} + +void MergeResultWindow::setModified(bool bModified) +{ + if (bModified != m_bModified) + { + m_bModified = bModified; + emit modifiedChanged(m_bModified); + } +} + +/// Saves and returns true when successful. +bool MergeResultWindow::saveDocument( const QString& fileName, QTextCodec* pEncoding ) +{ + // Are still conflicts somewhere? + if ( getNrOfUnsolvedConflicts()>0 ) + { + KMessageBox::error( this, + i18n("Not all conflicts are solved yet.\n" + "File not saved.\n"), + i18n("Conflicts Left")); + return false; + } + + update(); + + FileAccess file( fileName, true /*bWantToWrite*/ ); + if ( m_pOptionDialog->m_bDmCreateBakFiles && file.exists() ) + { + bool bSuccess = file.createBackup(".orig"); + if ( !bSuccess ) + { + KMessageBox::error( this, file.getStatusText() + i18n("\n\nCreating backup failed. File not saved."), i18n("File Save Error") ); + return false; + } + } + + QByteArray dataArray; + QTextStream textOutStream(dataArray, IO_WriteOnly); + textOutStream.setCodec( pEncoding ); + + int line = 0; + MergeLineList::iterator mlIt = m_mergeLineList.begin(); + for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) + { + MergeLine& ml = *mlIt; + MergeEditLineList::iterator melIt; + for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) + { + MergeEditLine& mel = *melIt; + + if ( mel.isEditableText() ) + { + QString str = mel.getString( this ); + + if (line>0) // Prepend line feed, but not for first line + { + if ( m_pOptionDialog->m_lineEndStyle == eLineEndDos ) + { str.prepend("\r\n"); } + else + { str.prepend("\n"); } + } + + textOutStream << str; + ++line; + } + } + } + bool bSuccess = file.writeFile( dataArray.data(), dataArray.size() ); + if ( ! bSuccess ) + { + KMessageBox::error( this, i18n("Error while writing."), i18n("File Save Error") ); + return false; + } + + setModified( false ); + update(); + + return true; +} + +QString MergeResultWindow::getString( int lineIdx ) +{ + MergeResultWindow::MergeLineList::iterator mlIt; + MergeResultWindow::MergeEditLineList::iterator melIt; + calcIteratorFromLineNr( lineIdx, mlIt, melIt ); + QString s = melIt->getString( this ); + return s; +} + +bool MergeResultWindow::findString( const QString& s, int& d3vLine, int& posInLine, bool bDirDown, bool bCaseSensitive ) +{ + int it = d3vLine; + int endIt = bDirDown ? getNofLines() : -1; + int step = bDirDown ? 1 : -1; + int startPos = posInLine; + + for( ; it!=endIt; it+=step ) + { + QString line = getString( it ); + if ( !line.isEmpty() ) + { + int pos = line.find( s, startPos, bCaseSensitive ); + if ( pos != -1 ) + { + d3vLine = it; + posInLine = pos; + return true; + } + + startPos = 0; + } + } + return false; +} + +void MergeResultWindow::setSelection( int firstLine, int startPos, int lastLine, int endPos ) +{ + if ( lastLine >= getNofLines() ) + { + lastLine = getNofLines()-1; + QString s = getString( lastLine ); + endPos = s.length(); + } + m_selection.reset(); + m_selection.start( firstLine, convertToPosOnScreen( getString(firstLine), startPos, m_pOptionDialog->m_tabSize ) ); + m_selection.end( lastLine, convertToPosOnScreen( getString(lastLine), endPos, m_pOptionDialog->m_tabSize ) ); + update(); +} + +Overview::Overview( QWidget* pParent, OptionDialog* pOptions ) +: QWidget( pParent, 0, WRepaintNoErase ) +{ + m_pDiff3LineList = 0; + m_pOptions = pOptions; + m_bTripleDiff = false; + m_eOverviewMode = eOMNormal; + m_nofLines = 1; + m_bPaintingAllowed = false; + setFixedWidth(20); +} + +void Overview::init( Diff3LineList* pDiff3LineList, bool bTripleDiff ) +{ + m_pDiff3LineList = pDiff3LineList; + m_bTripleDiff = bTripleDiff; + m_pixmap.resize( QSize(0,0) ); // make sure that a redraw happens + update(); +} + +void Overview::reset() +{ + m_pDiff3LineList = 0; +} + +void Overview::slotRedraw() +{ + m_pixmap.resize( QSize(0,0) ); // make sure that a redraw happens + update(); +} + +void Overview::setRange( int firstLine, int pageHeight ) +{ + m_firstLine = firstLine; + m_pageHeight = pageHeight; + update(); +} +void Overview::setFirstLine( int firstLine ) +{ + m_firstLine = firstLine; + update(); +} + +void Overview::setOverviewMode( e_OverviewMode eOverviewMode ) +{ + m_eOverviewMode = eOverviewMode; + slotRedraw(); +} + +Overview::e_OverviewMode Overview::getOverviewMode() +{ + return m_eOverviewMode; +} + +void Overview::mousePressEvent( QMouseEvent* e ) +{ + int h = height()-1; + int h1 = h * m_pageHeight / max2(1,m_nofLines)+3; + if ( h>0 ) + emit setLine( ( e->y() - h1/2 )*m_nofLines/h ); +} + +void Overview::mouseMoveEvent( QMouseEvent* e ) +{ + mousePressEvent(e); +} + +void Overview::setPaintingAllowed( bool bAllowPainting ) +{ + if (m_bPaintingAllowed != bAllowPainting) + { + m_bPaintingAllowed = bAllowPainting; + if ( m_bPaintingAllowed ) update(); + else reset(); + } +} + +void Overview::drawColumn( QPainter& p, e_OverviewMode eOverviewMode, int x, int w, int h, int nofLines ) +{ + p.setPen(Qt::black); + p.drawLine( x, 0, x, h ); + + if (nofLines==0) return; + + int line = 0; + int oldY = 0; + int oldConflictY = -1; + int wrapLineIdx=0; + Diff3LineList::const_iterator i; + for( i = m_pDiff3LineList->begin(); i!= m_pDiff3LineList->end(); ) + { + const Diff3Line& d3l = *i; + int y = h * (line+1) / nofLines; + e_MergeDetails md; + bool bConflict; + bool bLineRemoved; + int src; + mergeOneLine( d3l, md, bConflict, bLineRemoved, src, !m_bTripleDiff ); + + QColor c = m_pOptions->m_bgColor; + bool bWhiteSpaceChange = false; + //if( bConflict ) c=m_pOptions->m_colorForConflict; + //else + if ( eOverviewMode==eOMNormal ) + { + switch( md ) + { + case eDefault: + case eNoChange: + c = m_pOptions->m_bgColor; + break; + + case eBAdded: + case eBDeleted: + case eBChanged: + c = bConflict ? m_pOptions->m_colorForConflict : m_pOptions->m_colorB; + bWhiteSpaceChange = d3l.bAEqB || d3l.bWhiteLineA && d3l.bWhiteLineB; + break; + + case eCAdded: + case eCDeleted: + case eCChanged: + bWhiteSpaceChange = d3l.bAEqC || d3l.bWhiteLineA && d3l.bWhiteLineC; + c = bConflict ? m_pOptions->m_colorForConflict : m_pOptions->m_colorC; + break; + + case eBCChanged: // conflict + case eBCChangedAndEqual: // possible conflict + case eBCDeleted: // possible conflict + case eBChanged_CDeleted: // conflict + case eCChanged_BDeleted: // conflict + case eBCAdded: // conflict + case eBCAddedAndEqual: // possible conflict + c=m_pOptions->m_colorForConflict; + break; + default: assert(false); break; + } + } + else if ( eOverviewMode==eOMAvsB ) + { + switch( md ) + { + case eDefault: + case eNoChange: + case eCAdded: + case eCDeleted: + case eCChanged: break; + default: c = m_pOptions->m_colorForConflict; + bWhiteSpaceChange = d3l.bAEqB || d3l.bWhiteLineA && d3l.bWhiteLineB; + break; + } + } + else if ( eOverviewMode==eOMAvsC ) + { + switch( md ) + { + case eDefault: + case eNoChange: + case eBAdded: + case eBDeleted: + case eBChanged: break; + default: c = m_pOptions->m_colorForConflict; + bWhiteSpaceChange = d3l.bAEqC || d3l.bWhiteLineA && d3l.bWhiteLineC; + break; + } + } + else if ( eOverviewMode==eOMBvsC ) + { + switch( md ) + { + case eDefault: + case eNoChange: + case eBCChangedAndEqual: + case eBCDeleted: + case eBCAddedAndEqual: break; + default: c=m_pOptions->m_colorForConflict; + bWhiteSpaceChange = d3l.bBEqC || d3l.bWhiteLineB && d3l.bWhiteLineC; + break; + } + } + + if (!bWhiteSpaceChange || m_pOptions->m_bShowWhiteSpace ) + { + // Make sure that lines with conflict are not overwritten. + if ( c == m_pOptions->m_colorForConflict ) + { + p.fillRect(x+1, oldY, w, max2(1,y-oldY), bWhiteSpaceChange ? QBrush(c,Qt::Dense4Pattern) : QBrush(c) ); + oldConflictY = oldY; + } + else if ( c!=m_pOptions->m_bgColor && oldY>oldConflictY ) + { + p.fillRect(x+1, oldY, w, max2(1,y-oldY), bWhiteSpaceChange ? QBrush(c,Qt::Dense4Pattern) : QBrush(c) ); + } + } + + oldY = y; + + ++line; + if ( m_pOptions->m_bWordWrap ) + { + ++wrapLineIdx; + if(wrapLineIdx>=d3l.linesNeededForDisplay) + { + wrapLineIdx=0; + ++i; + } + } + else + { + ++i; + } + } +} + +void Overview::paintEvent( QPaintEvent* ) +{ + if (m_pDiff3LineList==0 || !m_bPaintingAllowed ) return; + int h = height()-1; + int w = width(); + + + if ( m_pixmap.size() != size() ) + { + if ( m_pOptions->m_bWordWrap ) + { + m_nofLines = 0; + Diff3LineList::const_iterator i; + for( i = m_pDiff3LineList->begin(); i!= m_pDiff3LineList->end(); ++i ) + { + m_nofLines += i->linesNeededForDisplay; + } + } + else + { + m_nofLines = m_pDiff3LineList->size(); + } + + m_pixmap.resize( size() ); + + QPainter p(&m_pixmap); + p.fillRect( rect(), m_pOptions->m_bgColor ); + + if ( !m_bTripleDiff || m_eOverviewMode == eOMNormal ) + { + drawColumn( p, eOMNormal, 0, w, h, m_nofLines ); + } + else + { + drawColumn( p, eOMNormal, 0, w/2, h, m_nofLines ); + drawColumn( p, m_eOverviewMode, w/2, w/2, h, m_nofLines ); + } + } + + QPainter painter( this ); + painter.drawPixmap( 0,0, m_pixmap ); + + int y1 = h * m_firstLine / m_nofLines-1; + int h1 = h * m_pageHeight / m_nofLines+3; + painter.setPen(Qt::black); + painter.drawRect( 1, y1, w-1, h1 ); +} + +WindowTitleWidget::WindowTitleWidget(OptionDialog* pOptionDialog, QWidget* pParent) +:QWidget(pParent) +{ + m_pOptionDialog = pOptionDialog; + //setAutoFillBackground(true); + + QHBoxLayout* pHLayout = new QHBoxLayout(this); + pHLayout->setMargin(2); + pHLayout->setSpacing(2); + + m_pLabel = new QLabel(i18n("Output")+":", this); + pHLayout->addWidget( m_pLabel ); + + m_pFileNameLineEdit = new QLineEdit(this); + pHLayout->addWidget( m_pFileNameLineEdit, 6 ); + m_pFileNameLineEdit->installEventFilter( this ); + m_pFileNameLineEdit->setReadOnly( true ); + + //m_pBrowseButton = new QPushButton("..."); + //pHLayout->addWidget( m_pBrowseButton, 0 ); + //connect( m_pBrowseButton, SIGNAL(clicked()), this, SLOT(slotBrowseButtonClicked())); + + m_pModifiedLabel = new QLabel(i18n("[Modified]"),this); + pHLayout->addWidget( m_pModifiedLabel ); + m_pModifiedLabel->setMinimumSize( m_pModifiedLabel->sizeHint() ); + m_pModifiedLabel->setText(""); + + pHLayout->addStretch(1); + + m_pEncodingLabel = new QLabel(i18n("Encoding for saving")+":",this); + pHLayout->addWidget( m_pEncodingLabel ); + + m_pEncodingSelector = new QComboBox(this); + pHLayout->addWidget( m_pEncodingSelector, 3 ); + setEncodings(0,0,0); +} + +void WindowTitleWidget::setFileName( const QString& fileName ) +{ + m_pFileNameLineEdit->setText( QDir::convertSeparators(fileName) ); +} + +QString WindowTitleWidget::getFileName() +{ + return m_pFileNameLineEdit->text(); +} + +void WindowTitleWidget::setEncodings( QTextCodec* pCodecForA, QTextCodec* pCodecForB, QTextCodec* pCodecForC ) +{ + m_pEncodingSelector->clear(); + m_codecMap.clear(); + + // First sort codec names: + std::map names; + int i; + for(i=0;;++i) + { + QTextCodec* c = QTextCodec::codecForIndex(i); + if ( c==0 ) break; + else names[QString(c->name())]=c; + } + + i=0; + if ( pCodecForA ) + { + m_pEncodingSelector->insertItem( i18n("Codec from") + " A: " + pCodecForA->name(), i ); + m_codecMap[i]=pCodecForA; + ++i; + } + if ( pCodecForB ) + { + m_pEncodingSelector->insertItem( i18n("Codec from") + " B: " + pCodecForB->name(), i ); + m_codecMap[i]=pCodecForB; + ++i; + } + if ( pCodecForC ) + { + m_pEncodingSelector->insertItem( i18n("Codec from") + " C: " + pCodecForC->name(), i ); + m_codecMap[i]=pCodecForC; + ++i; + } + + std::map::iterator it; + for(it=names.begin();it!=names.end();++it) + { + m_pEncodingSelector->insertItem( it->first, i ); + m_codecMap[i]=it->second; + ++i; + } + m_pEncodingSelector->setMinimumSize( m_pEncodingSelector->sizeHint() ); + + if ( pCodecForC && pCodecForB && pCodecForA ) + { + if ( pCodecForA == pCodecForB ) + m_pEncodingSelector->setCurrentItem( 2 ); // C + else if ( pCodecForA == pCodecForC ) + m_pEncodingSelector->setCurrentItem( 1 ); // B + else + m_pEncodingSelector->setCurrentItem( 2 ); // C + } + else if ( pCodecForA && pCodecForB ) + m_pEncodingSelector->setCurrentItem( 1 ); // B + else + m_pEncodingSelector->setCurrentItem( 0 ); +} + +QTextCodec* WindowTitleWidget::getEncoding() +{ + return m_codecMap[ m_pEncodingSelector->currentItem() ]; +} + +void WindowTitleWidget::setEncoding(QTextCodec* pEncoding) +{ + m_pEncodingSelector->setCurrentText( QString( pEncoding->name() ) ); +} + +//void WindowTitleWidget::slotBrowseButtonClicked() +//{ +// QString current = m_pFileNameLineEdit->text(); +// +// KURL newURL = KFileDialog::getSaveURL( current, 0, this, i18n("Select file (not saving yet)")); +// if ( !newURL.isEmpty() ) +// { +// m_pFileNameLineEdit->setText( newURL.url() ); +// } +//} + +void WindowTitleWidget::slotSetModified( bool bModified ) +{ + m_pModifiedLabel->setText( bModified ? i18n("[Modified]") : "" ); +} + +bool WindowTitleWidget::eventFilter( QObject* o, QEvent* e ) +{ + if ( e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut ) + { + QPalette p = m_pLabel->palette(); + + QColor c1 = m_pOptionDialog->m_fgColor; + QColor c2 = Qt::lightGray; + if ( e->type()==QEvent::FocusOut ) + c2 = m_pOptionDialog->m_bgColor; + + p.setColor(QColorGroup::Background, c2); + setPalette( p ); + + p.setColor(QColorGroup::Foreground, c1); + m_pLabel->setPalette( p ); + m_pEncodingLabel->setPalette( p ); + m_pEncodingSelector->setPalette( p ); + } + if (o == m_pFileNameLineEdit && e->type()==QEvent::Drop) + { + QDropEvent* d = static_cast(e); + + if ( QUriDrag::canDecode( d ) ) + { + QStringList lst; + QUriDrag::decodeLocalFiles( d, lst ); + + if ( lst.count() > 0 ) + { + static_cast(o)->setText( lst[0] ); + static_cast(o)->setFocus(); + return true; + } + } + } + return false; +} + +#include "mergeresultwindow.moc" -- cgit v1.2.1