changeset 44:bed7ab59f62e

* A snatched bit of work on graph layout
author Chris Cannam
date Tue, 09 Nov 2010 17:51:12 +0000
parents c32067cd19f8
children 4286836bb3c9
files changeset.h changesetitem.cpp changesetitem.h grapher.cpp grapher.h hgexplorer.pro hgexpwidget.cpp hgrunner.cpp mainwindow.cpp
diffstat 9 files changed, 188 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/changeset.h	Sun Nov 07 19:59:54 2010 +0000
+++ b/changeset.h	Tue Nov 09 17:51:12 2010 +0000
@@ -13,6 +13,7 @@
     Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged STORED true);
     Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged STORED true);
     Q_PROPERTY(QString branch READ branch WRITE setBranch NOTIFY branchChanged STORED true);
+    Q_PROPERTY(QString tag READ tag WRITE setTag NOTIFY tagChanged STORED true);
     Q_PROPERTY(QString date READ date WRITE setDate NOTIFY dateChanged STORED true);
     Q_PROPERTY(QString age READ age WRITE setAge NOTIFY ageChanged STORED true);
     Q_PROPERTY(QStringList parents READ parents WRITE setParents NOTIFY parentsChanged STORED true);
@@ -24,6 +25,7 @@
     QString id() const { return m_id; }
     QString author() const { return m_author; }
     QString branch() const { return m_branch; }
+    QString tag() const { return m_tag; }
     QString date() const { return m_date; }
     QString age() const { return m_age; }
     QStringList parents() const { return m_parents; }
@@ -33,10 +35,16 @@
         return id().split(':')[0].toInt();
     }
 
+    QString authorName() const {
+	QString a = author();
+	return a.replace(QRegExp("\\s*<[^>]*>"), "");
+    }
+
 signals:
     void idChanged(QString id);
     void authorChanged(QString author);
     void branchChanged(QString branch);
+    void tagChanged(QString tag);
     void dateChanged(QString date);
     void ageChanged(QString age);
     void parentsChanged(QStringList parents);
@@ -46,6 +54,7 @@
     void setId(QString id) { m_id = id; emit idChanged(id); }
     void setAuthor(QString author) { m_author = author; emit authorChanged(author); }
     void setBranch(QString branch) { m_branch = branch; emit branchChanged(branch); }
+    void setTag(QString tag) { m_tag = tag; emit tagChanged(tag); }
     void setDate(QString date) { m_date = date; emit dateChanged(date); }
     void setAge(QString age) { m_age = age; emit ageChanged(age); }
     void setParents(QStringList parents) { m_parents = parents; emit parentsChanged(parents); }
@@ -55,6 +64,7 @@
     QString m_id;
     QString m_author;
     QString m_branch;
+    QString m_tag;
     QString m_date;
     QString m_age;
     QStringList m_parents;
--- a/changesetitem.cpp	Sun Nov 07 19:59:54 2010 +0000
+++ b/changesetitem.cpp	Tue Nov 09 17:51:12 2010 +0000
@@ -14,7 +14,9 @@
 ChangesetItem::paint(QPainter *paint, const QStyleOptionGraphicsItem *option,
                      QWidget *w)
 {
-    paint->drawText(50, 0, m_changeset->comment());
+//    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));
 
 //    paint->drawRect(QRectF(0, 0, 50, 50));
--- a/changesetitem.h	Sun Nov 07 19:59:54 2010 +0000
+++ b/changesetitem.h	Tue Nov 09 17:51:12 2010 +0000
@@ -8,13 +8,20 @@
 class ChangesetItem : public QGraphicsItem
 {
 public:
-    ChangesetItem(Changeset *cs) : m_changeset(cs) { }
+    ChangesetItem(Changeset *cs) : m_changeset(cs), m_column(0), m_row(0) { }
 
     virtual QRectF boundingRect() const;
     virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
 
+    int column() const { return m_column; }
+    int row() const { return m_row; }
+    void setColumn(int c) { m_column = c; }
+    void setRow(int r) { m_row = r; }
+
 private:
     Changeset *m_changeset;
+    int m_column;
+    int m_row;
 };
 
 #endif // CHANGESETITEM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grapher.cpp	Tue Nov 09 17:51:12 2010 +0000
@@ -0,0 +1,87 @@
+
+#include "grapher.h"
+
+#include <QSet>
+#include <QMap>
+
+#include <iostream>
+
+typedef QSet<int> ColumnSet;
+typedef QMap<int, ColumnSet> GridAlloc;
+typedef QMap<QString, Changeset *> IdChangesetMap;
+typedef QSet<Changeset *> ChangesetSet;
+
+ChangesetItem *
+layout(Changeset *cs,
+       IdChangesetMap idCsetMap,
+       ChangesetItemMap items,
+       GridAlloc &alloc,
+       ChangesetSet &handled)
+{
+    if (!cs) {
+	throw std::string("Null Changeset");
+    }
+    if (!items.contains(cs)) {
+	throw std::string("Changeset not in item map");
+    }
+    ChangesetItem *item = items[cs];
+    if (handled.contains(cs)) {
+	return item;
+    }
+    int row = 0;
+    int col = 0;
+    if (!cs->parents().empty()) {
+	bool haveRow = false;
+	foreach (QString parentId, cs->parents()) {
+	    if (parentId == "") continue; //!!!
+	    std::cerr << "recursing to parent \"" << parentId.toStdString() << "\" of \"" << cs->id().toStdString() << "\"" << std::endl;
+	    ChangesetItem *parentItem =
+		layout(idCsetMap[parentId],
+		       idCsetMap,
+		       items,
+		       alloc,
+		       handled);
+	    if (!haveRow || parentItem->row() < row) {
+		row = parentItem->row();
+		haveRow = true;
+	    }
+	    col += parentItem->column();
+	}
+	col /= cs->parents().size();
+	row = row - 1;
+	while (alloc[row].contains(col)) {
+	    if (col > 0) col = -col;
+	    else col = -col + 1;
+	}
+	alloc[row].insert(col);
+    }	
+    item->setColumn(col);
+    item->setRow(row);
+    item->setX(col * 100);
+    item->setY(row * 100);
+    handled.insert(cs);
+    return item;
+}
+
+void
+Grapher::layout(Changesets csets, ChangesetItemMap items)
+{
+    IdChangesetMap idCsetMap;
+    foreach (Changeset *cs, csets) {
+	std::cerr << cs->id().toStdString() << std::endl;
+	if (cs->id() == "") {
+	    throw std::string("Changeset has no ID");
+	}
+	if (idCsetMap.contains(cs->id())) {
+	    throw std::string("Changeset ID is already in map");
+	}
+	idCsetMap[cs->id()] = cs;
+    }
+
+    GridAlloc alloc;
+    ChangesetSet handled;
+    foreach (Changeset *cs, csets) {
+	::layout(cs, idCsetMap, items, alloc, handled);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grapher.h	Tue Nov 09 17:51:12 2010 +0000
@@ -0,0 +1,15 @@
+#ifndef GRAPHER_H
+#define GRAPHER_H
+
+#include "changeset.h"
+#include "changesetitem.h"
+
+typedef QMap<Changeset *, ChangesetItem *> ChangesetItemMap;
+
+class Grapher
+{
+public:
+    void layout(Changesets csets, ChangesetItemMap items);
+};
+
+#endif 
--- a/hgexplorer.pro	Sun Nov 07 19:59:54 2010 +0000
+++ b/hgexplorer.pro	Tue Nov 09 17:51:12 2010 +0000
@@ -1,3 +1,6 @@
+
+CONFIG += debug
+
 TEMPLATE = app
 TARGET = hgexplorer
 unix {
@@ -7,6 +10,7 @@
 HEADERS = mainwindow.h \
     hgexpwidget.h \
     common.h \
+    grapher.h \
     hgrunner.h \
     settingsdialog.h \
     changeset.h \
@@ -16,6 +20,7 @@
     mainwindow.cpp \
     hgexpwidget.cpp \
     hgrunner.cpp \
+    grapher.cpp \
     settingsdialog.cpp \
     common.cpp \
     changeset.cpp \
--- a/hgexpwidget.cpp	Sun Nov 07 19:59:54 2010 +0000
+++ b/hgexpwidget.cpp	Tue Nov 09 17:51:12 2010 +0000
@@ -11,6 +11,9 @@
 #include "logparser.h"
 #include "changeset.h"
 #include "changesetitem.h"
+#include "grapher.h"
+
+#include <iostream>
 
 #define REMOTE_REPO_STR  "Remote repository: "
 #define LOCAL_REPO_STR   "Local repository: "
@@ -245,12 +248,51 @@
     gv->scene()->deleteLater();
     QGraphicsScene *scene = new QGraphicsScene();
     Changesets csets = parseChangeSets(hgLogList);
+    if (csets.empty()) return;
+    ChangesetItemMap csetItemMap;
     foreach (Changeset *cs, csets) {
         ChangesetItem *item = new ChangesetItem(cs);
         item->setX(0);
-        item->setY(cs->number() * 100);
+        item->setY(0);
+	csetItemMap[cs] = item;
         scene->addItem(item);
     }
+    try {
+	Grapher().layout(csets, csetItemMap);
+    } catch (std::string s) {
+	std::cerr << "Internal error: Layout failed: " << s << std::endl;
+    }
+/*
+    QMap<QString, Changeset *> idCsetMap;
+    foreach (Changeset *cs, csets) {
+	if (cs->id() == "") {
+	    throw std::string("Changeset has no ID");
+	}
+	if (idCsetMap.contains(cs->id())) {
+	    throw std::string("Changeset ID is already in map");
+	}
+	idCsetMap[cs->id()] = cs;
+    }
+    typedef QSet<int> ColumnSet;
+    typedef QMap<int, ColumnSet> GridAlloc;
+    typedef QMap<Changeset *, ChangesetItem *> ChangesetItemMap;
+    ChangesetItemMap csetItemMap;
+    foreach (Changeset *cs, csets) {
+        ChangesetItem *item = new ChangesetItem(cs);
+        item->setX(0);
+        item->setY(-cs->number() * 100);
+	csetItemMap[cs] = item;
+        scene->addItem(item);
+    }
+    QSet<Changeset *> handled;
+    for (int i = csets.size() - 1; i >= 0; --i) {
+	Changeset *cs = csets[i];
+	if (handled.contains(cs)) continue;
+	
+	
+	handled.insert(cs);
+    }
+*/
     gv->setScene(scene);
 }
 
@@ -326,10 +368,24 @@
     foreach (LogEntry e, log) {
         Changeset *cs = new Changeset();
         foreach (QString key, e.keys()) {
-            cs->setProperty(key.toLocal8Bit().data(), e.value(key));
+	    if (key == "parents") {
+		QStringList parents = e.value(key).split
+		    (" ", QString::SkipEmptyParts);
+		cs->setParents(parents);
+	    } else {
+		cs->setProperty(key.toLocal8Bit().data(), e.value(key));
+	    }
         }
         csets.push_back(cs);
     }
+    for (int i = 0; i+1 < csets.size(); ++i) {
+	Changeset *cs = csets[i];
+	if (cs->parents().empty()) {
+	    QStringList list;
+	    list.push_back(csets[i+1]->id());
+	    cs->setParents(list);
+	}
+    }
     return csets;
 }
 
--- a/hgrunner.cpp	Sun Nov 07 19:59:54 2010 +0000
+++ b/hgrunner.cpp	Tue Nov 09 17:51:12 2010 +0000
@@ -151,7 +151,7 @@
     lastHgCommand = hgExePathAndName;
     lastParams = params.join(" ");
 
-    std::cerr << "HgRunner: starting: " << hgExePathAndName.toStdString();
+    std::cerr << "HgRunner: starting: " << hgExePathAndName.toStdString() << " ";
     foreach (QString param, params) std::cerr << param.toStdString() << " ";
     std::cerr << std::endl;
 
--- a/mainwindow.cpp	Sun Nov 07 19:59:54 2010 +0000
+++ b/mainwindow.cpp	Tue Nov 09 17:51:12 2010 +0000
@@ -121,7 +121,7 @@
         QStringList params;
         params << "log";
         params << "--template";
-        params << "id: {rev}:{node|short}\\nauthor: {author}\\nbranch: {branches}\\ndate: {date|isodate}\\nage: {date|age}\\nparents: {parents}\\ncomment: {desc|json}\\n\\n";
+        params << "id: {rev}:{node|short}\\nauthor: {author}\\nbranch: {branches}\\ntag: {tag}\\ndate: {date|isodate}\\nage: {date|age}\\nparents: {parents}\\ncomment: {desc|json}\\n\\n";
 
         runner -> startProc(getHgBinaryName(), workFolderPath, params);
         runningAction = ACT_LOG;