[JGIT PATCH 1/3] Add a rudimentary graph-based user interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The idea for a graph-based user interface cropped up during the Google
Summer of Code 2009.  Unfortunately, the project failed, but the idea
is too good to let it go to waste.

This commit introduces a very basic Swing component which shows a graph
of the commits in the repository, newest on top, older on the bottom.

Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx>
---
 .../src/org/spearce/jgit/gui/CommitGraph.java      |   27 ++++
 .../src/org/spearce/jgit/gui/CommitGrid.java       |  147 ++++++++++++++++++++
 .../src/org/spearce/jgit/gui/CommitPainter.java    |   43 ++++++
 .../src/org/spearce/jgit/gui/CommitRow.java        |   32 +++++
 .../src/org/spearce/jgit/gui/GraphCommit.java      |   41 ++++++
 .../src/org/spearce/jgit/gui/GraphWalk.java        |   36 +++++
 6 files changed, 326 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/gui/CommitGraph.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/gui/CommitGrid.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/gui/CommitPainter.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/gui/CommitRow.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/gui/GraphCommit.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/gui/GraphWalk.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/gui/CommitGraph.java b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitGraph.java
new file mode 100644
index 0000000..0d94865
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitGraph.java
@@ -0,0 +1,27 @@
+package org.spearce.jgit.gui;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import java.io.IOException;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.errors.MissingObjectException;
+
+public class CommitGraph extends JScrollPane {
+
+	protected GraphWalk walk;
+	protected CommitGrid grid;
+
+	public CommitGraph(GraphWalk walk)
+			throws IOException, MissingObjectException {
+		super(new CommitGrid(walk));
+	}
+
+	public Dimension getPreferredSize() {
+		return new Dimension(300, 600);
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/gui/CommitGrid.java b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitGrid.java
new file mode 100644
index 0000000..77f56f6
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitGrid.java
@@ -0,0 +1,147 @@
+package org.spearce.jgit.gui;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.JComponent;
+
+import org.spearce.jgit.lib.Repository;
+
+import org.spearce.jgit.revwalk.RevSort;
+
+public class CommitGrid extends JComponent {
+	protected GraphWalk walk;
+	protected List<CommitRow> rows;
+	protected CommitPainter painter;
+	protected Rectangle clip;
+
+	protected Dimension size;
+	protected int minColumn = 0, maxColumn = 0;
+
+	// commit dimensions
+	protected int w = 200;
+	protected int h = 30;
+	protected int spacing = 20;
+
+	public CommitGrid(GraphWalk walk) {
+		this.walk = walk;
+		rows = new ArrayList<CommitRow>();
+		painter = new CommitPainter();
+		clip = new Rectangle();
+		size = new Dimension(0, 0);
+	}
+
+	public Dimension getMinimumSize() {
+		return new Dimension((maxColumn - minColumn) * (w + spacing),
+			rows.size() * (h + spacing));
+	}
+
+	public Dimension getPreferredSize() {
+		return new Dimension((maxColumn - minColumn) * (w + spacing),
+			rows.size() * (h + spacing));
+	}
+
+	// TODO: public void resetWalk(Graphwalk walk)
+
+	protected void readCommits(int maxRow) {
+		if (rows.size() > maxRow)
+			return;
+		for (;;) {
+			GraphCommit commit;
+			try {
+				commit = (GraphCommit)walk.next();
+				if (commit == null)
+					return;
+			} catch (IOException e) {
+				// TODO: show error
+				continue;
+			}
+			commit.calculateColumn();
+			int row = commit.getRow();
+			while (row >= rows.size()) {
+				rows.add(new CommitRow());
+				size.height += h + spacing;
+				resize(getMinimumSize());
+				revalidate();
+			}
+			commit.column =
+				rows.get(row).add(commit, commit.getColumn());
+			commit.childrenCount = -1;
+			maxColumn = Math.max(maxColumn, commit.column);
+			minColumn = Math.min(minColumn, commit.column);
+		}
+	}
+
+	int x2column(int x) { return x / (w + spacing); }
+	int y2row(int y) { return y / (h + spacing); }
+	int column2x(int column) { return column * (w + spacing); }
+	int row2y(int row) { return row * (h + spacing); }
+
+	class CommitsInRectangle implements Iterator<GraphCommit> {
+		int minRow, maxRow, minColumn, maxColumn;
+		int row, column;
+
+		CommitsInRectangle(int minRow, int maxRow,
+				int minColumn, int maxColumn) {
+			this.minRow = minRow;
+			this.maxRow = maxRow;
+			this.minColumn = minColumn;
+			this.maxColumn = maxColumn;
+			row = minRow;
+			column = minColumn - 1;
+
+			readCommits(maxRow);
+			maxRow = Math.min(maxRow, rows.size());
+		}
+
+		CommitsInRectangle(Rectangle r) {
+			this(y2row(r.y), y2row(r.y + r.height - 1),
+				x2column(r.x), x2column(r.x + r.width - 1));
+		}
+
+		public boolean hasNext() {
+			for (; row <= maxRow; row++, column = minColumn - 1)
+				while (++column <= maxColumn)
+					if (rows.get(row).get(column) != null)
+						return true;
+			return false;
+		}
+
+		public GraphCommit next() {
+			return rows.get(row).get(column);
+		}
+
+		public void remove() throws UnsupportedOperationException {
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	public void paintComponent(Graphics g) {
+		g.getClipBounds(clip);
+		CommitsInRectangle iter = new CommitsInRectangle(clip);
+		while (iter.hasNext()) {
+			GraphCommit commit = iter.next();
+			painter.paint((Graphics2D)g, commit,
+					column2x(iter.column), row2y(iter.row),
+					w, h);
+			for (int i = 0; i < commit.getParentCount(); i++) {
+				GraphCommit parent =
+					(GraphCommit)commit.getParent(i);
+				parent.calculateColumn();
+				g.setColor(painter.foreground);
+				g.drawLine(column2x(commit.column) + w / 2,
+					row2y(commit.row) + h,
+					column2x(parent.column) + w / 2,
+					row2y(parent.row));
+			}
+		}
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/gui/CommitPainter.java b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitPainter.java
new file mode 100644
index 0000000..061cd2f
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitPainter.java
@@ -0,0 +1,43 @@
+package org.spearce.jgit.gui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.GradientPaint;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import java.awt.geom.Area;
+
+class CommitPainter {
+
+	protected Color background1, background2, foreground;
+	protected int margin = 2;
+
+	public CommitPainter() {
+		background1 = Color.LIGHT_GRAY;
+		background2 = background1.darker();
+		foreground = Color.BLACK;
+	}
+
+	public void paint(Graphics2D g, GraphCommit commit,
+			int x, int y, int w, int h) {
+
+		GradientPaint gradient = new GradientPaint(x, y, background1,
+				x + w, y + h, background2);
+
+		g.setPaint(gradient);
+		g.fillRect(x, y, w, h);
+		g.setColor(foreground);
+		g.drawRect(x, y, w, h);
+
+		Shape clip = g.getClip();
+		Area area = new Area(new Rectangle(x + margin, y + margin,
+					w - 2 * margin, h - 2 * margin));
+		area.intersect(new Area(clip));
+		g.setClip(area);
+		g.drawString(commit.getShortMessage(),
+				x + margin, y + margin + h / 2);
+		g.setClip(clip);
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/gui/CommitRow.java b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitRow.java
new file mode 100644
index 0000000..d110e89
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/gui/CommitRow.java
@@ -0,0 +1,32 @@
+package org.spearce.jgit.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CommitRow {
+	protected List<GraphCommit> left, right;
+
+	public CommitRow() {
+		left = new ArrayList<GraphCommit>();
+		right = new ArrayList<GraphCommit>();
+	}
+
+	public GraphCommit get(int index) {
+		if (index < 0) {
+			index = -1 - index;
+			return index < left.size() ? left.get(index) : null;
+		}
+		return index < right.size() ? right.get(index) : null;
+	}
+
+	public int add(GraphCommit commit, int desiredColumn) {
+		int leftIndex = -1 - desiredColumn;
+		if (Math.abs(-1 - desiredColumn - left.size()) <
+				Math.abs(desiredColumn - right.size())) {
+			left.add(commit);
+			return -left.size();
+		}
+		right.add(commit);
+		return right.size() - 1;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/gui/GraphCommit.java b/org.spearce.jgit/src/org/spearce/jgit/gui/GraphCommit.java
new file mode 100644
index 0000000..13a5936
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/gui/GraphCommit.java
@@ -0,0 +1,41 @@
+package org.spearce.jgit.gui;
+
+import org.spearce.jgit.lib.AnyObjectId;
+import org.spearce.jgit.revwalk.RevCommit;
+
+public class GraphCommit extends RevCommit {
+
+	protected int column = 0, row = 0;
+
+	protected int childrenCount = 0;
+	protected int cumulatedChildColumn = 0;
+
+	protected GraphCommit(final AnyObjectId id) {
+		super(id);
+	}
+
+	public void addChild(GraphCommit child) {
+		cumulatedChildColumn += child.column;
+		this.row = Math.max(this.row, child.row + 1);
+		childrenCount++;
+	}
+
+	// TODO: CommitLayout argument
+	public void calculateColumn() {
+		if (childrenCount < 0)
+			return; // already assigned
+		if (childrenCount > 0)
+			column = cumulatedChildColumn / childrenCount;
+		else
+			column = 0;
+		// TODO: ask the layout for the position
+	}
+
+	public int getColumn() {
+		return column;
+	}
+
+	public int getRow() {
+		return row;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/gui/GraphWalk.java b/org.spearce.jgit/src/org/spearce/jgit/gui/GraphWalk.java
new file mode 100644
index 0000000..fe98df3
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/gui/GraphWalk.java
@@ -0,0 +1,36 @@
+package org.spearce.jgit.gui;
+
+import java.io.IOException;
+
+import org.spearce.jgit.lib.AnyObjectId;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.revwalk.RevSort;
+import org.spearce.jgit.revwalk.RevWalk;
+import org.spearce.jgit.revwalk.RevCommit;
+import org.spearce.jgit.errors.IncorrectObjectTypeException;
+import org.spearce.jgit.errors.MissingObjectException;
+
+public class GraphWalk extends RevWalk {
+	public GraphWalk(final Repository repo)
+			/* throws IOException, MissingObjectException */ {
+		super(repo);
+		sort(RevSort.COMMIT_TIME_DESC, true);
+	}
+	
+	protected RevCommit createCommit(final AnyObjectId id) {
+		return new GraphCommit(id);
+	}
+
+	public RevCommit next() throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		GraphCommit commit = (GraphCommit)super.next();
+		if (commit == null)
+			return null;
+
+		// Update all parents
+		for (int i = 0; i < commit.getParentCount(); i++)
+			((GraphCommit)commit.getParent(i)).addChild(commit);
+
+		return commit;
+	}
+}
-- 
1.6.3.3.644.g82c56

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]