Switch jgit library to the EDL (3-clause BSD)
[jgit.git] / org.spearce.jgit / src / org / spearce / jgit / awtui / CommitGraphPane.java
blob2be0e955d78573519561421030cf296ba3bc988c
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.awtui;
40 import java.awt.BasicStroke;
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Graphics;
44 import java.awt.Graphics2D;
45 import java.awt.Polygon;
46 import java.awt.Stroke;
47 import java.text.DateFormat;
48 import java.text.SimpleDateFormat;
50 import javax.swing.JTable;
51 import javax.swing.ListSelectionModel;
52 import javax.swing.table.AbstractTableModel;
53 import javax.swing.table.DefaultTableCellRenderer;
54 import javax.swing.table.JTableHeader;
55 import javax.swing.table.TableColumn;
56 import javax.swing.table.TableColumnModel;
57 import javax.swing.table.TableModel;
59 import org.spearce.jgit.awtui.SwingCommitList.SwingLane;
60 import org.spearce.jgit.lib.PersonIdent;
61 import org.spearce.jgit.revplot.AbstractPlotRenderer;
62 import org.spearce.jgit.revplot.PlotCommit;
63 import org.spearce.jgit.revplot.PlotCommitList;
65 /**
66 * Draws a commit graph in a JTable.
67 * <p>
68 * This class is currently a very primitive commit visualization tool. It shows
69 * a table of 3 columns:
70 * <ol>
71 * <li>Commit graph and short message</li>
72 * <li>Author name and email address</li>
73 * <li>Author date and time</li>
74 * </ul>
76 public class CommitGraphPane extends JTable {
77 private static final long serialVersionUID = 1L;
79 private final SwingCommitList allCommits;
81 /** Create a new empty panel. */
82 public CommitGraphPane() {
83 allCommits = new SwingCommitList();
84 configureHeader();
85 setShowHorizontalLines(false);
86 setRowMargin(0);
87 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
90 /**
91 * Get the commit list this pane renders from.
93 * @return the list the caller must populate.
95 public PlotCommitList getCommitList() {
96 return allCommits;
99 @Override
100 public void setModel(final TableModel dataModel) {
101 if (dataModel != null && !(dataModel instanceof CommitTableModel))
102 throw new ClassCastException("Must be special table model.");
103 super.setModel(dataModel);
106 @Override
107 protected TableModel createDefaultDataModel() {
108 return new CommitTableModel();
111 private void configureHeader() {
112 final JTableHeader th = getTableHeader();
113 final TableColumnModel cols = th.getColumnModel();
115 final TableColumn graph = cols.getColumn(0);
116 final TableColumn author = cols.getColumn(1);
117 final TableColumn date = cols.getColumn(2);
119 graph.setHeaderValue("");
120 author.setHeaderValue("Author");
121 date.setHeaderValue("Date");
123 graph.setCellRenderer(new GraphCellRender());
124 author.setCellRenderer(new NameCellRender());
125 date.setCellRenderer(new DateCellRender());
128 class CommitTableModel extends AbstractTableModel {
129 private static final long serialVersionUID = 1L;
131 PlotCommit<SwingLane> lastCommit;
133 PersonIdent lastAuthor;
135 public int getColumnCount() {
136 return 3;
139 public int getRowCount() {
140 return allCommits != null ? allCommits.size() : 0;
143 public Object getValueAt(final int rowIndex, final int columnIndex) {
144 final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
145 switch (columnIndex) {
146 case 0:
147 return c;
148 case 1:
149 return authorFor(c);
150 case 2:
151 return authorFor(c);
152 default:
153 return null;
157 PersonIdent authorFor(final PlotCommit<SwingLane> c) {
158 if (c != lastCommit) {
159 lastCommit = c;
160 lastAuthor = c.getAuthorIdent();
162 return lastAuthor;
166 class NameCellRender extends DefaultTableCellRenderer {
167 private static final long serialVersionUID = 1L;
169 public Component getTableCellRendererComponent(final JTable table,
170 final Object value, final boolean isSelected,
171 final boolean hasFocus, final int row, final int column) {
172 final PersonIdent pi = (PersonIdent) value;
174 final String valueStr;
175 if (pi != null)
176 valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">";
177 else
178 valueStr = "";
179 return super.getTableCellRendererComponent(table, valueStr,
180 isSelected, hasFocus, row, column);
184 class DateCellRender extends DefaultTableCellRenderer {
185 private static final long serialVersionUID = 1L;
187 private final DateFormat fmt = new SimpleDateFormat(
188 "yyyy-MM-dd HH:mm:ss");
190 public Component getTableCellRendererComponent(final JTable table,
191 final Object value, final boolean isSelected,
192 final boolean hasFocus, final int row, final int column) {
193 final PersonIdent pi = (PersonIdent) value;
195 final String valueStr;
196 if (pi != null)
197 valueStr = fmt.format(pi.getWhen());
198 else
199 valueStr = "";
200 return super.getTableCellRendererComponent(table, valueStr,
201 isSelected, hasFocus, row, column);
205 class GraphCellRender extends DefaultTableCellRenderer {
206 private static final long serialVersionUID = 1L;
208 private final AWTPlotRenderer renderer = new AWTPlotRenderer(this);
210 PlotCommit<SwingLane> commit;
212 public Component getTableCellRendererComponent(final JTable table,
213 final Object value, final boolean isSelected,
214 final boolean hasFocus, final int row, final int column) {
215 super.getTableCellRendererComponent(table, value, isSelected,
216 hasFocus, row, column);
217 commit = (PlotCommit<SwingLane>) value;
218 return this;
221 @Override
222 protected void paintComponent(final Graphics inputGraphics) {
223 if (inputGraphics == null)
224 return;
225 renderer.paint(inputGraphics, commit);
229 static final Stroke[] strokeCache;
231 static {
232 strokeCache = new Stroke[4];
233 for (int i = 1; i < strokeCache.length; i++)
234 strokeCache[i] = new BasicStroke(i);
237 static Stroke stroke(final int width) {
238 if (width < strokeCache.length)
239 return strokeCache[width];
240 return new BasicStroke(width);
243 final class AWTPlotRenderer extends AbstractPlotRenderer<SwingLane, Color> {
245 final GraphCellRender cell;
247 Graphics2D g;
249 AWTPlotRenderer(final GraphCellRender c) {
250 cell = c;
253 void paint(final Graphics in, final PlotCommit<SwingLane> commit) {
254 g = (Graphics2D) in.create();
255 try {
256 final int h = cell.getHeight();
257 g.setColor(cell.getBackground());
258 g.fillRect(0, 0, cell.getWidth(), h);
259 if (commit != null)
260 paintCommit(commit, h);
261 } finally {
262 g.dispose();
263 g = null;
267 @Override
268 protected void drawLine(final Color color, int x1, int y1, int x2,
269 int y2, int width) {
270 if (y1 == y2) {
271 x1 -= width / 2;
272 x2 -= width / 2;
273 } else if (x1 == x2) {
274 y1 -= width / 2;
275 y2 -= width / 2;
278 g.setColor(color);
279 g.setStroke(stroke(width));
280 g.drawLine(x1, y1, x2, y2);
283 @Override
284 protected void drawCommitDot(final int x, final int y, final int w,
285 final int h) {
286 g.setColor(Color.blue);
287 g.setStroke(strokeCache[1]);
288 g.fillOval(x, y, w, h);
289 g.setColor(Color.black);
290 g.drawOval(x, y, w, h);
293 @Override
294 protected void drawBoundaryDot(final int x, final int y, final int w,
295 final int h) {
296 g.setColor(cell.getBackground());
297 g.setStroke(strokeCache[1]);
298 g.fillOval(x, y, w, h);
299 g.setColor(Color.black);
300 g.drawOval(x, y, w, h);
303 @Override
304 protected void drawText(final String msg, final int x, final int y) {
305 final int texty = g.getFontMetrics().getHeight()
306 - g.getFontMetrics().getDescent();
307 g.setColor(cell.getForeground());
308 g.drawString(msg, x, texty - (cell.getHeight() - y * 2));
311 @Override
312 protected Color laneColor(final SwingLane myLane) {
313 return myLane != null ? myLane.color : Color.black;
316 void paintTriangleDown(final int cx, final int y, final int h) {
317 final int tipX = cx;
318 final int tipY = y + h;
319 final int baseX1 = cx - 10 / 2;
320 final int baseX2 = tipX + 10 / 2;
321 final int baseY = y;
322 final Polygon triangle = new Polygon();
323 triangle.addPoint(tipX, tipY);
324 triangle.addPoint(baseX1, baseY);
325 triangle.addPoint(baseX2, baseY);
326 g.fillPolygon(triangle);
327 g.drawPolygon(triangle);