Use thicker lines when drawing revplot diagrams
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / awtui / CommitGraphPane.java
bloba84f41e2fd83f4b33913beafa232cdb6229587d8
1 package org.spearce.jgit.awtui;
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.awt.Graphics;
7 import java.awt.Graphics2D;
8 import java.awt.Polygon;
9 import java.awt.Stroke;
10 import java.text.DateFormat;
11 import java.text.SimpleDateFormat;
13 import javax.swing.JTable;
14 import javax.swing.ListSelectionModel;
15 import javax.swing.table.AbstractTableModel;
16 import javax.swing.table.DefaultTableCellRenderer;
17 import javax.swing.table.JTableHeader;
18 import javax.swing.table.TableColumn;
19 import javax.swing.table.TableColumnModel;
20 import javax.swing.table.TableModel;
22 import org.spearce.jgit.awtui.SwingCommitList.SwingLane;
23 import org.spearce.jgit.lib.PersonIdent;
24 import org.spearce.jgit.revplot.AbstractPlotRenderer;
25 import org.spearce.jgit.revplot.PlotCommit;
26 import org.spearce.jgit.revplot.PlotCommitList;
28 /**
29 * Draws a commit graph in a JTable.
30 * <p>
31 * This class is currently a very primitive commit visualization tool. It shows
32 * a table of 3 columns:
33 * <ol>
34 * <li>Commit graph and short message</li>
35 * <li>Author name and email address</li>
36 * <li>Author date and time</li>
37 * </ul>
39 public class CommitGraphPane extends JTable {
40 private static final long serialVersionUID = 1L;
42 private final SwingCommitList allCommits;
44 /** Create a new empty panel. */
45 public CommitGraphPane() {
46 allCommits = new SwingCommitList();
47 configureHeader();
48 setShowHorizontalLines(false);
49 setRowMargin(0);
50 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
53 /**
54 * Get the commit list this pane renders from.
56 * @return the list the caller must populate.
58 public PlotCommitList getCommitList() {
59 return allCommits;
62 @Override
63 public void setModel(final TableModel dataModel) {
64 if (dataModel != null && !(dataModel instanceof CommitTableModel))
65 throw new ClassCastException("Must be special table model.");
66 super.setModel(dataModel);
69 @Override
70 protected TableModel createDefaultDataModel() {
71 return new CommitTableModel();
74 private void configureHeader() {
75 final JTableHeader th = getTableHeader();
76 final TableColumnModel cols = th.getColumnModel();
78 final TableColumn graph = cols.getColumn(0);
79 final TableColumn author = cols.getColumn(1);
80 final TableColumn date = cols.getColumn(2);
82 graph.setHeaderValue("");
83 author.setHeaderValue("Author");
84 date.setHeaderValue("Date");
86 graph.setCellRenderer(new GraphCellRender());
87 author.setCellRenderer(new NameCellRender());
88 date.setCellRenderer(new DateCellRender());
91 class CommitTableModel extends AbstractTableModel {
92 private static final long serialVersionUID = 1L;
94 PlotCommit<SwingLane> lastCommit;
96 PersonIdent lastAuthor;
98 public int getColumnCount() {
99 return 3;
102 public int getRowCount() {
103 return allCommits != null ? allCommits.size() : 0;
106 public Object getValueAt(final int rowIndex, final int columnIndex) {
107 final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
108 switch (columnIndex) {
109 case 0:
110 return c;
111 case 1:
112 return authorFor(c);
113 case 2:
114 return authorFor(c);
115 default:
116 return null;
120 PersonIdent authorFor(final PlotCommit<SwingLane> c) {
121 if (c != lastCommit) {
122 lastCommit = c;
123 lastAuthor = c.getAuthorIdent();
125 return lastAuthor;
129 class NameCellRender extends DefaultTableCellRenderer {
130 private static final long serialVersionUID = 1L;
132 public Component getTableCellRendererComponent(final JTable table,
133 final Object value, final boolean isSelected,
134 final boolean hasFocus, final int row, final int column) {
135 final PersonIdent pi = (PersonIdent) value;
137 final String valueStr;
138 if (pi != null)
139 valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">";
140 else
141 valueStr = "";
142 return super.getTableCellRendererComponent(table, valueStr,
143 isSelected, hasFocus, row, column);
147 class DateCellRender extends DefaultTableCellRenderer {
148 private static final long serialVersionUID = 1L;
150 private final DateFormat fmt = new SimpleDateFormat(
151 "yyyy-MM-dd HH:mm:ss");
153 public Component getTableCellRendererComponent(final JTable table,
154 final Object value, final boolean isSelected,
155 final boolean hasFocus, final int row, final int column) {
156 final PersonIdent pi = (PersonIdent) value;
158 final String valueStr;
159 if (pi != null)
160 valueStr = fmt.format(pi.getWhen());
161 else
162 valueStr = "";
163 return super.getTableCellRendererComponent(table, valueStr,
164 isSelected, hasFocus, row, column);
168 class GraphCellRender extends DefaultTableCellRenderer {
169 private static final long serialVersionUID = 1L;
171 private final AWTPlotRenderer renderer = new AWTPlotRenderer(this);
173 PlotCommit<SwingLane> commit;
175 public Component getTableCellRendererComponent(final JTable table,
176 final Object value, final boolean isSelected,
177 final boolean hasFocus, final int row, final int column) {
178 super.getTableCellRendererComponent(table, value, isSelected,
179 hasFocus, row, column);
180 commit = (PlotCommit<SwingLane>) value;
181 return this;
184 @Override
185 protected void paintComponent(final Graphics inputGraphics) {
186 if (inputGraphics == null)
187 return;
188 renderer.paint(inputGraphics, commit);
192 static final Stroke[] strokeCache;
194 static {
195 strokeCache = new Stroke[4];
196 for (int i = 1; i < strokeCache.length; i++)
197 strokeCache[i] = new BasicStroke(i);
200 static Stroke stroke(final int width) {
201 if (width < strokeCache.length)
202 return strokeCache[width];
203 return new BasicStroke(width);
206 final class AWTPlotRenderer extends AbstractPlotRenderer<SwingLane, Color> {
208 final GraphCellRender cell;
210 Graphics2D g;
212 AWTPlotRenderer(final GraphCellRender c) {
213 cell = c;
216 void paint(final Graphics in, final PlotCommit<SwingLane> commit) {
217 g = (Graphics2D) in.create();
218 try {
219 final int h = cell.getHeight();
220 g.setColor(cell.getBackground());
221 g.fillRect(0, 0, cell.getWidth(), h);
222 if (commit != null)
223 paintCommit(commit, h);
224 } finally {
225 g.dispose();
226 g = null;
230 @Override
231 protected void drawLine(final Color color, final int x1, final int y1,
232 final int x2, final int y2, int width) {
233 g.setColor(color);
234 g.setStroke(stroke(width));
235 g.drawLine(x1, y1, x2, y2);
238 @Override
239 protected void drawCommitDot(final int x, final int y, final int w,
240 final int h) {
241 g.setColor(Color.blue);
242 g.setStroke(strokeCache[1]);
243 g.fillOval(x, y, w, h);
244 g.setColor(Color.black);
245 g.drawOval(x, y, w, h);
248 @Override
249 protected void drawBoundaryDot(final int x, final int y, final int w,
250 final int h) {
251 g.setColor(cell.getBackground());
252 g.setStroke(strokeCache[1]);
253 g.fillOval(x, y, w, h);
254 g.setColor(Color.black);
255 g.drawOval(x, y, w, h);
258 @Override
259 protected void drawText(final String msg, final int x, final int y) {
260 final int texty = g.getFontMetrics().getHeight()
261 - g.getFontMetrics().getDescent();
262 g.setColor(cell.getForeground());
263 g.drawString(msg, x, texty - (cell.getHeight() - y * 2));
266 @Override
267 protected Color laneColor(final SwingLane myLane) {
268 return myLane != null ? myLane.color : Color.black;
271 void paintTriangleDown(final int cx, final int y, final int h) {
272 final int tipX = cx;
273 final int tipY = y + h;
274 final int baseX1 = cx - 10 / 2;
275 final int baseX2 = tipX + 10 / 2;
276 final int baseY = y;
277 final Polygon triangle = new Polygon();
278 triangle.addPoint(tipX, tipY);
279 triangle.addPoint(baseX1, baseY);
280 triangle.addPoint(baseX2, baseY);
281 g.fillPolygon(triangle);
282 g.drawPolygon(triangle);