/****************************************************************************
**
** Implementation of crossvc' diff tool.
**
** Based on code created by Bernd Gehrmann
** Copyright (C) 1999 Bernd Gehrmann
** bernd@physik.hu-berlin.de
**
**
**
** Copyright (C) 2000-2006 Frank Hemer <frank@hemer.org>,
** Tilo Riemer <riemer@crossvc.com>,
** Wim Delvaux <wim.delvaux@chello.be>,
** Jose Hernandez <joseh@tesco.net>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt
** CrossVC is released under the terms of the
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification,
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
** different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
** products derived from this software without specific prior written
** permission.
** 4. The source code is the creative property of the authors.
** Extensions and development under the terms of the Gnu Public License
** are limited to the Unix platform. Any distribution or compilation of
** the source code against libraries licensed other than gpl requires
** the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** 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, version 2 of the License.
** This program is distributed in
** the hope that it will be useful, but WITHOUT ANY WARRANTY; without
** even the implied warranty of MERCHANTABILITY or FITNESS FOR A
** PARTICULAR PURPOSE.
**
** See the GNU General Public License version 2 for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/
#include "config.h"
#include <iostream>
#include <stdlib.h>
#include <ctype.h>
#include <qpainter.h>
#include <qscrollbar.h>
#include <qstyle.h>
#include <qregexp.h>
#include <qapplication.h>
#include "diffview.h"
class DiffViewItem
{
public:
DiffView::DiffInfoList diffInfoList;
DiffView::DiffType type;
bool inverted;
int no;
int pixelwidth;
};
class DiffViewItemList : public QPtrList<DiffViewItem>
{
protected:
virtual int compareItems(QCollection::Item item1, QCollection::Item item2);
};
int DiffViewItemList::compareItems(QCollection::Item item1, QCollection::Item item2)
{
return (static_cast<DiffViewItem*>(item1)->no
== static_cast<DiffViewItem*>(item2)->no)? 0 : 1;
}
static const int BORDER = 7;
DiffView::DiffView(QWidget *parent /*=0*/, const char *name /*=0*/, WFlags f /*=0*/)
: QtTableView(parent, name, f), linenos(false), marker(false), m_blockScrollPos(false)
{
adjustLeftMargin();
setFocusPolicy(QWidget::WheelFocus);
setNumCols(1 + (linenos ? 1 : 0) + (marker ? 1 : 0));
setNumRows(0);
setTableFlags(Tbl_autoVScrollBar | Tbl_autoHScrollBar |
Tbl_smoothVScrolling);
setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
setBackgroundMode(PaletteBase);
setWFlags(WResizeNoErase);
setCellWidth(0);
adjustCellHeight();
items = new DiffViewItemList;
TableSize = 0;
items->setAutoDelete(true);
m_ScrollBarMinPos = -1;
}
void DiffView::setFont(const QFont &f)
{
QtTableView::setFont(f);
adjustLeftMargin();
adjustCellHeight();
}
void DiffView::adjustLeftMargin()
{
QFontMetrics fm(font());
m_markerTypeWidth = QMAX(QMAX(fm.width("-"), fm.width("+")),
fm.width("|"))+2*BORDER;
m_lineNbTypeWidth = fm.width("99999");
}
void DiffView::adjustCellHeight()
{
QFontMetrics fm(font());
setCellHeight(fm.lineSpacing());
}
void DiffView::setLineNumbers(bool On)
{
linenos = On;
setNumCols(1 + (linenos ? 1 : 0) + (marker ? 1 : 0));
}
void DiffView::setMarkers(bool On)
{
marker = On;
setNumCols(1 + (linenos ? 1 : 0) + (marker ? 1 : 0));
}
DiffView::~DiffView()
{
delete items;
}
void DiffView::setPartner(DiffView *other)
{
partner = other;
if (partner)
{
connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
SLOT(yPositionChanged(int)) );
connect( verticalScrollBar(), SIGNAL(sliderMoved(int)),
SLOT(yPositionChanged(int)) );
connect( horizontalScrollBar(), SIGNAL(valueChanged(int)),
SLOT(xPositionChanged(int)) );
connect( horizontalScrollBar(), SIGNAL(sliderMoved(int)),
SLOT(xPositionChanged(int)) );
}
}
void DiffView::wheelEvent( QWheelEvent *e) {
if (viewRect().contains(e->pos())) {
int d = e->delta();
int v = verticalScrollBar()->value();
int l = verticalScrollBar()->lineStep();
l *= QApplication::wheelScrollLines();
if (d > 0) {
d = v - l;
} else {
d = v + l;
}
verticalScrollBar()->setValue(d);
e->accept();
} else {
e->ignore();
}
}
void DiffView::keyPressEvent ( QKeyEvent * e) {
switch (e->key()) {
case Qt::Key_Up: {
int v = verticalScrollBar()->value();
v -= verticalScrollBar()->lineStep();
verticalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_Down: {
int v = verticalScrollBar()->value();
v += verticalScrollBar()->lineStep();
verticalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_Left: {
int v = horizontalScrollBar()->value();
v -= horizontalScrollBar()->lineStep();
horizontalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_Right: {
int v = horizontalScrollBar()->value();
v += horizontalScrollBar()->lineStep();
horizontalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_Prior: {
int v = verticalScrollBar()->value();
v -= verticalScrollBar()->pageStep();
verticalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_Next: {
int v = verticalScrollBar()->value();
v += verticalScrollBar()->pageStep();
verticalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_Home: {
int v = verticalScrollBar()->minValue();
verticalScrollBar()->setValue(v);
e->accept();
break;
}
case Qt::Key_End: {
int v = verticalScrollBar()->maxValue();
verticalScrollBar()->setValue(v);
e->accept();
break;
}
default: {
break;
}
}
}
void DiffView::yPositionChanged(int val)
{
if (partner)
partner->setYOffset(val);
}
void DiffView::xPositionChanged(int val)
{
if ( !m_blockScrollPos && partner && partner->horizontalScrollBar() && partner->horizontalScrollBar()->isVisible() ) {
int w = horizontalScrollBar()->maxValue() - horizontalScrollBar()->minValue();
int wp = partner->horizontalScrollBar()->maxValue() - partner->horizontalScrollBar()->minValue();
int value = (int)((double)wp*(double)val/(double)w);
partner->m_blockScrollPos = true;
partner->horizontalScrollBar()->setValue(value);
partner->m_blockScrollPos = false;
}
}
void DiffView::recalcTableSize( DiffViewItem * NewItem ) {
int maxline = 0;
if( NewItem ) {
// just update
maxline = NewItem->pixelwidth;
} else {
DiffViewItem * it;
// find largest line
for( unsigned int i = 0 ; i < items->count(); i ++ ) {
it = items->at(i);
if( it->pixelwidth > maxline )
maxline = it->pixelwidth;
}
}
if( maxline >= TableSize ) {
TableSize = maxline;
updateTableSize();
}
}
void DiffView::setCenterOffset(int offset)
{
if (!rowIsVisible(offset))
{
int visiblerows = viewHeight()/cellHeight(0);
setTopCell( QMAX(0, offset - visiblerows/2) );
}
}
void DiffView::addLine(QString line, DiffList diffList, DiffType type, int no)
{
DiffViewItem *item = new DiffViewItem;
QFontMetrics fm(font());
int index = line.length()-1;
while( line[index].isSpace() ) {
index --;
}
//contains trailing spaces
line.truncate( index+1 );
item->type = type;
item->no = no;
item->inverted = false;
int lastPos = 0;
int fmFullSize = 0;
int fullWidth = 0;
DiffList::iterator it;
for (it = diffList.begin(); it != diffList.end(); ++it) {
DiffInfo diffInfo;
diffInfo.txt = line.mid( (*it).pos,(*it).len);
diffInfo.type = (*it).type;
diffInfo.fmSize = fm.width( diffInfo.txt);
fmFullSize += diffInfo.fmSize;
fullWidth += (*it).len;
lastPos = (*it).pos + (*it).len;
item->diffInfoList.append(diffInfo);
}
item->pixelwidth = fmFullSize;
items->append(item);
recalcTableSize( item );
setNumRows(numRows()+1);
}
int DiffView::count()
{
return items->count();
}
int DiffView::findLine(int lineno)
{
int offset;
DiffViewItem tmp;
tmp.no = lineno;
if ( (offset = items->find(&tmp)) == -1)
{
exit( 234);
return -1;
}
return offset;
}
void DiffView::findNextDiff (void)
{
if (numRows() < 1) return;
QPtrListIterator<DiffViewItem> it(*items);
DiffType type;
/* Look for the next change and move the view to it */
for (it+=topCell(), type=it.current()->type; it.current(); ++it)
{
if (!it.current()) return;
if (type != it.current()->type)
{
type = it.current()->type;
if ((type != Neutral) && (type != Unchanged))
{
setTopCell (items->findRef (it.current()));
return;
}
}
}
/* Scroll to the bottom of the view if no further changes were found */
setTopCell (numRows ());
}
void DiffView::findPrevDiff (void)
{
if (numRows() < 1) return;
QPtrListIterator<DiffViewItem> it(*items);
DiffType type;
/* Look for the previous change and move the view to it */
for (it+=topCell(), type=it.current()->type; it.current(); --it)
{
if (type != it.current()->type)
{
type = it.current()->type;
if ((type != Neutral) && (type != Unchanged))
{
/* Adjust so we show the begining of the
* change rather than the end.
*/
while (type == it.current()->type) {
--it;
if (it == 0) {
setTopCell (0);
return;
}
}
setTopCell (items->findRef (++it));
return;
}
}
}
/* Scroll to the top of the view if no further changes were found */
setTopCell (0);
}
void DiffView::setInverted(int lineno, bool inverted)
{
int offset;
if ( (offset = findLine(lineno)) != -1)
items->at(offset)->inverted = inverted;
}
void DiffView::setCenterLine(int lineno)
{
int offset;
if ( (offset = findLine(lineno)) != -1)
setCenterOffset(offset);
}
int DiffView::cellWidth(int col) {
if (linenos && (col == 0) ) {
// col 0 contains linenos in this case
return m_lineNbTypeWidth;
} else {
if (marker && (col <= 1) ) return m_markerTypeWidth;
else return TableSize;
}
}
QSize DiffView::sizeHint() const
{
QFontMetrics fm(font());
return QSize( 4*fm.width("0123456789"), fm.lineSpacing()*8 );
}
void DiffView::paintCell(QPainter *p, int row, int col)
{
DiffViewItem *item = items->at(row);
int width = cellWidth(col);
int height = cellHeight();
QColor backgroundColor;
bool inverted;
int align;
int innerborder;
QString str;
bool isDiffLine = FALSE;
if (col == 0 && linenos) {
backgroundColor = QColor(222, 222, 222);
inverted = false;
align = AlignLeft;
innerborder = 0;
if (item->no == -1)
str = "+++++";
else
str.setNum(item->no);
} else if (marker && (col == 0 || col == 1)) {
//backgroundColor = lightGray; --> lightgray is wrongly mapped under IRIX
backgroundColor = QColor(222, 222, 222);
inverted = false;
align = AlignRight;
innerborder = BORDER;
str = (item->type==Change)? "|"
: (item->type==Insert)? "+"
: (item->type==Delete)? "-" : " ";
} else {
backgroundColor =
(item->type==Change)? QColor(237, 190, 190)
: (item->type==Insert)? QColor(190, 190, 237)
: (item->type==Delete)? QColor(190, 237, 190)
//: (item->type==Neutral)? gray : white; --> gray is wrongly mapped under IRIX
: (item->type==Neutral)? QColor(170, 170, 170) : QColor(255,255,255);
inverted = item->inverted;
align = AlignLeft;
innerborder = 0;
isDiffLine = TRUE;
}
if (inverted) {
p->setPen(backgroundColor);
//backgroundColor = black; --> black also
backgroundColor = QColor(90, 90, 90);
} else
p->setPen(black);
p->fillRect(0, 0, width, height, backgroundColor);
if (isDiffLine) {
DiffInfoList::iterator it;
QPen oriPen = p->pen();
QPen changePen(QColor(255,0,0));//changed
QPen insertPen(QColor(0,0,255));//added
QPen deletePen(QColor(80,255,80));//removed
int offset = 0;
for (it = item->diffInfoList.begin(); it != item->diffInfoList.end(); ++it) {
switch ( (*it).type) {
case Insert: {
p->setPen(insertPen);
break;
}
case Delete: {
p->setPen(deletePen);
break;
}
case Change: {
p->setPen(changePen);
break;
}
default: {
p->setPen(oriPen);
break;
}
}
p->drawText(innerborder+offset, 0, width-2*innerborder-offset, height, align, (*it).txt);
offset += (*it).fmSize;
}
} else {
p->drawText(innerborder, 0, width-2*innerborder, height, align, str);
}
}
void DiffView::resizeEvent( QResizeEvent *e) {
QtTableView::resizeEvent(e);
updateScrollBars();
if (verticalScrollBar()->isVisible()) {
QRect rect = verticalScrollBar()->style().querySubControlMetrics(QStyle::CC_ScrollBar,
verticalScrollBar(),
QStyle::SC_ScrollBarGroove);
m_ScrollBarMinPos = rect.top();
m_ScrollBarMaxPos = rect.height();
}
}
void DiffView::adjustScrollBarOffsets(int &from, int &height) {
updateScrollBars();
if (verticalScrollBar()->isVisible()) {
if (m_ScrollBarMinPos == -1) {
QRect rect = verticalScrollBar()->style().querySubControlMetrics(QStyle::CC_ScrollBar,
verticalScrollBar(),
QStyle::SC_ScrollBarGroove);
m_ScrollBarMinPos = rect.top();
m_ScrollBarMaxPos = rect.height();
}
from += m_ScrollBarMinPos;
height = m_ScrollBarMaxPos;
}
}
syntax highlighted by Code2HTML, v. 0.9.1