diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 01:27:27 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 01:27:27 +0000 |
commit | 76718abdb2138623102398a10f3228e576dd0ae8 (patch) | |
tree | ddb098baac9689b9e661a41c2a28a8a23ef246d4 /src/difftextwindow.cpp | |
download | kdiff3-76718abdb2138623102398a10f3228e576dd0ae8.tar.gz kdiff3-76718abdb2138623102398a10f3228e576dd0ae8.zip |
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
Diffstat (limited to 'src/difftextwindow.cpp')
-rw-r--r-- | src/difftextwindow.cpp | 1751 |
1 files changed, 1751 insertions, 0 deletions
diff --git a/src/difftextwindow.cpp b/src/difftextwindow.cpp new file mode 100644 index 0000000..79d55ed --- /dev/null +++ b/src/difftextwindow.cpp @@ -0,0 +1,1751 @@ +/*************************************************************************** + difftextwindow.cpp - description + ------------------- + begin : Mon Apr 8 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 "difftextwindow.h" +#include "merger.h" +#include <qpainter.h> +#include <assert.h> +#include <qpixmap.h> +#include <qstatusbar.h> +#include <qapplication.h> +#include <qtooltip.h> +#include <qfont.h> +#include <qstringlist.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qtextcodec.h> +#include <optiondialog.h> +#include <math.h> +#include <cstdlib> +#include <qdragobject.h> +#include <klocale.h> +#include <kfiledialog.h> + +class DiffTextWindowData +{ +public: + DiffTextWindowData( DiffTextWindow* p ) + { + m_pDiffTextWindow = p; + m_bPaintingAllowed = false; + m_pLineData = 0; + m_size = 0; + m_bWordWrap = false; + m_delayedDrawTimer = 0; + m_pDiff3LineVector = 0; + m_pManualDiffHelpList = 0; + m_pOptionDialog = 0; + m_fastSelectorLine1 = 0; + m_fastSelectorNofLines = 0; + m_bTriple = 0; + m_winIdx = 0; + m_firstLine = 0; + m_oldFirstLine = 0; + m_oldFirstColumn = 0; + m_firstColumn = 0; + m_lineNumberWidth = 0; + m_pStatusBar = 0; + m_scrollDeltaX = 0; + m_scrollDeltaY = 0; + m_bMyUpdate = false; + m_bSelectionInProgress = false; + } + DiffTextWindow* m_pDiffTextWindow; + DiffTextWindowFrame* m_pDiffTextWindowFrame; + + bool m_bPaintingAllowed; + const LineData* m_pLineData; + int m_size; + QString m_filename; + bool m_bWordWrap; + int m_delayedDrawTimer; + + const Diff3LineVector* m_pDiff3LineVector; + Diff3WrapLineVector m_diff3WrapLineVector; + const ManualDiffHelpList* m_pManualDiffHelpList; + + OptionDialog* m_pOptionDialog; + QColor m_cThis; + QColor m_cDiff1; + QColor m_cDiff2; + QColor m_cDiffBoth; + + int m_fastSelectorLine1; + int m_fastSelectorNofLines; + + bool m_bTriple; + int m_winIdx; + int m_firstLine; + int m_oldFirstLine; + int m_oldFirstColumn; + int m_firstColumn; + int m_lineNumberWidth; + + void getLineInfo( + const Diff3Line& d, + int& lineIdx, + DiffList*& pFineDiff1, DiffList*& pFineDiff2, // return values + int& changed, int& changed2 ); + + QString getString( int d3lIdx ); + QString getLineString( int line ); + + void writeLine( + MyPainter& p, const LineData* pld, + const DiffList* pLineDiff1, const DiffList* pLineDiff2, int line, + int whatChanged, int whatChanged2, int srcLineIdx, + int wrapLineOffset, int wrapLineLength, bool bWrapLine, const QRect& invalidRect, int deviceWidth + ); + + void draw( MyPainter& p, const QRect& invalidRect, int deviceWidth, int beginLine, int endLine ); + + QStatusBar* m_pStatusBar; + + Selection m_selection; + + int m_scrollDeltaX; + int m_scrollDeltaY; + + bool m_bMyUpdate; + void myUpdate(int afterMilliSecs ); + + int leftInfoWidth() { return 4+m_lineNumberWidth; } // Nr of information columns on left side + int convertLineOnScreenToLineInSource( int lineOnScreen, e_CoordType coordType, bool bFirstLine ); + + bool m_bSelectionInProgress; + QPoint m_lastKnownMousePos; +}; + +DiffTextWindow::DiffTextWindow( + DiffTextWindowFrame* pParent, + QStatusBar* pStatusBar, + OptionDialog* pOptionDialog, + int winIdx + ) + : QWidget(pParent, 0, Qt::WResizeNoErase | Qt::WRepaintNoErase) +{ + d = new DiffTextWindowData(this); + d->m_pDiffTextWindowFrame = pParent; + setFocusPolicy( ClickFocus ); + setAcceptDrops( true ); + + d->m_pOptionDialog = pOptionDialog; + init( 0, 0, 0, 0, 0, false ); + + setMinimumSize(QSize(20,20)); + + d->m_pStatusBar = pStatusBar; + d->m_bPaintingAllowed = true; + d->m_bWordWrap = false; + d->m_winIdx = winIdx; + + setFont(d->m_pOptionDialog->m_font); +} + +DiffTextWindow::~DiffTextWindow() +{ + delete d; +} + +void DiffTextWindow::init( + const QString& filename, + const LineData* pLineData, + int size, + const Diff3LineVector* pDiff3LineVector, + const ManualDiffHelpList* pManualDiffHelpList, + bool bTriple + ) +{ + d->m_filename = filename; + d->m_pLineData = pLineData; + d->m_size = size; + d->m_pDiff3LineVector = pDiff3LineVector; + d->m_diff3WrapLineVector.clear(); + d->m_pManualDiffHelpList = pManualDiffHelpList; + + d->m_firstLine = 0; + d->m_oldFirstLine = -1; + d->m_firstColumn = 0; + d->m_oldFirstColumn = -1; + d->m_bTriple = bTriple; + d->m_scrollDeltaX=0; + d->m_scrollDeltaY=0; + d->m_bMyUpdate = false; + d->m_fastSelectorLine1 = 0; + d->m_fastSelectorNofLines = 0; + d->m_lineNumberWidth = 0; + d->m_selection.reset(); + d->m_selection.oldFirstLine = -1; // reset is not enough here. + d->m_selection.oldLastLine = -1; + d->m_selection.lastLine = -1; + + update(); + d->m_pDiffTextWindowFrame->init(); +} + +void DiffTextWindow::reset() +{ + d->m_pLineData=0; + d->m_size=0; + d->m_pDiff3LineVector=0; + d->m_filename=""; + d->m_diff3WrapLineVector.clear(); +} + +void DiffTextWindow::setPaintingAllowed( bool bAllowPainting ) +{ + if (d->m_bPaintingAllowed != bAllowPainting) + { + d->m_bPaintingAllowed = bAllowPainting; + if ( d->m_bPaintingAllowed ) update(); + else reset(); + } +} + +void DiffTextWindow::dragEnterEvent( QDragEnterEvent* e ) +{ + e->accept( QUriDrag::canDecode(e) || QTextDrag::canDecode(e) ); + // Note that the corresponding drop is handled in KDiff3App::eventFilter(). +} + + +void DiffTextWindow::setFirstLine(int firstLine) +{ + int fontHeight = fontMetrics().height(); + + int newFirstLine = max2(0,firstLine); + + int deltaY = fontHeight * ( d->m_firstLine - newFirstLine ); + + d->m_firstLine = newFirstLine; + + if ( d->m_bSelectionInProgress && d->m_selection.firstLine != -1 ) + { + int line, pos; + convertToLinePos( d->m_lastKnownMousePos.x(), d->m_lastKnownMousePos.y(), line, pos ); + d->m_selection.end( line, pos ); + update(); + } + else + { + QWidget::scroll( 0, deltaY ); + } + d->m_pDiffTextWindowFrame->setFirstLine( d->m_firstLine ); +} + +int DiffTextWindow::getFirstLine() +{ + return d->m_firstLine; +} + +void DiffTextWindow::setFirstColumn(int firstCol) +{ + int fontWidth = fontMetrics().width('W'); + int xOffset = d->leftInfoWidth() * fontWidth; + + int newFirstColumn = max2(0,firstCol); + + int deltaX = fontWidth * ( d->m_firstColumn - newFirstColumn ); + + d->m_firstColumn = newFirstColumn; + + QRect r( xOffset, 0, width()-xOffset, height() ); + + if ( d->m_pOptionDialog->m_bRightToLeftLanguage ) + { + deltaX = -deltaX; + r = QRect( width()-1-xOffset, 0, -(width()-xOffset), height() ).normalize(); + } + + if ( d->m_bSelectionInProgress && d->m_selection.firstLine != -1 ) + { + int line, pos; + convertToLinePos( d->m_lastKnownMousePos.x(), d->m_lastKnownMousePos.y(), line, pos ); + d->m_selection.end( line, pos ); + update(); + } + else + { + QWidget::scroll( deltaX, 0, r ); + } +} + +int DiffTextWindow::getNofColumns() +{ + if (d->m_bWordWrap) + { + return getNofVisibleColumns(); + } + else + { + int nofColumns = 0; + for( int i = 0; i< d->m_size; ++i ) + { + if ( d->m_pLineData[i].width( d->m_pOptionDialog->m_tabSize ) > nofColumns ) + nofColumns = d->m_pLineData[i].width( d->m_pOptionDialog->m_tabSize ); + } + return nofColumns; + } +} + +int DiffTextWindow::getNofLines() +{ + return d->m_bWordWrap ? d->m_diff3WrapLineVector.size() : + d->m_pDiff3LineVector->size(); +} + + +int DiffTextWindow::convertLineToDiff3LineIdx( int line ) +{ + if ( d->m_bWordWrap && d->m_diff3WrapLineVector.size()>0 ) + return d->m_diff3WrapLineVector[ min2( line, (int)d->m_diff3WrapLineVector.size()-1 ) ].diff3LineIndex; + else + return line; +} + +int DiffTextWindow::convertDiff3LineIdxToLine( int d3lIdx ) +{ + if ( d->m_bWordWrap && d->m_pDiff3LineVector!=0 && d->m_pDiff3LineVector->size()>0 ) + return (*d->m_pDiff3LineVector)[ min2( d3lIdx, (int)d->m_pDiff3LineVector->size()-1 ) ]->sumLinesNeededForDisplay; + else + return d3lIdx; +} + +/** Returns a line number where the linerange [line, line+nofLines] can + be displayed best. If it fits into the currently visible range then + the returned value is the current firstLine. +*/ +int getBestFirstLine( int line, int nofLines, int firstLine, int visibleLines ) +{ + int newFirstLine = firstLine; + if ( line < firstLine || line + nofLines + 2 > firstLine + visibleLines ) + { + if ( nofLines > visibleLines || nofLines <= ( 2*visibleLines / 3 - 1) ) + newFirstLine = line - visibleLines/3; + else + newFirstLine = line - (visibleLines - nofLines); + } + + return newFirstLine; +} + + +void DiffTextWindow::setFastSelectorRange( int line1, int nofLines ) +{ + d->m_fastSelectorLine1 = line1; + d->m_fastSelectorNofLines = nofLines; + if ( isVisible() ) + { + int newFirstLine = getBestFirstLine( + convertDiff3LineIdxToLine(d->m_fastSelectorLine1), + convertDiff3LineIdxToLine(d->m_fastSelectorLine1+d->m_fastSelectorNofLines)-convertDiff3LineIdxToLine(d->m_fastSelectorLine1), + d->m_firstLine, + getNofVisibleLines() + ); + if ( newFirstLine != d->m_firstLine ) + { + scroll( 0, newFirstLine - d->m_firstLine ); + } + + update(); + } +} + + +void DiffTextWindow::showStatusLine(int line ) +{ + int d3lIdx = convertLineToDiff3LineIdx( line ); + if(d3lIdx >= 0 && d3lIdx<(int)d->m_pDiff3LineVector->size() ) + { + const Diff3Line* pD3l = (*d->m_pDiff3LineVector)[d3lIdx]; + if ( pD3l != 0 ) + { + int l = pD3l->getLineInFile( d->m_winIdx ); + + QString s; + if ( l!=-1 ) + s.sprintf("File %s: Line %d", d->m_filename.ascii(), l+1 ); + else + s.sprintf("File %s: Line not available", d->m_filename.ascii() ); + if (d->m_pStatusBar!=0) d->m_pStatusBar->message(s); + + emit lineClicked( d->m_winIdx, l ); + } + } +} + +void DiffTextWindow::focusInEvent(QFocusEvent* e) +{ + emit gotFocus(); + QWidget::focusInEvent(e); +} + +void DiffTextWindow::mousePressEvent ( QMouseEvent* e ) +{ + if ( e->button() == Qt::LeftButton ) + { + int line; + int pos; + convertToLinePos( e->x(), e->y(), line, pos ); + if ( pos < d->m_firstColumn ) + { + emit setFastSelectorLine( convertLineToDiff3LineIdx(line) ); + d->m_selection.firstLine = -1; // Disable current d->m_selection + } + else + { // Selection + resetSelection(); + d->m_selection.start( line, pos ); + d->m_selection.end( line, pos ); + d->m_bSelectionInProgress = true; + d->m_lastKnownMousePos = e->pos(); + + showStatusLine( line ); + } + } +} + +bool isCTokenChar( QChar c ) +{ + return (c=='_') || + ( c>='A' && c<='Z' ) || ( c>='a' && c<='z' ) || + (c>='0' && c<='9'); +} + +/// Calculate where a token starts and ends, given the x-position on screen. +void calcTokenPos( const QString& s, int posOnScreen, int& pos1, int& pos2, int tabSize ) +{ + // Cursor conversions that consider g_tabSize + int pos = convertToPosInText( s, max2( 0, posOnScreen ), tabSize ); + if ( pos>=(int)s.length() ) + { + pos1=s.length(); + pos2=s.length(); + return; + } + + pos1 = pos; + pos2 = pos+1; + + if( isCTokenChar( s[pos1] ) ) + { + while( pos1>=0 && isCTokenChar( s[pos1] ) ) + --pos1; + ++pos1; + + while( pos2<(int)s.length() && isCTokenChar( s[pos2] ) ) + ++pos2; + } +} + +void DiffTextWindow::mouseDoubleClickEvent( QMouseEvent* e ) +{ + d->m_bSelectionInProgress = false; + d->m_lastKnownMousePos = e->pos(); + if ( e->button() == Qt::LeftButton ) + { + int line; + int pos; + convertToLinePos( e->x(), e->y(), line, pos ); + + // Get the string data of the current line + QString s; + if ( d->m_bWordWrap ) + { + if ( line<0 || line >= (int)d->m_diff3WrapLineVector.size() ) + return; + const Diff3WrapLine& d3wl = d->m_diff3WrapLineVector[line]; + s = d->getString( d3wl.diff3LineIndex ).mid( d3wl.wrapLineOffset, d3wl.wrapLineLength ); + } + else + { + if ( line<0 || line >= (int)d->m_pDiff3LineVector->size() ) + return; + s = d->getString( line ); + } + + if ( ! s.isEmpty() ) + { + int pos1, pos2; + calcTokenPos( s, pos, pos1, pos2, d->m_pOptionDialog->m_tabSize ); + + resetSelection(); + d->m_selection.start( line, convertToPosOnScreen( s, pos1, d->m_pOptionDialog->m_tabSize ) ); + d->m_selection.end( line, convertToPosOnScreen( s, pos2, d->m_pOptionDialog->m_tabSize ) ); + update(); + // emit d->m_selectionEnd() happens in the mouseReleaseEvent. + showStatusLine( line ); + } + } +} + +void DiffTextWindow::mouseReleaseEvent ( QMouseEvent* e ) +{ + d->m_bSelectionInProgress = false; + d->m_lastKnownMousePos = e->pos(); + //if ( e->button() == LeftButton ) + { + killTimer(d->m_delayedDrawTimer); + d->m_delayedDrawTimer = 0; + if (d->m_selection.firstLine != -1 ) + { + emit selectionEnd(); + } + } + d->m_scrollDeltaX=0; + d->m_scrollDeltaY=0; +} + +inline int sqr(int x){return x*x;} + +void DiffTextWindow::mouseMoveEvent ( QMouseEvent * e ) +{ + int line; + int pos; + convertToLinePos( e->x(), e->y(), line, pos ); + d->m_lastKnownMousePos = e->pos(); + + if (d->m_selection.firstLine != -1 ) + { + d->m_selection.end( line, pos ); + + showStatusLine( line ); + + // Scroll because mouse moved out of the window + const QFontMetrics& fm = fontMetrics(); + int fontWidth = fm.width('W'); + int deltaX=0; + int deltaY=0; + if ( ! d->m_pOptionDialog->m_bRightToLeftLanguage ) + { + if ( e->x() < d->leftInfoWidth()*fontWidth ) deltaX = -1 - abs(e->x()-d->leftInfoWidth()*fontWidth)/fontWidth; + if ( e->x() > width() ) deltaX = +1 + abs(e->x()-width())/fontWidth; + } + else + { + if ( e->x() > width()-1-d->leftInfoWidth()*fontWidth ) deltaX=+1+ abs(e->x() - (width()-1-d->leftInfoWidth()*fontWidth)) / fontWidth; + if ( e->x() < fontWidth ) deltaX=-1- abs(e->x()-fontWidth)/fontWidth; + } + if ( e->y() < 0 ) deltaY = -1 - sqr( e->y() ) / sqr(fm.height()); + if ( e->y() > height() ) deltaY = +1 + sqr( e->y() - height() ) / sqr(fm.height()); + if ( deltaX != 0 && d->m_scrollDeltaX!=deltaX || deltaY!= 0 && d->m_scrollDeltaY!=deltaY ) + { + d->m_scrollDeltaX = deltaX; + d->m_scrollDeltaY = deltaY; + emit scroll( deltaX, deltaY ); + killTimer( d->m_delayedDrawTimer ); + d->m_delayedDrawTimer = startTimer(50); + } + else + { + d->m_scrollDeltaX = deltaX; + d->m_scrollDeltaY = deltaY; + d->myUpdate(0); + } + } +} + + +void DiffTextWindowData::myUpdate(int afterMilliSecs) +{ + m_pDiffTextWindow->killTimer( m_delayedDrawTimer ); + m_bMyUpdate = true; + m_delayedDrawTimer = m_pDiffTextWindow->startTimer( afterMilliSecs ); +} + +void DiffTextWindow::timerEvent(QTimerEvent*) +{ + killTimer(d->m_delayedDrawTimer); + d->m_delayedDrawTimer = 0; + + if ( d->m_bMyUpdate ) + { + int fontHeight = fontMetrics().height(); + + if ( d->m_selection.oldLastLine != -1 ) + { + int lastLine; + int firstLine; + if ( d->m_selection.oldFirstLine != -1 ) + { + firstLine = min3( d->m_selection.oldFirstLine, d->m_selection.lastLine, d->m_selection.oldLastLine ); + lastLine = max3( d->m_selection.oldFirstLine, d->m_selection.lastLine, d->m_selection.oldLastLine ); + } + else + { + firstLine = min2( d->m_selection.lastLine, d->m_selection.oldLastLine ); + lastLine = max2( d->m_selection.lastLine, d->m_selection.oldLastLine ); + } + int y1 = ( firstLine - d->m_firstLine ) * fontHeight; + int y2 = min2( height(), ( lastLine - d->m_firstLine + 1 ) * fontHeight ); + + if ( y1<height() && y2>0 ) + { + QRect invalidRect = QRect( 0, y1, width(), y2-y1 ); + update( invalidRect ); + } + } + + d->m_bMyUpdate = false; + } + + if ( d->m_scrollDeltaX != 0 || d->m_scrollDeltaY != 0 ) + { + d->m_selection.end( d->m_selection.lastLine + d->m_scrollDeltaY, d->m_selection.lastPos + d->m_scrollDeltaX ); + emit scroll( d->m_scrollDeltaX, d->m_scrollDeltaY ); + killTimer(d->m_delayedDrawTimer); + d->m_delayedDrawTimer = startTimer(50); + } +} + +void DiffTextWindow::resetSelection() +{ + d->m_selection.reset(); + update(); +} + +void DiffTextWindow::convertToLinePos( int x, int y, int& line, int& pos ) +{ + const QFontMetrics& fm = fontMetrics(); + int fontHeight = fm.height(); + int fontWidth = fm.width('W'); + int xOffset = ( d->leftInfoWidth() - d->m_firstColumn ) * fontWidth; + + int yOffset = - d->m_firstLine * fontHeight; + + line = ( y - yOffset ) / fontHeight; + if ( ! d->m_pOptionDialog->m_bRightToLeftLanguage ) + pos = ( x - xOffset ) / fontWidth; + else + pos = ( (width() - 1 - x) - xOffset ) / fontWidth; +} + +int Selection::firstPosInLine(int l) +{ + assert( firstLine != -1 ); + + int l1 = firstLine; + int l2 = lastLine; + int p1 = firstPos; + int p2 = lastPos; + if ( l1>l2 ){ std::swap(l1,l2); std::swap(p1,p2); } + if ( l1==l2 && p1>p2 ){ std::swap(p1,p2); } + + if ( l==l1 ) + return p1; + return 0; +} + +int Selection::lastPosInLine(int l) +{ + assert( firstLine != -1 ); + + int l1 = firstLine; + int l2 = lastLine; + int p1 = firstPos; + int p2 = lastPos; + + if ( l1>l2 ){ std::swap(l1,l2); std::swap(p1,p2); } + if ( l1==l2 && p1>p2 ){ std::swap(p1,p2); } + + if ( l==l2 ) + return p2; + return INT_MAX; +} + +bool Selection::within( int l, int p ) +{ + if ( firstLine == -1 ) return false; + int l1 = firstLine; + int l2 = lastLine; + int p1 = firstPos; + int p2 = lastPos; + if ( l1>l2 ){ std::swap(l1,l2); std::swap(p1,p2); } + if ( l1==l2 && p1>p2 ){ std::swap(p1,p2); } + if( l1 <= l && l <= l2 ) + { + if ( l1==l2 ) + return p>=p1 && p<p2; + if ( l==l1 ) + return p>=p1; + if ( l==l2 ) + return p<p2; + return true; + } + return false; +} + +bool Selection::lineWithin( int l ) +{ + if ( firstLine == -1 ) return false; + int l1 = firstLine; + int l2 = lastLine; + + if ( l1>l2 ){ std::swap(l1,l2); } + + return ( l1 <= l && l <= l2 ); +} + + +void DiffTextWindowData::writeLine( + MyPainter& p, + const LineData* pld, + const DiffList* pLineDiff1, + const DiffList* pLineDiff2, + int line, + int whatChanged, + int whatChanged2, + int srcLineIdx, + int wrapLineOffset, + int wrapLineLength, + bool bWrapLine, + const QRect& invalidRect, + int deviceWidth + ) +{ + QFont normalFont = p.font(); + QFont diffFont = normalFont; + diffFont.setItalic( m_pOptionDialog->m_bItalicForDeltas ); + const QFontMetrics& fm = p.fontMetrics(); + int fontHeight = fm.height(); + int fontAscent = fm.ascent(); + int fontDescent = fm.descent(); + int fontWidth = fm.width('W'); + + int xOffset = (leftInfoWidth() - m_firstColumn)*fontWidth; + int yOffset = (line-m_firstLine) * fontHeight; + + QRect lineRect( 0, yOffset, deviceWidth, fontHeight ); + if ( ! invalidRect.intersects( lineRect ) ) + { + return; + } + + int fastSelectorLine1 = m_pDiffTextWindow->convertDiff3LineIdxToLine(m_fastSelectorLine1); + int fastSelectorLine2 = m_pDiffTextWindow->convertDiff3LineIdxToLine(m_fastSelectorLine1+m_fastSelectorNofLines)-1; + + bool bFastSelectionRange = (line>=fastSelectorLine1 && line<= fastSelectorLine2 ); + QColor bgColor = m_pOptionDialog->m_bgColor; + QColor diffBgColor = m_pOptionDialog->m_diffBgColor; + + if ( bFastSelectionRange ) + { + bgColor = m_pOptionDialog->m_currentRangeBgColor; + diffBgColor = m_pOptionDialog->m_currentRangeDiffBgColor; + } + + if ( yOffset+fontHeight<invalidRect.top() || invalidRect.bottom() < yOffset-fontHeight ) + return; + + int changed = whatChanged; + if ( pLineDiff1 != 0 ) changed |= 1; + if ( pLineDiff2 != 0 ) changed |= 2; + + QColor c = m_pOptionDialog->m_fgColor; + if ( changed == 2 ) { + c = m_cDiff2; + } else if ( changed == 1 ) { + c = m_cDiff1; + } else if ( changed == 3 ) { + c = m_cDiffBoth; + } + + p.fillRect( leftInfoWidth()*fontWidth, yOffset, deviceWidth, fontHeight, bgColor ); + + if (pld!=0) + { + // First calculate the "changed" information for each character. + int i=0; + std::vector<UINT8> charChanged( pld->size ); + if ( pLineDiff1!=0 || pLineDiff2 != 0 ) + { + Merger merger( pLineDiff1, pLineDiff2 ); + while( ! merger.isEndReached() && i<pld->size ) + { + if ( i < pld->size ) + { + charChanged[i] = merger.whatChanged(); + ++i; + } + merger.next(); + } + } + + QString s=" "; + // Convert tabs + int outPos = 0; + + QString lineString( pld->pLine, pld->size ); + int lineLength = m_bWordWrap ? wrapLineOffset+wrapLineLength : lineString.length(); + + for( i=wrapLineOffset; i<lineLength; ++i ) + { + int spaces = 1; + + if ( lineString[i]=='\t' ) + { + spaces = tabber( outPos, m_pOptionDialog->m_tabSize ); + s[0] = ' '; + } + else + { + s[0] = lineString[i]; + } + + QColor c = m_pOptionDialog->m_fgColor; + int cchanged = charChanged[i] | whatChanged; + + if ( cchanged == 2 ) { + c = m_cDiff2; + } else if ( cchanged == 1 ) { + c = m_cDiff1; + } else if ( cchanged == 3 ) { + c = m_cDiffBoth; + } + + if ( c!=m_pOptionDialog->m_fgColor && whatChanged2==0 && !m_pOptionDialog->m_bShowWhiteSpace ) + { + // The user doesn't want to see highlighted white space. + c = m_pOptionDialog->m_fgColor; + } + + QRect outRect( xOffset + fontWidth*outPos, yOffset, fontWidth*spaces, fontHeight ); + if ( m_pOptionDialog->m_bRightToLeftLanguage ) + outRect = QRect( deviceWidth-1-(xOffset + fontWidth*outPos), yOffset, -fontWidth*spaces, fontHeight ).normalize(); + if ( invalidRect.intersects( outRect ) ) + { + if( !m_selection.within( line, outPos ) ) + { + + if( c!=m_pOptionDialog->m_fgColor ) + { + QColor lightc = diffBgColor; + p.fillRect( xOffset + fontWidth*outPos, yOffset, + fontWidth*spaces, fontHeight, lightc ); + p.setFont(diffFont); + } + + p.setPen( c ); + if ( s[0]==' ' && c!=m_pOptionDialog->m_fgColor && charChanged[i]!=0 ) + { + if ( m_pOptionDialog->m_bShowWhiteSpaceCharacters && m_pOptionDialog->m_bShowWhiteSpace) + { + p.fillRect( xOffset + fontWidth*outPos, yOffset+fontAscent, + fontWidth*spaces-1, fontDescent, c ); // QT3 + //fontWidth*spaces-1, fontDescent, c ); // QT4 + } + } + else + { + p.drawText( xOffset + fontWidth*outPos, yOffset + fontAscent, s ); + } + p.setFont(normalFont); + } + else + { + p.fillRect( xOffset + fontWidth*outPos, yOffset, + fontWidth*(spaces), fontHeight, m_pDiffTextWindow->colorGroup().highlight() ); + + p.setPen( m_pDiffTextWindow->colorGroup().highlightedText() ); + p.drawText( xOffset + fontWidth*outPos, yOffset + fontAscent, s ); + + m_selection.bSelectionContainsData = true; + } + } + + outPos += spaces; + } + + if( m_selection.lineWithin( line ) && m_selection.lineWithin( line+1 ) ) + { + p.fillRect( xOffset + fontWidth*outPos, yOffset, + deviceWidth, fontHeight, m_pDiffTextWindow->colorGroup().highlight() ); + } + } + + p.fillRect( 0, yOffset, leftInfoWidth()*fontWidth, fontHeight, m_pOptionDialog->m_bgColor ); + + xOffset = (m_lineNumberWidth+2)*fontWidth; + int xLeft = m_lineNumberWidth*fontWidth; + p.setPen( m_pOptionDialog->m_fgColor ); + if ( pld!=0 ) + { + if ( m_pOptionDialog->m_bShowLineNumbers && !bWrapLine ) + { + QString num; + num.sprintf( "%0*d", m_lineNumberWidth, srcLineIdx+1); + p.drawText( 0, yOffset + fontAscent, num ); + //p.drawLine( xLeft -1, yOffset, xLeft -1, yOffset+fontHeight-1 ); + } + if ( !bWrapLine || wrapLineLength>0 ) + { + p.setPen( QPen( m_pOptionDialog->m_fgColor, 0, bWrapLine ? Qt::DotLine : Qt::SolidLine) ); + p.drawLine( xOffset +1, yOffset, xOffset +1, yOffset+fontHeight-1 ); + p.setPen( QPen( m_pOptionDialog->m_fgColor, 0, Qt::SolidLine) ); + } + } + if ( c!=m_pOptionDialog->m_fgColor && whatChanged2==0 )//&& whatChanged==0 ) + { + if ( m_pOptionDialog->m_bShowWhiteSpace ) + { + p.setBrushOrigin(0,0); + p.fillRect( xLeft, yOffset, fontWidth*2-1, fontHeight, QBrush(c,Qt::Dense5Pattern) ); + } + } + else + { + p.fillRect( xLeft, yOffset, fontWidth*2-1, fontHeight, c==m_pOptionDialog->m_fgColor ? bgColor : c ); + } + + if ( bFastSelectionRange ) + { + p.fillRect( xOffset + fontWidth-1, yOffset, 3, fontHeight, m_pOptionDialog->m_fgColor ); + } + + // Check if line needs a manual diff help mark + ManualDiffHelpList::const_iterator ci; + for( ci = m_pManualDiffHelpList->begin(); ci!=m_pManualDiffHelpList->end(); ++ci) + { + const ManualDiffHelpEntry& mdhe=*ci; + int rangeLine1 = -1; + int rangeLine2 = -1; + if (m_winIdx==1 ) { rangeLine1 = mdhe.lineA1; rangeLine2= mdhe.lineA2; } + if (m_winIdx==2 ) { rangeLine1 = mdhe.lineB1; rangeLine2= mdhe.lineB2; } + if (m_winIdx==3 ) { rangeLine1 = mdhe.lineC1; rangeLine2= mdhe.lineC2; } + if ( rangeLine1>=0 && rangeLine2>=0 && srcLineIdx >= rangeLine1 && srcLineIdx <= rangeLine2 ) + { + p.fillRect( xOffset - fontWidth, yOffset, fontWidth-1, fontHeight, m_pOptionDialog->m_manualHelpRangeColor ); + break; + } + } +} + +void DiffTextWindow::paintEvent( QPaintEvent* e ) +{ + if ( d->m_pDiff3LineVector==0 || ! d->m_bPaintingAllowed || + ( d->m_diff3WrapLineVector.empty() && d->m_bWordWrap ) ) + return; + + QRect invalidRect = e->rect(); + if ( invalidRect.isEmpty() ) + return; + + bool bOldSelectionContainsData = d->m_selection.bSelectionContainsData; + d->m_selection.bSelectionContainsData = false; + + int endLine = min2( d->m_firstLine + getNofVisibleLines()+2, getNofLines() ); + + //if ( invalidRect.size()==size() ) + { // double buffering, obsolete with Qt4 + QPainter painter(this); // Remove for Qt4 + QPixmap pixmap( invalidRect.size() );// Remove for Qt4 + + MyPainter p( &pixmap, d->m_pOptionDialog->m_bRightToLeftLanguage, width(), fontMetrics().width('W') ); // For Qt4 change pixmap to this + + p.translate( -invalidRect.x(), -invalidRect.y() );// Remove for Qt4 + + p.setFont( font() ); + p.QPainter::fillRect( invalidRect, d->m_pOptionDialog->m_bgColor ); + + d->draw( p, invalidRect, width(), d->m_firstLine, endLine ); + // p.drawLine( m_invalidRect.x(), m_invalidRect.y(), m_invalidRect.right(), m_invalidRect.bottom() ); // For test only + p.end(); + + painter.drawPixmap( invalidRect.x(), invalidRect.y(), pixmap );// Remove for Qt4 + } +// else +// { // no double buffering +// MyPainter p( this, d->m_pOptionDialog->m_bRightToLeftLanguage, width(), fontMetrics().width('W') ); +// p.setFont( font() ); +// p.QPainter::fillRect( invalidRect, d->m_pOptionDialog->m_bgColor ); +// d->draw( p, invalidRect, width(), d->m_firstLine, endLine ); +// } + + + d->m_oldFirstLine = d->m_firstLine; + d->m_oldFirstColumn = d->m_firstColumn; + d->m_selection.oldLastLine = -1; + if ( d->m_selection.oldFirstLine !=-1 ) + d->m_selection.oldFirstLine = -1; + + if( !bOldSelectionContainsData && d->m_selection.bSelectionContainsData ) + emit newSelection(); +} + +void DiffTextWindow::print( MyPainter& p, const QRect&, int firstLine, int nofLinesPerPage ) +{ + if ( d->m_pDiff3LineVector==0 || ! d->m_bPaintingAllowed || + ( d->m_diff3WrapLineVector.empty() && d->m_bWordWrap ) ) + return; + resetSelection(); +// MyPainter p( this, d->m_pOptionDialog->m_bRightToLeftLanguage, width(), fontMetrics().width('W') ); + int oldFirstLine = d->m_firstLine; + d->m_firstLine = firstLine; + QRect invalidRect = QRect(0,0,QCOORD_MAX,QCOORD_MAX); + QColor bgColor = d->m_pOptionDialog->m_bgColor; + d->m_pOptionDialog->m_bgColor = Qt::white; + d->draw( p, invalidRect, p.window().width(), firstLine, min2(firstLine+nofLinesPerPage,getNofLines()) ); + d->m_pOptionDialog->m_bgColor = bgColor; + d->m_firstLine = oldFirstLine; +} + +void DiffTextWindowData::draw( MyPainter& p, const QRect& invalidRect, int deviceWidth, int beginLine, int endLine ) +{ + m_lineNumberWidth = m_pOptionDialog->m_bShowLineNumbers ? (int)log10((double)m_size)+1 : 0; + + if ( m_winIdx==1 ) + { + m_cThis = m_pOptionDialog->m_colorA; + m_cDiff1 = m_pOptionDialog->m_colorB; + m_cDiff2 = m_pOptionDialog->m_colorC; + } + if ( m_winIdx==2 ) + { + m_cThis = m_pOptionDialog->m_colorB; + m_cDiff1 = m_pOptionDialog->m_colorC; + m_cDiff2 = m_pOptionDialog->m_colorA; + } + if ( m_winIdx==3 ) + { + m_cThis = m_pOptionDialog->m_colorC; + m_cDiff1 = m_pOptionDialog->m_colorA; + m_cDiff2 = m_pOptionDialog->m_colorB; + } + m_cDiffBoth = m_pOptionDialog->m_colorForConflict; // Conflict color + + p.setPen( m_cThis ); + + for ( int line = beginLine; line<endLine; ++line ) + { + int wrapLineOffset=0; + int wrapLineLength=0; + const Diff3Line* d3l =0; + bool bWrapLine = false; + if (m_bWordWrap) + { + Diff3WrapLine& d3wl = m_diff3WrapLineVector[line]; + wrapLineOffset = d3wl.wrapLineOffset; + wrapLineLength = d3wl.wrapLineLength; + d3l = d3wl.pD3L; + bWrapLine = line > 0 && m_diff3WrapLineVector[line-1].pD3L == d3l; + } + else + { + d3l = (*m_pDiff3LineVector)[line]; + } + DiffList* pFineDiff1; + DiffList* pFineDiff2; + int changed=0; + int changed2=0; + + int srcLineIdx=-1; + getLineInfo( *d3l, srcLineIdx, pFineDiff1, pFineDiff2, changed, changed2 ); + + writeLine( + p, // QPainter + srcLineIdx == -1 ? 0 : &m_pLineData[srcLineIdx], // Text in this line + pFineDiff1, + pFineDiff2, + line, // Line on the screen + changed, + changed2, + srcLineIdx, + wrapLineOffset, + wrapLineLength, + bWrapLine, + invalidRect, + deviceWidth + ); + } +} + +QString DiffTextWindowData::getString( int d3lIdx ) +{ + if ( d3lIdx<0 || d3lIdx>=(int)m_pDiff3LineVector->size() ) + return QString(); + const Diff3Line* d3l = (*m_pDiff3LineVector)[d3lIdx]; + DiffList* pFineDiff1; + DiffList* pFineDiff2; + int changed=0; + int changed2=0; + int lineIdx; + getLineInfo( *d3l, lineIdx, pFineDiff1, pFineDiff2, changed, changed2 ); + + if (lineIdx==-1) return QString(); + else + { + const LineData* ld = &m_pLineData[lineIdx]; + return QString( ld->pLine, ld->size ); + } + return QString(); +} + +QString DiffTextWindowData::getLineString( int line ) +{ + if ( m_bWordWrap ) + { + int d3LIdx = m_pDiffTextWindow->convertLineToDiff3LineIdx(line); + return getString( d3LIdx ).mid( m_diff3WrapLineVector[line].wrapLineOffset, m_diff3WrapLineVector[line].wrapLineLength ); + } + else + { + return getString( line ); + } +} + +void DiffTextWindowData::getLineInfo( + const Diff3Line& d3l, + int& lineIdx, + DiffList*& pFineDiff1, DiffList*& pFineDiff2, // return values + int& changed, int& changed2 + ) +{ + changed=0; + changed2=0; + bool bAEqB = d3l.bAEqB || ( d3l.bWhiteLineA && d3l.bWhiteLineB ); + bool bAEqC = d3l.bAEqC || ( d3l.bWhiteLineA && d3l.bWhiteLineC ); + bool bBEqC = d3l.bBEqC || ( d3l.bWhiteLineB && d3l.bWhiteLineC ); + if ( m_winIdx == 1 ) { + lineIdx=d3l.lineA; + pFineDiff1=d3l.pFineAB; + pFineDiff2=d3l.pFineCA; + changed |= ((d3l.lineB==-1)!=(lineIdx==-1) ? 1 : 0) + + ((d3l.lineC==-1)!=(lineIdx==-1) && m_bTriple ? 2 : 0); + changed2 |= ( bAEqB ? 0 : 1 ) + (bAEqC || !m_bTriple ? 0 : 2); + } + else if ( m_winIdx == 2 ) { + lineIdx=d3l.lineB; + pFineDiff1=d3l.pFineBC; + pFineDiff2=d3l.pFineAB; + changed |= ((d3l.lineC==-1)!=(lineIdx==-1) && m_bTriple ? 1 : 0) + + ((d3l.lineA==-1)!=(lineIdx==-1) ? 2 : 0); + changed2 |= ( bBEqC || !m_bTriple ? 0 : 1 ) + (bAEqB ? 0 : 2); + } + else if ( m_winIdx == 3 ) { + lineIdx=d3l.lineC; + pFineDiff1=d3l.pFineCA; + pFineDiff2=d3l.pFineBC; + changed |= ((d3l.lineA==-1)!=(lineIdx==-1) ? 1 : 0) + + ((d3l.lineB==-1)!=(lineIdx==-1) ? 2 : 0); + changed2 |= ( bAEqC ? 0 : 1 ) + (bBEqC ? 0 : 2); + } + else assert(false); +} + + + +void DiffTextWindow::resizeEvent( QResizeEvent* e ) +{ + QSize s = e->size(); + QFontMetrics fm = fontMetrics(); + int visibleLines = s.height()/fm.height()-2; + int visibleColumns = s.width()/fm.width('W') - d->leftInfoWidth(); + emit resizeSignal( visibleColumns, visibleLines ); + QWidget::resizeEvent(e); +} + +int DiffTextWindow::getNofVisibleLines() +{ + QFontMetrics fm = fontMetrics(); + int fmh = fm.height(); + int h = height(); + return h/fmh -1;//height()/fm.height()-2; +} + +int DiffTextWindow::getNofVisibleColumns() +{ + QFontMetrics fm = fontMetrics(); + return width()/fm.width('W') - d->leftInfoWidth(); +} + +QString DiffTextWindow::getSelection() +{ + QString selectionString; + + int line=0; + int lineIdx=0; + + int it; + int vectorSize = d->m_bWordWrap ? d->m_diff3WrapLineVector.size() : d->m_pDiff3LineVector->size(); + for( it=0; it<vectorSize; ++it ) + { + const Diff3Line* d3l = d->m_bWordWrap ? d->m_diff3WrapLineVector[it].pD3L : (*d->m_pDiff3LineVector)[it]; + if ( d->m_winIdx == 1 ) { lineIdx=d3l->lineA; } + else if ( d->m_winIdx == 2 ) { lineIdx=d3l->lineB; } + else if ( d->m_winIdx == 3 ) { lineIdx=d3l->lineC; } + else assert(false); + + if( lineIdx != -1 ) + { + const QChar* pLine = d->m_pLineData[lineIdx].pLine; + int size = d->m_pLineData[lineIdx].size; + QString lineString = QString( pLine, size ); + + if ( d->m_bWordWrap ) + { + size = d->m_diff3WrapLineVector[it].wrapLineLength; + lineString = lineString.mid( d->m_diff3WrapLineVector[it].wrapLineOffset, size ); + } + + // Consider tabs + int outPos = 0; + for( int i=0; i<size; ++i ) + { + int spaces = 1; + if ( lineString[i]=='\t' ) + { + spaces = tabber( outPos, d->m_pOptionDialog->m_tabSize ); + } + + if( d->m_selection.within( line, outPos ) ) + { + selectionString += lineString[i]; + } + + outPos += spaces; + } + + if( d->m_selection.within( line, outPos ) && + !( d->m_bWordWrap && it+1<vectorSize && d3l == d->m_diff3WrapLineVector[it+1].pD3L ) + ) + { + #ifdef _WIN32 + selectionString += '\r'; + #endif + selectionString += '\n'; + } + } + + ++line; + } + + return selectionString; +} + +bool DiffTextWindow::findString( const QString& s, int& d3vLine, int& posInLine, bool bDirDown, bool bCaseSensitive ) +{ + int it = d3vLine; + int endIt = bDirDown ? (int)d->m_pDiff3LineVector->size() : -1; + int step = bDirDown ? 1 : -1; + int startPos = posInLine; + + for( ; it!=endIt; it+=step ) + { + QString line = d->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 DiffTextWindow::convertD3LCoordsToLineCoords( int d3LIdx, int d3LPos, int& line, int& pos ) +{ + if( d->m_bWordWrap ) + { + int wrapPos = d3LPos; + int wrapLine = convertDiff3LineIdxToLine(d3LIdx); + while ( wrapPos > d->m_diff3WrapLineVector[wrapLine].wrapLineLength ) + { + wrapPos -= d->m_diff3WrapLineVector[wrapLine].wrapLineLength; + ++wrapLine; + } + pos = wrapPos; + line = wrapLine; + } + else + { + pos = d3LPos; + line = d3LIdx; + } +} + +void DiffTextWindow::convertLineCoordsToD3LCoords( int line, int pos, int& d3LIdx, int& d3LPos ) +{ + if( d->m_bWordWrap ) + { + d3LPos = pos; + d3LIdx = convertLineToDiff3LineIdx( line ); + int wrapLine = convertDiff3LineIdxToLine(d3LIdx); // First wrap line belonging to this d3LIdx + while ( wrapLine < line ) + { + d3LPos += d->m_diff3WrapLineVector[wrapLine].wrapLineLength; + ++wrapLine; + } + } + else + { + d3LPos = pos; + d3LIdx = line; + } +} + + +void DiffTextWindow::setSelection( int firstLine, int startPos, int lastLine, int endPos, int& l, int& p ) +{ + d->m_selection.reset(); + if ( lastLine >= getNofLines() ) + { + lastLine = getNofLines()-1; + + const Diff3Line* d3l = (*d->m_pDiff3LineVector)[convertLineToDiff3LineIdx(lastLine)]; + int line = -1; + if ( d->m_winIdx==1 ) line = d3l->lineA; + if ( d->m_winIdx==2 ) line = d3l->lineB; + if ( d->m_winIdx==3 ) line = d3l->lineC; + if (line>=0) + endPos = d->m_pLineData[line].width( d->m_pOptionDialog->m_tabSize); + } + + if ( d->m_bWordWrap && d->m_pDiff3LineVector!=0 ) + { + QString s1 = d->getString(firstLine); + int firstWrapLine = convertDiff3LineIdxToLine(firstLine); + int wrapStartPos = startPos; + while ( wrapStartPos > d->m_diff3WrapLineVector[firstWrapLine].wrapLineLength ) + { + wrapStartPos -= d->m_diff3WrapLineVector[firstWrapLine].wrapLineLength; + s1 = s1.mid(d->m_diff3WrapLineVector[firstWrapLine].wrapLineLength); + ++firstWrapLine; + } + + QString s2 = d->getString(lastLine); + int lastWrapLine = convertDiff3LineIdxToLine(lastLine); + int wrapEndPos = endPos; + while ( wrapEndPos > d->m_diff3WrapLineVector[lastWrapLine].wrapLineLength ) + { + wrapEndPos -= d->m_diff3WrapLineVector[lastWrapLine].wrapLineLength; + s2 = s2.mid(d->m_diff3WrapLineVector[lastWrapLine].wrapLineLength); + ++lastWrapLine; + } + + d->m_selection.start( firstWrapLine, convertToPosOnScreen( s1, wrapStartPos, d->m_pOptionDialog->m_tabSize ) ); + d->m_selection.end( lastWrapLine, convertToPosOnScreen( s2, wrapEndPos, d->m_pOptionDialog->m_tabSize ) ); + l=firstWrapLine; + p=wrapStartPos; + } + else + { + d->m_selection.start( firstLine, convertToPosOnScreen( d->getString(firstLine), startPos, d->m_pOptionDialog->m_tabSize ) ); + d->m_selection.end( lastLine, convertToPosOnScreen( d->getString(lastLine), endPos, d->m_pOptionDialog->m_tabSize ) ); + l=firstLine; + p=startPos; + } + update(); +} + +int DiffTextWindowData::convertLineOnScreenToLineInSource( int lineOnScreen, e_CoordType coordType, bool bFirstLine ) +{ + int line=-1; + if (lineOnScreen>=0) + { + if (coordType==eWrapCoords) return lineOnScreen; + int d3lIdx = m_pDiffTextWindow->convertLineToDiff3LineIdx( lineOnScreen ); + if ( !bFirstLine && d3lIdx >= (int)m_pDiff3LineVector->size() ) + d3lIdx = m_pDiff3LineVector->size()-1; + if (coordType==eD3LLineCoords) return d3lIdx; + while ( line<0 && d3lIdx<(int)m_pDiff3LineVector->size() && d3lIdx>=0 ) + { + const Diff3Line* d3l = (*m_pDiff3LineVector)[d3lIdx]; + if ( m_winIdx==1 ) line = d3l->lineA; + if ( m_winIdx==2 ) line = d3l->lineB; + if ( m_winIdx==3 ) line = d3l->lineC; + if ( bFirstLine ) + ++d3lIdx; + else + --d3lIdx; + } + if (coordType==eFileCoords) return line; + } + return line; +} + + +void DiffTextWindow::getSelectionRange( int* pFirstLine, int* pLastLine, e_CoordType coordType ) +{ + if (pFirstLine) + *pFirstLine = d->convertLineOnScreenToLineInSource( d->m_selection.beginLine(), coordType, true ); + if (pLastLine) + *pLastLine = d->convertLineOnScreenToLineInSource( d->m_selection.endLine(), coordType, false ); +} + +// Returns the number of wrapped lines +// if pWrappedLines != 0 then the stringlist will contain the wrapped lines. +int wordWrap( const QString& origLine, int nofColumns, Diff3WrapLine* pDiff3WrapLine ) +{ + if (nofColumns<=0) + nofColumns = 1; + + int nofNeededLines = 0; + int length = origLine.length(); + + if (length==0) + { + nofNeededLines = 1; + if( pDiff3WrapLine ) + { + pDiff3WrapLine->wrapLineOffset=0; + pDiff3WrapLine->wrapLineLength=0; + } + } + else + { + int pos = 0; + + while ( pos < length ) + { + int wrapPos = pos + nofColumns; + + if ( length-pos <= nofColumns ) + { + wrapPos = length; + } + else + { + int wsPos = max2( origLine.findRev( ' ', wrapPos ), origLine.findRev( '\t', wrapPos ) ); + + if ( wsPos > pos ) + { + // Wrap line at wsPos + wrapPos = wsPos; + } + } + + if ( pDiff3WrapLine ) + { + pDiff3WrapLine->wrapLineOffset = pos; + pDiff3WrapLine->wrapLineLength = wrapPos-pos; + ++pDiff3WrapLine; + } + + pos = wrapPos; + + ++nofNeededLines; + } + } + return nofNeededLines; +} + +void DiffTextWindow::convertSelectionToD3LCoords() +{ + if ( d->m_pDiff3LineVector==0 || ! d->m_bPaintingAllowed || !isVisible() || d->m_selection.isEmpty() ) + { + return; + } + + // convert the d->m_selection to unwrapped coordinates: Later restore to new coords + int firstD3LIdx, firstD3LPos; + QString s = d->getLineString( d->m_selection.beginLine() ); + int firstPosInText = convertToPosInText( s, d->m_selection.beginPos(), d->m_pOptionDialog->m_tabSize ); + convertLineCoordsToD3LCoords( d->m_selection.beginLine(), firstPosInText, firstD3LIdx, firstD3LPos ); + + int lastD3LIdx, lastD3LPos; + s = d->getLineString( d->m_selection.endLine() ); + int lastPosInText = convertToPosInText( s, d->m_selection.endPos(), d->m_pOptionDialog->m_tabSize ); + convertLineCoordsToD3LCoords( d->m_selection.endLine(), lastPosInText, lastD3LIdx, lastD3LPos ); + + //d->m_selection.reset(); + d->m_selection.start( firstD3LIdx, firstD3LPos ); + d->m_selection.end( lastD3LIdx, lastD3LPos ); +} + +void DiffTextWindow::recalcWordWrap( bool bWordWrap, int wrapLineVectorSize, int nofVisibleColumns ) +{ + if ( d->m_pDiff3LineVector==0 || ! d->m_bPaintingAllowed || !isVisible() ) + { + d->m_bWordWrap = bWordWrap; + if (!bWordWrap) d->m_diff3WrapLineVector.resize( 0 ); + return; + } + + d->m_bWordWrap = bWordWrap; + + if ( bWordWrap ) + { + d->m_diff3WrapLineVector.resize( wrapLineVectorSize ); + + if (nofVisibleColumns<0) + nofVisibleColumns = getNofVisibleColumns(); + else + nofVisibleColumns-= d->leftInfoWidth(); + int i; + int wrapLineIdx = 0; + int size = d->m_pDiff3LineVector->size(); + for( i=0; i<size; ++i ) + { + QString s = d->getString( i ); + int linesNeeded = wordWrap( s, nofVisibleColumns, wrapLineVectorSize==0 ? 0 : &d->m_diff3WrapLineVector[wrapLineIdx] ); + Diff3Line& d3l = *(*d->m_pDiff3LineVector)[i]; + if ( d3l.linesNeededForDisplay<linesNeeded ) + { + d3l.linesNeededForDisplay = linesNeeded; + } + + if ( wrapLineVectorSize>0 ) + { + int j; + for( j=0; j<d3l.linesNeededForDisplay; ++j, ++wrapLineIdx ) + { + Diff3WrapLine& d3wl = d->m_diff3WrapLineVector[wrapLineIdx]; + d3wl.diff3LineIndex = i; + d3wl.pD3L = (*d->m_pDiff3LineVector)[i]; + if ( j>=linesNeeded ) + { + d3wl.wrapLineOffset=0; + d3wl.wrapLineLength=0; + } + } + } + } + + if ( wrapLineVectorSize>0 ) + { + d->m_firstLine = min2( d->m_firstLine, wrapLineVectorSize-1 ); + d->m_firstColumn = 0; + d->m_pDiffTextWindowFrame->setFirstLine( d->m_firstLine ); + } + } + else + { + d->m_diff3WrapLineVector.resize( 0 ); + } + + if ( !d->m_selection.isEmpty() && ( !d->m_bWordWrap || wrapLineVectorSize>0 ) ) + { + // Assume unwrapped coordinates + //( Why? ->Conversion to unwrapped coords happened a few lines above in this method. + // Also see KDiff3App::recalcWordWrap() on the role of wrapLineVectorSize) + + // Wrap them now. + + // convert the d->m_selection to unwrapped coordinates. + int firstLine, firstPos; + convertD3LCoordsToLineCoords( d->m_selection.beginLine(), d->m_selection.beginPos(), firstLine, firstPos ); + + int lastLine, lastPos; + convertD3LCoordsToLineCoords( d->m_selection.endLine(), d->m_selection.endPos(), lastLine, lastPos ); + + //d->m_selection.reset(); + d->m_selection.start( firstLine, convertToPosOnScreen( d->getLineString( firstLine ), firstPos, d->m_pOptionDialog->m_tabSize ) ); + d->m_selection.end( lastLine, convertToPosOnScreen( d->getLineString( lastLine ),lastPos, d->m_pOptionDialog->m_tabSize ) ); + } +} + + +class DiffTextWindowFrameData +{ +public: + DiffTextWindow* m_pDiffTextWindow; + QLineEdit* m_pFileSelection; + QPushButton* m_pBrowseButton; + OptionDialog* m_pOptionDialog; + QLabel* m_pLabel; + QLabel* m_pTopLine; + QWidget* m_pTopLineWidget; +}; + +DiffTextWindowFrame::DiffTextWindowFrame( QWidget* pParent, QStatusBar* pStatusBar, OptionDialog* pOptionDialog, int winIdx ) + : QWidget( pParent ) +{ + d = new DiffTextWindowFrameData; + d->m_pOptionDialog = pOptionDialog; + d->m_pTopLineWidget = new QWidget(this); + d->m_pFileSelection = new QLineEdit(d->m_pTopLineWidget); + d->m_pBrowseButton = new QPushButton( "...",d->m_pTopLineWidget ); + d->m_pBrowseButton->setFixedWidth( 30 ); + connect(d->m_pBrowseButton,SIGNAL(clicked()), this, SLOT(slotBrowseButtonClicked())); + connect(d->m_pFileSelection,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed())); + + d->m_pLabel = new QLabel("A:",d->m_pTopLineWidget); + d->m_pTopLine = new QLabel(d->m_pTopLineWidget); + d->m_pDiffTextWindow = 0; + d->m_pDiffTextWindow = new DiffTextWindow( this, pStatusBar, pOptionDialog, winIdx ); + QHBoxLayout* pHL = new QHBoxLayout(d->m_pTopLineWidget); + pHL->setMargin(2); + pHL->setSpacing(2); + + pHL->addWidget( d->m_pLabel, 0 ); + pHL->addWidget( d->m_pFileSelection, 1 ); + pHL->addWidget( d->m_pBrowseButton, 0 ); + pHL->addWidget( d->m_pTopLine, 0 ); + + QVBoxLayout* pVL = new QVBoxLayout( this, 0, 0 ); + pVL->addWidget( d->m_pTopLineWidget, 0 ); + pVL->addWidget( d->m_pDiffTextWindow, 1 ); + + d->m_pDiffTextWindow->installEventFilter( this ); + d->m_pFileSelection->installEventFilter( this ); + d->m_pBrowseButton->installEventFilter( this ); + init(); +} + +DiffTextWindowFrame::~DiffTextWindowFrame() +{ + delete d; +} + +void DiffTextWindowFrame::init() +{ + DiffTextWindow* pDTW = d->m_pDiffTextWindow; + if ( pDTW ) + { + QString s = pDTW->d->m_filename ; + d->m_pFileSelection->setText( QDir::convertSeparators(s) ); + QString winId = pDTW->d->m_winIdx==1 ? + ( pDTW->d->m_bTriple?"A (Base)":"A") : + ( pDTW->d->m_winIdx==2 ? "B" : "C" ); + d->m_pLabel->setText( winId + ":" ); + } +} + +// Search for the first visible line (search loop needed when no line exist for this file.) +int DiffTextWindow::calcTopLineInFile( int firstLine ) +{ + int l=-1; + for ( int i = convertLineToDiff3LineIdx(firstLine); i<(int)d->m_pDiff3LineVector->size(); ++i ) + { + const Diff3Line* d3l = (*d->m_pDiff3LineVector)[i]; + l = d3l->getLineInFile(d->m_winIdx); + if (l!=-1) break; + } + return l; +} + +void DiffTextWindowFrame::setFirstLine( int firstLine ) +{ + DiffTextWindow* pDTW = d->m_pDiffTextWindow; + if ( pDTW && pDTW->d->m_pDiff3LineVector ) + { + QString s= i18n("Top line"); + int lineNumberWidth = (int)log10((double)pDTW->d->m_size)+1; + + int l=pDTW->calcTopLineInFile(firstLine); + + int w = d->m_pTopLine->fontMetrics().width( + s+" "+QString().fill('0',lineNumberWidth)); + d->m_pTopLine->setMinimumWidth( w ); + + if (l==-1) + s = i18n("End"); + else + s += " " + QString::number( l+1 ); + + d->m_pTopLine->setText( s ); + d->m_pTopLine->repaint(); + } +} + +DiffTextWindow* DiffTextWindowFrame::getDiffTextWindow() +{ + return d->m_pDiffTextWindow; +} + +bool DiffTextWindowFrame::eventFilter( QObject* o, QEvent* e ) +{ + DiffTextWindow* pDTW = d->m_pDiffTextWindow; + if ( e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut ) + { + QColor c1 = d->m_pOptionDialog->m_bgColor; + QColor c2 = pDTW->d->m_cThis; + QPalette p = d->m_pTopLineWidget->palette(); + if ( e->type()==QEvent::FocusOut ) + std::swap(c1,c2); + + p.setColor(QColorGroup::Background, c2); + d->m_pTopLineWidget->setPalette( p ); + d->m_pBrowseButton->setPalette( p ); + d->m_pFileSelection->setPalette( p ); + + p.setColor(QColorGroup::Foreground, c1); + d->m_pLabel->setPalette( p ); + d->m_pTopLine->setPalette( p ); + } + if (o == d->m_pFileSelection && 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(); + emit fileNameChanged( lst[0], pDTW->d->m_winIdx ); + return true; + } + } + /* The following lines work for Qt>4.1 but not for 4.0.x*/ + /*if ( d->mimeData()->hasUrls() ) + { + QList<QUrl> lst = d->mimeData()->urls(); + if ( !lst.empty() ) + { + static_cast<QLineEdit*>(o)->setText( lst[0].toLocalFile() ); + static_cast<QLineEdit*>(o)->setFocus(); + emit fileNameChanged( lst[0], pDTW->d->m_winIdx ); + return true; + } + }*/ + } + return false; +} + +void DiffTextWindowFrame::slotReturnPressed() +{ + DiffTextWindow* pDTW = d->m_pDiffTextWindow; + if ( pDTW->d->m_filename != d->m_pFileSelection->text() ) + { + emit fileNameChanged( d->m_pFileSelection->text(), pDTW->d->m_winIdx ); + } +} + +void DiffTextWindowFrame::slotBrowseButtonClicked() +{ + QString current = d->m_pFileSelection->text(); + + KURL newURL = KFileDialog::getOpenURL( current, 0, this); + if ( !newURL.isEmpty() ) + { + DiffTextWindow* pDTW = d->m_pDiffTextWindow; + emit fileNameChanged( newURL.url(), pDTW->d->m_winIdx ); + } +} + +QCString encodeString( const QString& s ) +{ + QTextCodec* c = QTextCodec::codecForLocale(); + if (c!=0) + return c->fromUnicode( s ); + else + return QCString( s.latin1() ); +} + +#include "difftextwindow.moc" |