summaryrefslogtreecommitdiffstats
path: root/src/mergeresultwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mergeresultwindow.cpp')
-rw-r--r--src/mergeresultwindow.cpp3222
1 files changed, 3222 insertions, 0 deletions
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 <qpainter.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qcursor.h>
+#include <qpopupmenu.h>
+#include <qstatusbar.h>
+#include <qregexp.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qlayout.h>
+#include <qtextcodec.h>
+#include <qdragobject.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <iostream>
+
+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; j<ml.srcRangeLength; ++j )
+ {
+ MergeEditLine mel(d3llit);
+ mel.setSource( defaultSelector, false );
+
+ int srcLine = defaultSelector==1 ? d3llit->lineA :
+ 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; j<ml.srcRangeLength; ++j )
+ {
+ MergeEditLine mel(d3llit);
+ mel.setSource( selector, false );
+ ml.mergeEditLineList.push_back(mel);
+
+ ++d3llit;
+ }
+ }
+
+ if ( ! ml.mergeEditLineList.empty() )
+ {
+ // Remove all lines that are empty, because no src lines are there.
+ for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); )
+ {
+ MergeEditLine& mel = *melIt;
+
+ int srcLine = mel.src()==1 ? mel.id3l()->lineA :
+ 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; i<s.length(); ++i )
+ {
+ if (s[i]!=' ' && s[i]!='\t')
+ {
+ for( ; i<s.length(); ++i )
+ {
+ if (s[i]==' ' || s[i]=='\t')
+ {
+ return s.left(i);
+ }
+ }
+ return s; // Very unlikely
+ }
+ }
+ return ""; // Must be an empty string, not a null string.
+}
+
+static void findHistoryRange( const QRegExp& historyStart, bool bThreeFiles, const Diff3LineList* pD3LList,
+ Diff3LineList::const_iterator& iBegin, Diff3LineList::const_iterator& iEnd, int& idxBegin, int& idxEnd )
+{
+ QString historyLead;
+ // Search for start of history
+ for( iBegin = pD3LList->begin(), 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<int> startPosStack;
+ int length = s.length();
+ for( i=0; i<length; ++i )
+ {
+ if ( s[i]=='\\' && i+1<length && ( s[i+1]=='\\' || s[i+1]=='(' || s[i+1]==')' ) )
+ {
+ ++i;
+ continue;
+ }
+ if ( s[i]=='(' )
+ {
+ startPosStack.push_back(i);
+ }
+ else if ( s[i]==')' )
+ {
+ if (startPosStack.empty())
+ return false; // Parentheses don't match
+ int startPos = startPosStack.back();
+ startPosStack.pop_back();
+ sl.push_back( s.mid( startPos+1, i-startPos-1 ) );
+ }
+ }
+ return startPosStack.empty(); // false if parentheses don't match
+}
+
+QString calcHistorySortKey( const QString& keyOrder, QRegExp& matchedRegExpr, const QStringList& parenthesesGroupList )
+{
+ QStringList keyOrderList = QStringList::split(',', keyOrder );
+ QString key;
+ for ( QStringList::iterator keyIt = keyOrderList.begin(); keyIt!=keyOrderList.end(); ++keyIt )
+ {
+ if ( (*keyIt).isEmpty() )
+ continue;
+ bool bOk=false;
+ int groupIdx = (*keyIt).toInt(&bOk);
+ if (!bOk || groupIdx<0 || groupIdx >(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<HistoryMap::iterator, bool> 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<HistoryMap::iterator, bool> 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<size; ++i )
+ {
+ if ( localPosOnScreen>=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<posInText; ++i )
+ {
+ // All letters except tabulator have width one.
+ int letterWidth = p[i]!='\t' ? 1 : tabber( posOnScreen, tabSize );
+
+ posOnScreen += letterWidth;
+ }
+ return posOnScreen;
+}
+
+void MergeResultWindow::writeLine(
+ MyPainter& p, int line, const QString& str,
+ int srcSelect, e_MergeDetails mergeDetails, int rangeMark, bool bUserModified, bool bLineRemoved, bool bWhiteSpaceConflict
+ )
+{
+ const QFontMetrics& fm = fontMetrics();
+ int fontHeight = fm.height();
+ int fontWidth = fm.width("W");
+ int fontAscent = fm.ascent();
+
+ int topLineYOffset = 0;
+ int xOffset = fontWidth * leftInfoWidth;
+
+ int yOffset = ( line-m_firstLine ) * fontHeight;
+ if ( yOffset < 0 || yOffset > 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; i<size; ++i )
+ {
+ int spaces = 1;
+ if ( str[i]=='\t' )
+ {
+ spaces = tabber( outPos, m_pOptionDialog->m_tabSize );
+ for( int j=0; j<spaces; ++j )
+ s+=' ';
+ }
+ else
+ {
+ s+=str[i];
+ }
+ outPos += spaces;
+ }
+
+ if ( m_selection.lineWithin( line ) )
+ {
+ int firstPosInLine = convertToPosOnScreen( str, convertToPosInText( str, m_selection.firstPosInLine(line), m_pOptionDialog->m_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("<No src line>") );
+ 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("<Merge Conflict (Whitespace only)>") );
+ else
+ p.drawText( xOffset, yOffset+fontAscent, i18n("<Merge Conflict>") );
+ 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 ( y<m_totalSize-1 )
+ {
+ setModified();
+ MergeLineList::iterator mlIt1;
+ MergeEditLineList::iterator melIt1;
+ calcIteratorFromLineNr( y+1, mlIt1, melIt1 );
+ if ( melIt1->isEditableText() )
+ {
+ 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; i<s.length(); ++i ){ if(s[i]!=' ' && s[i]!='\t') break; }
+ if (i<s.length()) {
+ indentation = s.left(i);
+ break;
+ }
+ }
+ // Go back one line
+ if ( melIt1 != mlIt1->mergeEditLineList.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() && y<m_totalSize-1){ ++y; x=0; }
+ }
+
+ else
+ {
+ while( x<(int)str.length() && (str[x]==' ' || str[x]=='\t') ) ++x;
+ while( x<(int)str.length() && (str[x]!=' ' && str[x]!='\t') ) ++x;
+ }
+ }
+ break;
+
+ case Qt::Key_Up: if (!bCtrl){ --y; bYMoveKey=true; } break;
+ case Qt::Key_Down: if (!bCtrl){ ++y; bYMoveKey=true; } break;
+ case Qt::Key_PageUp: if (!bCtrl){ y-=getNofVisibleLines(); bYMoveKey=true; } break;
+ case Qt::Key_PageDown: if (!bCtrl){ y+=getNofVisibleLines(); bYMoveKey=true; } break;
+ default:
+ {
+ QString t = e->text();
+ 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 )
+ newFirstLine = y;
+ else 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 )
+ newFirstColumn = xOnScreen;
+ else 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; i<str.length(); ++i )
+ {
+ int spaces = 1;
+ if ( str[i]=='\t' )
+ {
+ spaces = tabber( outPos, m_pOptionDialog->m_tabSize );
+ }
+
+ if( m_selection.within( line, outPos ) )
+ {
+ selectionString += str[i];
+ }
+
+ outPos += spaces;
+ }
+ }
+ else if ( mel.isConflict() )
+ {
+ selectionString += i18n("<Merge Conflict>");
+ }
+
+ 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; i<len; ++i )
+ {
+ QChar c = clipBoard[i];
+ if ( c == '\r' ) continue;
+ if ( c == '\n' )
+ {
+ melIt->setString( 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<QString, QTextCodec*> 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<QString, QTextCodec*>::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<QDropEvent*>(e);
+
+ if ( QUriDrag::canDecode( d ) )
+ {
+ QStringList lst;
+ QUriDrag::decodeLocalFiles( d, lst );
+
+ if ( lst.count() > 0 )
+ {
+ static_cast<QLineEdit*>(o)->setText( lst[0] );
+ static_cast<QLineEdit*>(o)->setFocus();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#include "mergeresultwindow.moc"