changeset 53:3c46b2ac45d3

* Put proper labels &c in changeset items; colour branches and users; etc
author Chris Cannam
date Fri, 12 Nov 2010 16:48:18 +0000
parents 384420567575
children 0e5fba6750c2
files changesetitem.cpp changesetitem.h colourset.cpp colourset.h connectionitem.cpp connectionitem.h dateitem.cpp dateitem.h easyhg.pro grapher.cpp grapher.h hgexpwidget.cpp mainwindow.cpp panned.cpp panned.h panner.cpp textabbrev.cpp textabbrev.h
diffstat 18 files changed, 546 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/changesetitem.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/changesetitem.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -1,22 +1,97 @@
 #include "changesetitem.h"
 #include "changeset.h"
+#include "textabbrev.h"
+#include "colourset.h"
 
 #include <QPainter>
 
+ChangesetItem::ChangesetItem(Changeset *cs) :
+    m_changeset(cs), m_column(0), m_row(0)
+{
+    m_font = QFont();
+    m_font.setPixelSize(11);
+    m_font.setBold(false);
+    m_font.setItalic(false);
+}
+
 QRectF
 ChangesetItem::boundingRect() const
 {
-    return QRectF(0, 0, 250, 50);
+    return QRectF(-24, -30, 97, 79);
 }
 
 void
 ChangesetItem::paint(QPainter *paint, const QStyleOptionGraphicsItem *option,
                      QWidget *w)
 {
-//    paint->drawText(50, 0, m_changeset->comment());
-    paint->drawText(5, 15, m_changeset->authorName());
-    paint->drawText(5, 30, m_changeset->branch());
-    paint->drawRect(QRectF(0, 0, 50, 50));
+    QTransform t = paint->worldTransform();
 
-//    paint->drawRect(QRectF(0, 0, 50, 50));
+    paint->save();
+    
+    ColourSet *colourSet = ColourSet::instance();
+    QColor branchColour = colourSet->getColourFor(m_changeset->branch());
+    QColor userColour = colourSet->getColourFor(m_changeset->author());
+
+    paint->setPen(QPen(branchColour, 2));
+    
+    QFont f(m_font);
+
+    float scale = std::min(t.m11(), t.m22());
+    if (scale > 1.0) {
+	int ps = int((f.pixelSize() / scale) + 0.5);
+	if (ps < 8) ps = 8;
+	f.setPixelSize(ps);
+    }
+	
+    paint->setFont(f);
+    QFontMetrics fm(f);
+    int fh = fm.height();
+    
+    QRectF r(-24, 0, 97, 49);
+    paint->drawRect(r);
+
+    if (scale < 0.1) {
+	paint->restore();
+	return;
+    }
+
+    if (m_changeset->children().empty()) {
+	f.setBold(true);
+	paint->setFont(f);
+	paint->drawText(-24, -fh + fm.ascent() - 4, m_changeset->branch());
+	f.setBold(false);
+    }
+
+    paint->fillRect(QRectF(-23.5, 0.5, 96, fh - 0.5), QBrush(userColour));
+
+    paint->setPen(QPen(Qt::white));
+
+    int wid = 95;
+    QString person = TextAbbrev::abbreviate(m_changeset->authorName(), fm, wid);
+    paint->drawText(-21, fm.ascent(), person);
+
+    paint->setPen(QPen(Qt::black));
+
+    f.setItalic(true);
+    fm = QFontMetrics(f);
+    fh = fm.height();
+    paint->setFont(f);
+
+    QString comment = m_changeset->comment().trimmed();
+    comment = comment.replace("\\n", "\n");
+    comment = comment.replace(QRegExp("^\"\\s*\\**\\s*"), "");
+    comment = comment.replace(QRegExp("\"$"), "");
+    comment = comment.replace("\\\"", "\"");
+    comment = comment.split('\n')[0];
+
+    wid = 95;
+    comment = TextAbbrev::abbreviate(comment, fm, wid, TextAbbrev::ElideEnd,
+				     "...", 2);
+
+    QStringList lines = comment.split('\n');
+    for (int i = 0; i < lines.size(); ++i) {
+	paint->drawText(-21, i * fh + fh + fm.ascent(), lines[i].trimmed());
+    }
+
+    paint->restore();
 }
--- a/changesetitem.h	Fri Nov 12 11:32:01 2010 +0000
+++ b/changesetitem.h	Fri Nov 12 16:48:18 2010 +0000
@@ -2,23 +2,27 @@
 #define CHANGESETITEM_H
 
 #include <QGraphicsItem>
+#include <QFont>
 
 class Changeset;
 
 class ChangesetItem : public QGraphicsItem
 {
 public:
-    ChangesetItem(Changeset *cs) : m_changeset(cs), m_column(0), m_row(0) { }
+    ChangesetItem(Changeset *cs);
 
     virtual QRectF boundingRect() const;
     virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
 
+    Changeset *getChangeset() { return m_changeset; }
+
     int column() const { return m_column; }
     int row() const { return m_row; }
     void setColumn(int c) { m_column = c; setX(c * 100); }
-    void setRow(int r) { m_row = r; setY(r * 100); }
+    void setRow(int r) { m_row = r; setY(r * 90); }
 
 private:
+    QFont m_font;
     Changeset *m_changeset;
     int m_column;
     int m_row;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/colourset.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -0,0 +1,35 @@
+
+#include "colourset.h"
+
+ColourSet
+ColourSet::m_instance;
+
+ColourSet::ColourSet() { }
+
+ColourSet *
+ColourSet::instance()
+{
+    return &m_instance;
+}
+
+QColor
+ColourSet::getColourFor(QString n)
+{
+    if (m_defaultNames.contains(n)) return Qt::black;
+    if (m_colours.contains(n)) return m_colours[n];
+
+    QColor c;
+
+    if (m_colours.empty()) {
+	c = QColor::fromHsv(0, 200, 100);
+    } else {
+	c = QColor::fromHsv((m_lastColour.hue() + 70) % 360, 200, 100);
+    }
+
+    m_colours[n] = c;
+    m_lastColour = c;
+    return c;
+}
+
+
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/colourset.h	Fri Nov 12 16:48:18 2010 +0000
@@ -0,0 +1,28 @@
+#ifndef _COLOURSET_H_
+#define _COLOURSET_H_
+
+#include <QSet>
+#include <QMap>
+#include <QColor>
+#include <QString>
+
+class ColourSet
+{
+public:
+    void clearDefaultNames() { m_defaultNames.clear(); }
+    void addDefaultName(QString n) { m_defaultNames.insert(n); }
+
+    QColor getColourFor(QString n);
+
+    static ColourSet *instance();
+
+private:
+    ColourSet();
+    QSet<QString> m_defaultNames;
+    QMap<QString, QColor> m_colours;
+    QColor m_lastColour;
+
+    static ColourSet m_instance;
+};
+
+#endif
--- a/connectionitem.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/connectionitem.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -3,6 +3,8 @@
 #include "connectionitem.h"
 
 #include "changesetitem.h"
+#include "changeset.h"
+#include "colourset.h"
 
 #include <QPainter>
 
@@ -10,12 +12,13 @@
 ConnectionItem::boundingRect() const
 {
     if (!m_parent || !m_child) return QRectF();
-    float scale = 100;
+    float xscale = 100;
+    float yscale = 90;
     float size = 50;
-    return QRectF(scale * m_child->column() + size/2 - 2,
-		  scale * m_child->row() + size - 2,
-		  scale * m_parent->column() - scale * m_child->column() + 4,
-		  scale * m_parent->row() - scale * m_child->row() - size + 4)
+    return QRectF(xscale * m_child->column() + size/2 - 2,
+		  yscale * m_child->row() + size - 2,
+		  xscale * m_parent->column() - xscale * m_child->column() + 4,
+		  yscale * m_parent->row() - yscale * m_child->row() - size + 4)
 	.normalized();
 }
 
@@ -23,26 +26,58 @@
 ConnectionItem::paint(QPainter *paint, const QStyleOptionGraphicsItem *, QWidget *)
 {
     QPainterPath p;
-    float scale = 100;
+
+    paint->save();
+
+    ColourSet *colourSet = ColourSet::instance();
+    QColor branchColour = colourSet->getColourFor(m_child->getChangeset()->branch());
+    paint->setPen(QPen(branchColour, 2));
+
+    float xscale = 100;
+
+    float yscale = 90;
     float size = 50;
-    p.moveTo(scale * m_child->column() + size/2,
-	     scale * m_child->row() + size);
-    if (m_parent->column() == m_child->column()) {
-	p.lineTo(scale * m_parent->column() + size/2,
-		 scale * m_parent->row());
-    } else {
-	p.cubicTo(scale * m_child->column() + size/2,
-		  scale * m_child->row() + size + size,
-		  scale * m_parent->column() + size/2,
-		  scale * m_child->row() + size,
-		  scale * m_parent->column() + size/2,
-		  scale * m_child->row() + scale);
-	if (abs(m_parent->row() - m_child->row()) > 1) {
-	    p.lineTo(scale * m_parent->column() + size/2,
-		     scale * m_parent->row());
+    float ygap = yscale - size;
+
+    int c_col = m_child->column(), c_row = m_child->row();
+    int p_col = m_parent->column(), p_row = m_parent->row();
+
+    float c_x = xscale * c_col + size/2;
+    float p_x = xscale * p_col + size/2;
+
+    p.moveTo(c_x, yscale * c_row + size);
+
+    if (p_col == c_col) {
+
+	p.lineTo(p_x, yscale * p_row);
+
+    } else if (m_type == Split || m_type == Normal) {
+
+	// place the bulk of the line on the child (i.e. branch) row
+
+	if (abs(p_row - c_row) > 1) {
+	    p.lineTo(c_x, yscale * p_row - ygap);
+	}
+
+	p.cubicTo(c_x, yscale * p_row,
+		  p_x, yscale * p_row - ygap,
+		  p_x, yscale * p_row);
+
+    } else if (m_type == Merge) {
+
+	// place bulk of the line on the parent row
+
+	p.cubicTo(c_x, yscale * c_row + size + ygap,
+		  p_x, yscale * c_row + size,
+		  p_x, yscale * c_row + size + ygap);
+
+	if (abs(p_row - c_row) > 1) {
+	    p.lineTo(p_x, yscale * p_row);
 	}
     }
+
     paint->drawPath(p);
+    paint->restore();
 }
 
 
--- a/connectionitem.h	Fri Nov 12 11:32:01 2010 +0000
+++ b/connectionitem.h	Fri Nov 12 16:48:18 2010 +0000
@@ -10,11 +10,20 @@
 class ConnectionItem : public QGraphicsItem
 {
 public:
-    ConnectionItem() : m_parent(0), m_child(0) { }
+    enum Type {
+	Normal,
+	Split,
+	Merge
+    };
+
+    ConnectionItem() : m_type(Normal), m_parent(0), m_child(0) { }
 
     virtual QRectF boundingRect() const;
     virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
 
+    Type connectionType() const { return m_type; }
+    void setConnectionType(Type t) { m_type = t; }
+
     //!!! deletion signals from parent/child
 
     ChangesetItem *parent() { return m_parent; }
@@ -24,6 +33,7 @@
     void setChild(ChangesetItem *c) { m_child = c; }
 
 private:
+    Type m_type;
     ChangesetItem *m_parent;
     ChangesetItem *m_child;
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dateitem.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -0,0 +1,55 @@
+
+#include "dateitem.h"
+
+#include <QPainter>
+#include <QBrush>
+#include <QFont>
+
+void
+DateItem::setRows(int minrow, int n)
+{
+    m_minrow = minrow;
+    m_maxrow = minrow + n - 1;
+    setY(m_minrow * 90);
+}
+
+void
+DateItem::setCols(int mincol, int n)
+{
+    m_mincol = mincol;
+    m_maxcol = mincol + n - 1;
+    setX(m_mincol * 100);
+}
+
+QRectF
+DateItem::boundingRect() const
+{
+    return QRectF(-75, -25,
+		  (m_maxcol - m_mincol + 1) * 100 + 100,
+		  (m_maxrow - m_minrow + 1) * 90).normalized();
+}
+
+void
+DateItem::paint(QPainter *paint, const QStyleOptionGraphicsItem *opt, QWidget *w)
+{
+    QBrush brush;
+
+    if (m_even) {
+	QColor c(QColor::fromRgb(240, 240, 240));
+	brush = QBrush(c);
+    } else {
+	QColor c(QColor::fromRgb(250, 250, 250));
+	brush = QBrush(c);
+    }
+
+    paint->fillRect(boundingRect(), brush);
+
+    paint->save();
+    QFont f(paint->font());
+    f.setBold(true);
+    paint->setFont(f);
+    paint->drawText(-70, -10, m_dateString);
+    paint->restore();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dateitem.h	Fri Nov 12 16:48:18 2010 +0000
@@ -0,0 +1,34 @@
+#ifndef DATEITEM_H
+#define DATEITEM_H
+
+#include <QGraphicsRectItem>
+
+class DateItem : public QGraphicsItem
+{
+public:
+    DateItem() :
+	m_minrow(0), m_maxrow(0),
+	m_mincol(0), m_maxcol(0),
+	m_even(false) {}
+
+    virtual QRectF boundingRect() const;
+    virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+
+    void setRows(int minrow, int n);
+    void setCols(int mincol, int n);
+
+    void setEven(bool e) { m_even = e; }
+
+    QString dateString() const { return m_dateString; }
+    void setDateString(QString s) { m_dateString = s; }
+
+private:
+    QString m_dateString;
+    int m_minrow;
+    int m_maxrow;
+    int m_mincol;
+    int m_maxcol;
+    bool m_even;
+};
+
+#endif // DATEITEM_H
--- a/easyhg.pro	Fri Nov 12 11:32:01 2010 +0000
+++ b/easyhg.pro	Fri Nov 12 16:48:18 2010 +0000
@@ -19,7 +19,9 @@
     panner.h \
     panned.h \
     connectionitem.h \
-    textabbrev.h
+    textabbrev.h \
+    dateitem.h \
+    colourset.h
 SOURCES = main.cpp \
     mainwindow.cpp \
     hgexpwidget.cpp \
@@ -33,7 +35,9 @@
     panner.cpp \
     panned.cpp \
     connectionitem.cpp \
-    textabbrev.cpp
+    textabbrev.cpp \
+    dateitem.cpp \
+    colourset.cpp
 
 # ! [0]
 RESOURCES = hgexplorer.qrc
--- a/grapher.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/grapher.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -1,6 +1,7 @@
 
 #include "grapher.h"
 #include "connectionitem.h"
+#include "dateitem.h"
 
 #include <QGraphicsScene>
 
@@ -43,7 +44,7 @@
     }
     Changeset *cs = m_changesets[id];
     ChangesetItem *item = m_items[id];
-    std::cerr << "Looking at " << id.toStdString() << std::endl;
+    std::cerr << "layoutRow: Looking at " << id.toStdString() << std::endl;
 
     int row = 0;
     int nparents = cs->parents().size();
@@ -73,11 +74,8 @@
     // above all nodes that have earlier dates (to the nearest day).
     // m_rowDates maps each row to a date: use that.
 
-    QString date = cs->date();
-
-    // n.b. this relies on the fact that the date component of an ISO
-    // date/time sorts correctly in a dictionary sort
-    while (m_rowDates.contains(row) && m_rowDates[row] < date) {
+    QString date = cs->age();
+    while (m_rowDates.contains(row) && m_rowDates[row] != date) {
 	--row;
     }
 
@@ -110,22 +108,11 @@
     if (!m_items.contains(id)) {
 	throw LayoutException(QString("Changeset %1 not in item map").arg(id));
     }
+
     Changeset *cs = m_changesets[id];
+    std::cerr << "layoutCol: Looking at " << id.toStdString() << std::endl;
+
     ChangesetItem *item = m_items[id];
-    std::cerr << "Looking at " << id.toStdString() << std::endl;
-
-    foreach (QString parentId, cs->parents()) {
-	if (!m_changesets.contains(parentId)) continue;
-	if (!m_handled.contains(parentId)) {
-	    layoutCol(parentId);
-	}
-    }
-
-    // Parent may have layed out child in the recursive call
-    if (m_handled.contains(id)) {
-	std::cerr << "Looks like we've dealt with " << id.toStdString() << std::endl;
-	return;
-    }
 
     int col = 0;
     int row = item->row();
@@ -191,19 +178,19 @@
 
     int nchildren = cs->children().size();
 
-    // look for merging children and make sure nobody
-    // is going to overwrite their "merge lines" if they extend further
-    // than a single step
+    // look for merging children and children distant from us but in a
+    // straight line, and make sure nobody is going to overwrite their
+    // connection lines
 
     foreach (QString childId, cs->children()) {
         if (!m_changesets.contains(childId)) continue;
         Changeset *child = m_changesets[childId];
-        if (child->parents().size() > 1) {
-            int childRow = m_items[childId]->row();
+	int childRow = m_items[childId]->row();
+        if (child->parents().size() > 1 || child->branch() == cs->branch()) {
             for (int r = row; r > childRow; --r) {
                 m_alloc[r].insert(col);
             }
-        }
+	}	    
     }
 
     // look for the case where exactly two children have the same
@@ -224,8 +211,11 @@
 	    for (int i = 0; i < 2; ++i) {
 		int off = i * 2 - 1; // 0 -> -1, 1 -> 1
 		ChangesetItem *it = m_items[special[i]];
+		m_alloc[it->row()].insert(col); // avoid our column
 		it->setColumn(findAvailableColumn(it->row(), col + off, true));
-		m_alloc[it->row()].insert(it->column());
+		for (int r = row; r >= it->row(); --r) {
+		    m_alloc[r].insert(it->column());
+		}
 		m_handled.insert(special[i]);
 	    }
 	}
@@ -279,10 +269,21 @@
 		}
 	    }
 	}
-	int home = 3;
+	int home = 2;
 	while (taken.contains(home)) {
-	    if (home > 0) home = -home;
-	    else home = -(home-3);
+	    if (home > 0) {
+		if (home % 2 == 1) {
+		    home = -home;
+		} else {
+		    home = home + 1;
+		}
+	    } else {
+		if ((-home) % 2 == 1) {
+		    home = home + 1;
+		} else {
+		    home = -(home-2);
+		}
+	    }
 	}
 	m_branchHomes[branch] = home;
     }
@@ -298,6 +299,13 @@
     return a->timestamp() < b->timestamp();
 }
 
+ChangesetItem *
+Grapher::getItemFor(Changeset *cs)
+{
+    if (!cs || !m_items.contains(cs->id())) return 0;
+    return m_items[cs->id()];
+}
+
 void
 Grapher::layout(Changesets csets)
 {
@@ -306,6 +314,8 @@
     m_alloc.clear();
     m_branchHomes.clear();
 
+    if (csets.empty()) return;
+
     foreach (Changeset *cs, csets) {
 
 	QString id = cs->id();
@@ -330,11 +340,13 @@
     foreach (Changeset *cs, csets) {
 	QString id = cs->id();
 	ChangesetItem *item = m_items[id];
+	bool merge = (cs->parents().size() > 1);
 	foreach (QString parentId, cs->parents()) {
 	    if (!m_changesets.contains(parentId)) continue;
 	    Changeset *parent = m_changesets[parentId];
 	    parent->addChild(id);
 	    ConnectionItem *conn = new ConnectionItem();
+	    if (merge) conn->setConnectionType(ConnectionItem::Merge);
 	    conn->setChild(item);
 	    conn->setParent(m_items[parentId]);
 	    m_scene->addItem(conn);
@@ -350,16 +362,80 @@
 
     qStableSort(csets.begin(), csets.end(), compareChangesetsByDate);
 
+    foreach (Changeset *cs, csets) {
+	std::cerr << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << std::endl;
+    }
+
     m_handled.clear();
-    for (int i = csets.size() - 1; i >= 0; --i) {
-	layoutRow(csets[i]->id());
+    foreach (Changeset *cs, csets) {
+	layoutRow(cs->id());
     }
 
     allocateBranchHomes(csets);
 
     m_handled.clear();
-    for (int i = csets.size() - 1; i >= 0; --i) {
-	layoutCol(csets[i]->id());
+    foreach (Changeset *cs, csets) {
+	foreach (QString parentId, cs->parents()) {
+	    if (!m_handled.contains(parentId) &&
+		m_changesets.contains(parentId)) {
+		layoutCol(parentId);
+	    }
+	}
+	layoutCol(cs->id());
+    }
+
+    // we know that 0 is an upper bound on row, and that mincol must
+    // be <= 0 and maxcol >= 0, so these initial values are good
+    int minrow = 0, maxrow = 0;
+    int mincol = 0, maxcol = 0;
+
+    foreach (int r, m_alloc.keys()) {
+	if (r < minrow) minrow = r;
+	if (r > maxrow) maxrow = r;
+	ColumnSet &c = m_alloc[r];
+	foreach (int i, c) {
+	    if (i < mincol) mincol = i;
+	    if (i > maxcol) maxcol = i;
+	}
+    }
+
+    QString prevDate;
+    int changeRow = 0;
+
+    bool even = false;
+    int n = 0;
+
+    for (int row = minrow; row <= maxrow; ++row) {
+	
+	QString date = m_rowDates[row];
+	n++;
+
+	if (date != prevDate) {
+	    if (prevDate != "") {
+		DateItem *item = new DateItem();
+		item->setDateString(prevDate);
+		item->setCols(mincol, maxcol - mincol + 1);
+		item->setRows(changeRow, n);
+		item->setEven(even);
+		item->setZValue(-1);
+		m_scene->addItem(item);
+		even = !even;
+	    }
+	    prevDate = date;
+	    changeRow = row;
+	    n = 0;
+	}
+    }
+    
+    if (n > 0) {
+	DateItem *item = new DateItem();
+	item->setDateString(prevDate);
+	item->setCols(mincol, maxcol - mincol + 1);
+	item->setRows(changeRow, n+1);
+	item->setEven(even);
+	item->setZValue(-1);
+	m_scene->addItem(item);
+	even = !even;
     }
 }
 
--- a/grapher.h	Fri Nov 12 11:32:01 2010 +0000
+++ b/grapher.h	Fri Nov 12 16:48:18 2010 +0000
@@ -17,6 +17,8 @@
 
     void layout(Changesets csets);
 
+    ChangesetItem *getItemFor(Changeset *cs);
+
     class LayoutException : public std::exception {
     public:
 	LayoutException(QString message) throw() : m_message(message) { }
--- a/hgexpwidget.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/hgexpwidget.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -261,8 +261,9 @@
     QGraphicsScene *scene = new QGraphicsScene();
     Changesets csets = parseChangeSets(hgLogList);
     if (csets.empty()) return;
+    Grapher g(scene);
     try {
-	Grapher(scene).layout(csets);
+	g.layout(csets);
     } catch (std::string s) {
 	std::cerr << "Internal error: Layout failed: " << s << std::endl;
     }
@@ -270,6 +271,8 @@
     panned->setScene(scene);
     panner->scene()->deleteLater();
     panner->setScene(scene);
+    ChangesetItem *tipItem = g.getItemFor(csets[0]);
+    if (tipItem) tipItem->ensureVisible();
 }
 
 
--- a/mainwindow.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/mainwindow.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -2,8 +2,6 @@
 ** Copyright (C) Jari Korhonen, 2010 (under lgpl)
 ****************************************************************************/
 
-#include "mainwindow.h"
-#include "settingsdialog.h"
 
 #include <QStringList>
 #include <QDir>
@@ -18,6 +16,10 @@
 #include <QToolBar>
 #include <QSettings>
 
+#include "mainwindow.h"
+#include "settingsdialog.h"
+#include "colourset.h"
+
 
 MainWindow::MainWindow()
 {
@@ -1445,6 +1447,12 @@
     initialFileTypesBits = (unsigned char) settings.value("viewFileTypes", QVariant(DEFAULT_HG_STAT_BITS)).toInt();
     resize(size);
     move(pos);
+
+    ColourSet *cs = ColourSet::instance();
+    cs->clearDefaultNames();
+    cs->addDefaultName("");
+    cs->addDefaultName("default");
+    cs->addDefaultName(userInfo);
 }
 
 
--- a/panned.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/panned.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -19,6 +19,7 @@
 
 #include <QScrollBar>
 #include <QGLWidget>
+#include <QWheelEvent>
 
 #include <iostream>
 
@@ -66,6 +67,22 @@
 }
 
 void
+Panned::zoomIn()
+{
+    QMatrix m = matrix();
+    m.scale(1.0 / 1.1, 1.0 / 1.1);
+    setMatrix(m);
+}
+
+void
+Panned::zoomOut()
+{
+    QMatrix m = matrix();
+    m.scale(1.1, 1.1);
+    setMatrix(m);
+}
+
+void
 Panned::slotSetPannedRect(QRectF pr)
 {
     centerOn(pr.center());
@@ -76,8 +93,23 @@
 void
 Panned::wheelEvent(QWheelEvent *ev)
 {
-    emit wheelEventReceived(ev);
-    QGraphicsView::wheelEvent(ev);
+    if (ev->modifiers() & Qt::ControlModifier) {
+        int d = ev->delta();
+        if (d > 0) {
+            while (d > 0) {
+                zoomOut();
+                d -= 120;
+            }
+        } else {
+            while (d < 0) {
+                zoomIn();
+                d += 120;
+            }
+        }
+    } else {
+        emit wheelEventReceived(ev);
+        QGraphicsView::wheelEvent(ev);
+    }
 }
 
 void
--- a/panned.h	Fri Nov 12 11:32:01 2010 +0000
+++ b/panned.h	Fri Nov 12 16:48:18 2010 +0000
@@ -41,6 +41,9 @@
     void slotSetPannedRect(QRectF);
     void slotEmulateWheelEvent(QWheelEvent *ev);
 
+    void zoomIn();
+    void zoomOut();
+
 protected:
     QRectF m_pannedRect;
 
--- a/panner.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/panner.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -39,6 +39,7 @@
     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setOptimizationFlags(QGraphicsView::DontSavePainterState |
                          QGraphicsView::IndirectPainting);
+    setRenderHints(QPainter::Antialiasing);
     setMouseTracking(true);
     setInteractive(false);
 }
@@ -65,6 +66,12 @@
 
     connect(this, SIGNAL(pannedRectChanged(QRectF)),
             p, SLOT(slotSetPannedRect(QRectF)));
+
+    connect(this, SIGNAL(zoomIn()),
+            p, SLOT(zoomIn()));
+
+    connect(this, SIGNAL(zoomOut()),
+            p, SLOT(zoomOut()));
 }
 
 void
@@ -210,10 +217,17 @@
 void
 Panner::wheelEvent(QWheelEvent *e)
 {
-    if (e->delta() > 0) {
-        emit zoomOut();
+    int d = e->delta();
+    if (d > 0) {
+        while (d > 0) {
+            emit zoomOut();
+            d -= 120;
+        }
     } else {
-        emit zoomIn();
+        while (d < 0) {
+            emit zoomIn();
+            d += 120;
+        }
     }
 }
 
--- a/textabbrev.cpp	Fri Nov 12 11:32:01 2010 +0000
+++ b/textabbrev.cpp	Fri Nov 12 16:48:18 2010 +0000
@@ -86,7 +86,7 @@
 QString
 TextAbbrev::abbreviate(QString text,
                        const QFontMetrics &metrics, int &maxWidth,
-                       Policy policy, QString ellipsis)
+                       Policy policy, QString ellipsis, int wrapLines)
 {
     if (ellipsis == "") ellipsis = getDefaultEllipsis();
 
@@ -100,21 +100,62 @@
     int truncated = text.length();
     QString original = text;
 
-    while (tw > maxWidth && truncated > 1) {
+    if (wrapLines < 2) {
 
-        truncated--;
+        while (tw > maxWidth && truncated > 1) {
 
-        if (truncated > ellipsis.length()) {
-            text = abbreviateTo(original, truncated, policy, ellipsis);
-        } else {
-            break;
+            truncated--;
+            
+            if (truncated > ellipsis.length()) {
+                text = abbreviateTo(original, truncated, policy, ellipsis);
+            } else {
+                break;
+            }
+            
+            tw = metrics.width(text);
+        }
+        
+        maxWidth = tw;
+        return text;
+
+    } else {
+        
+        QStringList words = text.split(' ', QString::SkipEmptyParts);
+        text = "";
+
+        tw = 0;
+        int i = 0;
+        QString good = "";
+        int lastUsed = 0;
+        while (tw < maxWidth && i < words.size()) {
+            if (text != "") text += " ";
+            text += words[i++];
+            tw = metrics.width(text);
+            if (tw < maxWidth) {
+                good = text;
+                lastUsed = i;
+            }
+        }
+            
+        if (tw < maxWidth) {
+            maxWidth = tw;
+            return text;
         }
 
-        tw = metrics.width(text);
+        text = good;
+
+        QString remainder;
+        while (lastUsed < words.size()) {
+            if (remainder != "") remainder += " ";
+            remainder += words[lastUsed++];
+        }
+        remainder = abbreviate(remainder, metrics, maxWidth,
+                               policy, ellipsis, wrapLines - 1);
+
+        maxWidth = std::max(maxWidth, tw);
+        text = text + '\n' + remainder;
+        return text;
     }
-
-    maxWidth = tw;
-    return text;
 }
 
 QStringList
--- a/textabbrev.h	Fri Nov 12 11:32:01 2010 +0000
+++ b/textabbrev.h	Fri Nov 12 16:48:18 2010 +0000
@@ -57,7 +57,8 @@
                               const QFontMetrics &metrics,
                               int &maxWidth,
                               Policy policy = ElideEnd,
-                              QString ellipsis = "");
+                              QString ellipsis = "",
+                              int wrapLines = 1);
     
     /**
      * Abbreviate all of the given texts to the given maximum length,