summaryrefslogtreecommitdiffstats
path: root/cervisia/diffview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cervisia/diffview.cpp')
-rw-r--r--cervisia/diffview.cpp507
1 files changed, 507 insertions, 0 deletions
diff --git a/cervisia/diffview.cpp b/cervisia/diffview.cpp
new file mode 100644
index 00000000..1892880e
--- /dev/null
+++ b/cervisia/diffview.cpp
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 1999-2002 Bernd Gehrmann
+ * bernd@mail.berlios.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.
+ *
+ * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include "diffview.h"
+
+#include <qpainter.h>
+#include <qscrollbar.h>
+#include <qpixmap.h>
+#include <qregexp.h>
+#include <qstyle.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+
+
+class DiffViewItem
+{
+public:
+ QString line;
+ DiffView::DiffType type;
+ bool inverted;
+ int no;
+};
+
+
+int DiffViewItemList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2)
+{
+ return (static_cast<DiffViewItem*>(item1)->no
+ == static_cast<DiffViewItem*>(item2)->no)? 0 : 1;
+}
+
+
+const int DiffView::BORDER = 7;
+
+
+DiffView::DiffView( KConfig& cfg, bool withlinenos, bool withmarker,
+ QWidget *parent, const char *name )
+ : QtTableView(parent, name, WRepaintNoErase)
+ , partConfig(cfg)
+{
+ setNumRows(0);
+ setNumCols( 1 + (withlinenos?1:0) + (withmarker?1:0) );
+ setTableFlags( Tbl_autoVScrollBar|Tbl_autoHScrollBar|
+ Tbl_smoothVScrolling );
+ setFrameStyle( QFrame::WinPanel | QFrame::Sunken );
+ setBackgroundMode( PaletteBase );
+ setWFlags( WResizeNoErase );
+
+ partConfig.setGroup("LookAndFeel");
+ setFont(partConfig.readFontEntry("DiffFont"));
+ QFontMetrics fm(font());
+ setCellHeight(fm.lineSpacing());
+ setCellWidth(0);
+ textwidth = 0;
+
+ partConfig.setGroup("General");
+ m_tabWidth = partConfig.readNumEntry("TabWidth", 8);
+
+ items.setAutoDelete(true);
+ linenos = withlinenos;
+ marker = withmarker;
+
+ partConfig.setGroup("Colors");
+ QColor defaultColor=QColor(237, 190, 190);
+ diffChangeColor=partConfig.readColorEntry("DiffChange",&defaultColor);
+ defaultColor=QColor(190, 190, 237);
+ diffInsertColor=partConfig.readColorEntry("DiffInsert",&defaultColor);
+ defaultColor=QColor(190, 237, 190);
+ diffDeleteColor=partConfig.readColorEntry("DiffDelete",&defaultColor);
+}
+
+
+void DiffView::setFont(const QFont &font)
+{
+ QtTableView::setFont(font);
+ QFontMetrics fm(font);
+ setCellHeight(fm.lineSpacing());
+}
+
+
+void DiffView::setPartner(DiffView *other)
+{
+ partner = other;
+ if (partner)
+ {
+ connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
+ SLOT(vertPositionChanged(int)) );
+ connect( verticalScrollBar(), SIGNAL(sliderMoved(int)),
+ SLOT(vertPositionChanged(int)) );
+ connect( horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ SLOT(horzPositionChanged(int)) );
+ connect( horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ SLOT(horzPositionChanged(int)) );
+ }
+}
+
+
+void DiffView::vertPositionChanged(int val)
+{
+ if (partner)
+ partner->setYOffset(QMIN(val,partner->maxYOffset()));
+}
+
+
+void DiffView::horzPositionChanged(int val)
+{
+ if (partner)
+ partner->setXOffset(QMIN(val,partner->maxXOffset()));
+}
+
+
+// *offset methods are only for views withlineno
+void DiffView::removeAtOffset(int offset)
+{
+ items.remove(offset);
+ setNumRows(numRows()-1);
+}
+
+
+void DiffView::insertAtOffset(const QString &line, DiffType type, int offset)
+{
+ DiffViewItem *item = new DiffViewItem;
+ item->line = line;
+ item->type = type;
+ item->no = -1;
+ item->inverted = false;
+ items.insert(offset, item);
+ setNumRows(numRows()+1);
+}
+
+
+void DiffView::setCenterOffset(int offset)
+{
+ if (!rowIsVisible(offset))
+ {
+ int visiblerows = viewHeight()/cellHeight(0);
+ setTopCell( QMAX(0, offset - visiblerows/2) );
+ }
+}
+
+
+void DiffView::addLine(const QString &line, DiffType type, int no)
+{
+ QFont f(font());
+ f.setBold(true);
+ QFontMetrics fmbold(f);
+ QFontMetrics fm(font());
+
+
+ // calculate textwidth based on 'line' where tabs are expanded
+ //
+ // *Please note*
+ // For some fonts, e.g. "Clean", is fm.maxWidth() greater than
+ // fmbold.maxWidth().
+ QString copy(line);
+ const int numTabs = copy.contains('\t', false);
+ copy.replace( QRegExp("\t"), "");
+
+ const int tabSize = m_tabWidth * QMAX(fm.maxWidth(), fmbold.maxWidth());
+ const int copyWidth = QMAX(fm.width(copy), fmbold.width(copy));
+ textwidth = QMAX(textwidth, copyWidth + numTabs * tabSize);
+
+ DiffViewItem *item = new DiffViewItem;
+ item->line = line;
+ item->type = type;
+ item->no = no;
+ item->inverted = false;
+ items.append(item);
+ setNumRows(numRows()+1);
+}
+
+
+QString DiffView::stringAtOffset(int offset)
+{
+ if (offset >= (int)items.count())
+ {
+ kdDebug(8050) << "Internal error: lineAtOffset" << endl;
+ }
+ return items.at(offset)->line;
+}
+
+
+int DiffView::count()
+{
+ return items.count();
+}
+
+
+int DiffView::findLine(int lineno)
+{
+ int offset;
+ DiffViewItem tmp;
+ tmp.no = lineno;
+ if ( (offset = items.find(&tmp)) == -1)
+ {
+ kdDebug(8050) << "Internal Error: Line " << lineno << " not found" << endl;
+ return -1;
+ }
+ return offset;
+}
+
+
+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);
+}
+
+
+QString DiffView::stringAtLine(int lineno)
+{
+ int pos;
+ if ( (pos = findLine(lineno)) != -1 )
+ return items.at(pos)->line;
+ else
+ return QString();
+}
+
+
+QByteArray DiffView::compressedContent()
+{
+ QByteArray res(items.count());
+
+ QPtrListIterator<DiffViewItem> it(items);
+ int i=0;
+ for (; it.current(); ++it)
+ {
+ switch (it.current()->type)
+ {
+ case Change: res[i] = 'C'; break;
+ case Insert: res[i] = 'I'; break;
+ case Delete: res[i] = 'D'; break;
+ case Neutral: res[i] = 'N'; break;
+ case Unchanged:res[i] = 'U'; break;
+ default: res[i] = ' ';
+ }
+ ++i;
+ }
+ return res;
+}
+
+
+int DiffView::cellWidth(int col)
+{
+ if (col == 0 && linenos)
+ {
+ QFontMetrics fm(font());
+ return fm.width("10000");
+ }
+ else if (marker && (col == 0 || col == 1))
+ {
+ QFontMetrics fm( fontMetrics() );
+ return QMAX(QMAX( fm.width(i18n("Delete")),
+ fm.width(i18n("Insert"))),
+ fm.width(i18n("Change")))+2*BORDER;
+ }
+ else
+ {
+ int rest = (linenos || marker)? cellWidth(0) : 0;
+ if (linenos && marker)
+ rest += cellWidth(1);
+ return QMAX(textwidth, viewWidth()-rest);
+ }
+}
+
+
+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)
+{
+ QFontMetrics fm(font());
+ p->setTabStops(m_tabWidth * fm.maxWidth());
+
+ DiffViewItem *item = items.at(row);
+
+ int width = cellWidth(col);
+ int height = cellHeight();
+
+ QColor backgroundColor;
+ bool inverted;
+ int align;
+ int innerborder;
+ QString str;
+
+ QFont oldFont(p->font());
+ if (item->type==Separator)
+ {
+ backgroundColor = KGlobalSettings::highlightColor();
+ p->setPen(KGlobalSettings::highlightedTextColor());
+ inverted = false;
+ align = AlignLeft;
+ innerborder = 0;
+ if (col == (linenos?1:0) + (marker?1:0))
+ str = item->line;
+ QFont f(oldFont);
+ f.setBold(true);
+ p->setFont(f);
+ }
+ else if (col == 0 && linenos)
+ {
+ backgroundColor = KGlobalSettings::highlightColor();
+ p->setPen(KGlobalSettings::highlightedTextColor());
+ inverted = false;
+ align = AlignLeft;
+ innerborder = 0;
+ if (item->no == -1)
+ str = "+++++";
+ else
+ str.setNum(item->no);
+ }
+ else if (marker && (col == 0 || col == 1))
+ {
+ backgroundColor = KGlobalSettings::alternateBackgroundColor();
+ p->setPen(KGlobalSettings::textColor());
+ inverted = false;
+ align = AlignRight;
+ innerborder = BORDER;
+ str = (item->type==Change)? i18n("Change")
+ : (item->type==Insert)? i18n("Insert")
+ : (item->type==Delete)? i18n("Delete") : QString::null;
+ }
+ else
+ {
+ backgroundColor =
+ (item->type==Change)? diffChangeColor
+ : (item->type==Insert)? diffInsertColor
+ : (item->type==Delete)? diffDeleteColor
+ : (item->type==Neutral)? KGlobalSettings::alternateBackgroundColor() : KGlobalSettings::baseColor();
+ p->setPen(KGlobalSettings::textColor());
+ inverted = item->inverted;
+ align = AlignLeft;
+ innerborder = 0;
+ str = item->line;
+ }
+
+ if (inverted)
+ {
+ p->setPen(backgroundColor);
+ backgroundColor = KGlobalSettings::textColor();
+ QFont f(oldFont);
+ f.setBold(true);
+ p->setFont(f);
+ }
+
+ p->fillRect(0, 0, width, height, backgroundColor);
+ p->drawText(innerborder, 0, width-2*innerborder, height, align|ExpandTabs, str);
+ p->setFont(oldFont);
+}
+
+
+void DiffView::wheelEvent(QWheelEvent *e)
+{
+ QApplication::sendEvent(verticalScrollBar(), e);
+}
+
+
+DiffZoomWidget::DiffZoomWidget(KConfig& cfg, QWidget *parent, const char *name)
+ : QFrame(parent, name)
+{
+ setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) );
+
+ cfg.setGroup("Colors");
+ QColor defaultColor=QColor(237, 190, 190);
+ diffChangeColor=cfg.readColorEntry("DiffChange",&defaultColor);
+ defaultColor=QColor(190, 190, 237);
+ diffInsertColor=cfg.readColorEntry("DiffInsert",&defaultColor);
+ defaultColor=QColor(190, 237, 190);
+ diffDeleteColor=cfg.readColorEntry("DiffDelete",&defaultColor);
+}
+
+
+DiffZoomWidget::~DiffZoomWidget()
+{}
+
+
+void DiffZoomWidget::setDiffView(DiffView *view)
+{
+ diffview = view;
+ QScrollBar *sb = const_cast<QScrollBar*>(diffview->scrollBar());
+ sb->installEventFilter(this);
+}
+
+
+QSize DiffZoomWidget::sizeHint() const
+{
+ return QSize(25, style().pixelMetric(QStyle::PM_ScrollBarExtent, this));
+}
+
+
+bool DiffZoomWidget::eventFilter(QObject *o, QEvent *e)
+{
+ if (e->type() == QEvent::Show
+ || e->type() == QEvent::Hide
+ || e->type() == QEvent::Resize)
+ repaint();
+
+ return QFrame::eventFilter(o, e);
+}
+
+
+void DiffZoomWidget::paintEvent(QPaintEvent *)
+{
+ const QScrollBar* scrollBar = diffview->scrollBar();
+ if (!scrollBar)
+ return;
+
+ // only y and height are important
+ const QRect scrollBarGroove(scrollBar->isVisible()
+ ? style().querySubControlMetrics(QStyle::CC_ScrollBar,
+ scrollBar,
+ QStyle::SC_ScrollBarGroove)
+ : rect());
+
+ // draw rectangles at the positions of the differences
+
+ const QByteArray& lineTypes(diffview->compressedContent());
+
+ QPixmap pixbuf(width(), scrollBarGroove.height());
+ pixbuf.fill(KGlobalSettings::baseColor());
+
+ QPainter p(&pixbuf, this);
+ if (const unsigned int numberOfLines = lineTypes.size())
+ {
+ const double scale(((double) scrollBarGroove.height()) / numberOfLines);
+ for (unsigned int index(0); index < numberOfLines;)
+ {
+ const char lineType(lineTypes[index]);
+
+ // don't use qRound() to avoid painting outside of the pixmap
+ // (yPos1 must be lesser than scrollBarGroove.height())
+ const int yPos1(static_cast<int>(index * scale));
+
+ // search next line with different lineType
+ for (++index; index < numberOfLines && lineType == lineTypes[index]; ++index)
+ ;
+
+ QColor color;
+ switch (lineType)
+ {
+ case 'C':
+ color = diffChangeColor;
+ break;
+ case 'I':
+ color = diffInsertColor;
+ break;
+ case 'D':
+ color = diffDeleteColor;
+ break;
+ case ' ':
+ case 'N':
+ color = KGlobalSettings::alternateBackgroundColor();
+ break;
+ }
+
+ if (color.isValid())
+ {
+ const int yPos2(qRound(index * scale));
+ const int areaHeight((yPos2 != yPos1) ? yPos2 - yPos1 : 1);
+
+ p.fillRect(0, yPos1, pixbuf.width(), areaHeight, QBrush(color));
+ }
+ }
+ }
+ p.flush();
+ bitBlt(this, 0, scrollBarGroove.y(), &pixbuf);
+}
+
+#include "diffview.moc"
+
+
+// Local Variables:
+// c-basic-offset: 4
+// End: