xref: /JGit/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java (revision 5c5f7c6b146b24f2bd4afae1902df85ad6e57ea3)
1cf2edb65SMykola Nikishov /*
2*5c5f7c6bSMatthias Sohn  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
3cf2edb65SMykola Nikishov  *
4*5c5f7c6bSMatthias Sohn  * This program and the accompanying materials are made available under the
5*5c5f7c6bSMatthias Sohn  * terms of the Eclipse Distribution License v. 1.0 which is available at
6*5c5f7c6bSMatthias Sohn  * https://www.eclipse.org/org/documents/edl-v10.php.
7cf2edb65SMykola Nikishov  *
8*5c5f7c6bSMatthias Sohn  * SPDX-License-Identifier: BSD-3-Clause
9cf2edb65SMykola Nikishov  */
10cf2edb65SMykola Nikishov 
11cf2edb65SMykola Nikishov package org.eclipse.jgit.awtui;
12cf2edb65SMykola Nikishov 
13cf2edb65SMykola Nikishov import java.awt.BasicStroke;
14cf2edb65SMykola Nikishov import java.awt.Component;
15cf2edb65SMykola Nikishov import java.awt.Graphics;
16cf2edb65SMykola Nikishov import java.awt.Stroke;
17cf2edb65SMykola Nikishov import java.text.DateFormat;
18cf2edb65SMykola Nikishov import java.text.SimpleDateFormat;
19cf2edb65SMykola Nikishov 
20cf2edb65SMykola Nikishov import javax.swing.JTable;
21cf2edb65SMykola Nikishov import javax.swing.ListSelectionModel;
22cf2edb65SMykola Nikishov import javax.swing.table.AbstractTableModel;
23cf2edb65SMykola Nikishov import javax.swing.table.DefaultTableCellRenderer;
24cf2edb65SMykola Nikishov import javax.swing.table.JTableHeader;
25cf2edb65SMykola Nikishov import javax.swing.table.TableCellRenderer;
26cf2edb65SMykola Nikishov import javax.swing.table.TableColumn;
27cf2edb65SMykola Nikishov import javax.swing.table.TableColumnModel;
28cf2edb65SMykola Nikishov import javax.swing.table.TableModel;
29cf2edb65SMykola Nikishov 
30cf2edb65SMykola Nikishov import org.eclipse.jgit.awtui.SwingCommitList.SwingLane;
31cf2edb65SMykola Nikishov import org.eclipse.jgit.lib.PersonIdent;
32cf2edb65SMykola Nikishov import org.eclipse.jgit.revplot.PlotCommit;
33cf2edb65SMykola Nikishov import org.eclipse.jgit.revplot.PlotCommitList;
3466cb2d9dSMatthias Sohn import org.eclipse.jgit.util.References;
35cf2edb65SMykola Nikishov 
36cf2edb65SMykola Nikishov /**
37cf2edb65SMykola Nikishov  * Draws a commit graph in a JTable.
38cf2edb65SMykola Nikishov  * <p>
39cf2edb65SMykola Nikishov  * This class is currently a very primitive commit visualization tool. It shows
40cf2edb65SMykola Nikishov  * a table of 3 columns:
41cf2edb65SMykola Nikishov  * <ol>
42cf2edb65SMykola Nikishov  * <li>Commit graph and short message</li>
43cf2edb65SMykola Nikishov  * <li>Author name and email address</li>
44cf2edb65SMykola Nikishov  * <li>Author date and time</li>
4532ff57a2SRobin Rosenberg  * </ol>
46cf2edb65SMykola Nikishov  */
47cf2edb65SMykola Nikishov public class CommitGraphPane extends JTable {
48cf2edb65SMykola Nikishov 	private static final long serialVersionUID = 1L;
49cf2edb65SMykola Nikishov 
50cf2edb65SMykola Nikishov 	private final SwingCommitList allCommits;
51cf2edb65SMykola Nikishov 
526ac27ee2SMatthias Sohn 	/**
536ac27ee2SMatthias Sohn 	 * Create a new empty panel.
546ac27ee2SMatthias Sohn 	 */
CommitGraphPane()55cf2edb65SMykola Nikishov 	public CommitGraphPane() {
56cf2edb65SMykola Nikishov 		allCommits = new SwingCommitList();
57cf2edb65SMykola Nikishov 		configureHeader();
58cf2edb65SMykola Nikishov 		setShowHorizontalLines(false);
59cf2edb65SMykola Nikishov 		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
60cf2edb65SMykola Nikishov 		configureRowHeight();
61cf2edb65SMykola Nikishov 	}
62cf2edb65SMykola Nikishov 
configureRowHeight()63cf2edb65SMykola Nikishov 	private void configureRowHeight() {
64cf2edb65SMykola Nikishov 		int h = 0;
65cf2edb65SMykola Nikishov 		for (int i = 0; i<getColumnCount(); ++i) {
66cf2edb65SMykola Nikishov 			TableCellRenderer renderer = getDefaultRenderer(getColumnClass(i));
67c310fa0cSRobin Rosenberg 			Component c = renderer.getTableCellRendererComponent(this,
68c310fa0cSRobin Rosenberg 					"ÅOj", false, false, 0, i); //$NON-NLS-1$
69cf2edb65SMykola Nikishov 			h = Math.max(h, c.getPreferredSize().height);
70cf2edb65SMykola Nikishov 		}
71cf2edb65SMykola Nikishov 		setRowHeight(h + getRowMargin());
72cf2edb65SMykola Nikishov 	}
73cf2edb65SMykola Nikishov 
74cf2edb65SMykola Nikishov 	/**
75cf2edb65SMykola Nikishov 	 * Get the commit list this pane renders from.
76cf2edb65SMykola Nikishov 	 *
77cf2edb65SMykola Nikishov 	 * @return the list the caller must populate.
78cf2edb65SMykola Nikishov 	 */
getCommitList()79cf2edb65SMykola Nikishov 	public PlotCommitList getCommitList() {
80cf2edb65SMykola Nikishov 		return allCommits;
81cf2edb65SMykola Nikishov 	}
82cf2edb65SMykola Nikishov 
836ac27ee2SMatthias Sohn 	/** {@inheritDoc} */
84cf2edb65SMykola Nikishov 	@Override
setModel(TableModel dataModel)856d370d83SHan-Wen Nienhuys 	public void setModel(TableModel dataModel) {
86cf2edb65SMykola Nikishov 		if (dataModel != null && !(dataModel instanceof CommitTableModel))
87f3d8a8ecSSasa Zivkov 			throw new ClassCastException(UIText.get().mustBeSpecialTableModel);
88cf2edb65SMykola Nikishov 		super.setModel(dataModel);
89cf2edb65SMykola Nikishov 	}
90cf2edb65SMykola Nikishov 
916ac27ee2SMatthias Sohn 	/** {@inheritDoc} */
92cf2edb65SMykola Nikishov 	@Override
createDefaultDataModel()93cf2edb65SMykola Nikishov 	protected TableModel createDefaultDataModel() {
94cf2edb65SMykola Nikishov 		return new CommitTableModel();
95cf2edb65SMykola Nikishov 	}
96cf2edb65SMykola Nikishov 
configureHeader()97cf2edb65SMykola Nikishov 	private void configureHeader() {
98cf2edb65SMykola Nikishov 		final JTableHeader th = getTableHeader();
99cf2edb65SMykola Nikishov 		final TableColumnModel cols = th.getColumnModel();
100cf2edb65SMykola Nikishov 
101cf2edb65SMykola Nikishov 		final TableColumn graph = cols.getColumn(0);
102cf2edb65SMykola Nikishov 		final TableColumn author = cols.getColumn(1);
103cf2edb65SMykola Nikishov 		final TableColumn date = cols.getColumn(2);
104cf2edb65SMykola Nikishov 
105c310fa0cSRobin Rosenberg 		graph.setHeaderValue(""); //$NON-NLS-1$
106f3d8a8ecSSasa Zivkov 		author.setHeaderValue(UIText.get().author);
107f3d8a8ecSSasa Zivkov 		date.setHeaderValue(UIText.get().date);
108cf2edb65SMykola Nikishov 
109cf2edb65SMykola Nikishov 		graph.setCellRenderer(new GraphCellRender());
110cf2edb65SMykola Nikishov 		author.setCellRenderer(new NameCellRender());
111cf2edb65SMykola Nikishov 		date.setCellRenderer(new DateCellRender());
112cf2edb65SMykola Nikishov 	}
113cf2edb65SMykola Nikishov 
114cf2edb65SMykola Nikishov 	class CommitTableModel extends AbstractTableModel {
115cf2edb65SMykola Nikishov 		private static final long serialVersionUID = 1L;
116cf2edb65SMykola Nikishov 
117cf2edb65SMykola Nikishov 		PlotCommit<SwingLane> lastCommit;
118cf2edb65SMykola Nikishov 
119cf2edb65SMykola Nikishov 		PersonIdent lastAuthor;
120cf2edb65SMykola Nikishov 
1217ac182f4SDavid Pursehouse 		@Override
getColumnCount()122cf2edb65SMykola Nikishov 		public int getColumnCount() {
123cf2edb65SMykola Nikishov 			return 3;
124cf2edb65SMykola Nikishov 		}
125cf2edb65SMykola Nikishov 
1267ac182f4SDavid Pursehouse 		@Override
getRowCount()127cf2edb65SMykola Nikishov 		public int getRowCount() {
128cf2edb65SMykola Nikishov 			return allCommits != null ? allCommits.size() : 0;
129cf2edb65SMykola Nikishov 		}
130cf2edb65SMykola Nikishov 
1317ac182f4SDavid Pursehouse 		@Override
getValueAt(int rowIndex, int columnIndex)1326d370d83SHan-Wen Nienhuys 		public Object getValueAt(int rowIndex, int columnIndex) {
133cf2edb65SMykola Nikishov 			final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
134cf2edb65SMykola Nikishov 			switch (columnIndex) {
135cf2edb65SMykola Nikishov 			case 0:
136cf2edb65SMykola Nikishov 				return c;
137cf2edb65SMykola Nikishov 			case 1:
138cf2edb65SMykola Nikishov 				return authorFor(c);
139cf2edb65SMykola Nikishov 			case 2:
140cf2edb65SMykola Nikishov 				return authorFor(c);
141cf2edb65SMykola Nikishov 			default:
142cf2edb65SMykola Nikishov 				return null;
143cf2edb65SMykola Nikishov 			}
144cf2edb65SMykola Nikishov 		}
145cf2edb65SMykola Nikishov 
authorFor(PlotCommit<SwingLane> c)146f3ec7cf3SHan-Wen Nienhuys 		PersonIdent authorFor(PlotCommit<SwingLane> c) {
14766cb2d9dSMatthias Sohn 			if (!References.isSameObject(c, lastCommit)) {
148cf2edb65SMykola Nikishov 				lastCommit = c;
149cf2edb65SMykola Nikishov 				lastAuthor = c.getAuthorIdent();
150cf2edb65SMykola Nikishov 			}
151cf2edb65SMykola Nikishov 			return lastAuthor;
152cf2edb65SMykola Nikishov 		}
153cf2edb65SMykola Nikishov 	}
154cf2edb65SMykola Nikishov 
1553a899a3aSRobin Rosenberg 	static class NameCellRender extends DefaultTableCellRenderer {
156cf2edb65SMykola Nikishov 		private static final long serialVersionUID = 1L;
157cf2edb65SMykola Nikishov 
1587ac182f4SDavid Pursehouse 		@Override
getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)159cf2edb65SMykola Nikishov 		public Component getTableCellRendererComponent(final JTable table,
160cf2edb65SMykola Nikishov 				final Object value, final boolean isSelected,
161cf2edb65SMykola Nikishov 				final boolean hasFocus, final int row, final int column) {
162cf2edb65SMykola Nikishov 			final PersonIdent pi = (PersonIdent) value;
163cf2edb65SMykola Nikishov 
164cf2edb65SMykola Nikishov 			final String valueStr;
165cf2edb65SMykola Nikishov 			if (pi != null)
166c310fa0cSRobin Rosenberg 				valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
167cf2edb65SMykola Nikishov 			else
168c310fa0cSRobin Rosenberg 				valueStr = ""; //$NON-NLS-1$
169cf2edb65SMykola Nikishov 			return super.getTableCellRendererComponent(table, valueStr,
170cf2edb65SMykola Nikishov 					isSelected, hasFocus, row, column);
171cf2edb65SMykola Nikishov 		}
172cf2edb65SMykola Nikishov 	}
173cf2edb65SMykola Nikishov 
1743a899a3aSRobin Rosenberg 	static class DateCellRender extends DefaultTableCellRenderer {
175cf2edb65SMykola Nikishov 		private static final long serialVersionUID = 1L;
176cf2edb65SMykola Nikishov 
177cf2edb65SMykola Nikishov 		private final DateFormat fmt = new SimpleDateFormat(
178c310fa0cSRobin Rosenberg 				"yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
179cf2edb65SMykola Nikishov 
1807ac182f4SDavid Pursehouse 		@Override
getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)181cf2edb65SMykola Nikishov 		public Component getTableCellRendererComponent(final JTable table,
182cf2edb65SMykola Nikishov 				final Object value, final boolean isSelected,
183cf2edb65SMykola Nikishov 				final boolean hasFocus, final int row, final int column) {
184cf2edb65SMykola Nikishov 			final PersonIdent pi = (PersonIdent) value;
185cf2edb65SMykola Nikishov 
186cf2edb65SMykola Nikishov 			final String valueStr;
187cf2edb65SMykola Nikishov 			if (pi != null)
188cf2edb65SMykola Nikishov 				valueStr = fmt.format(pi.getWhen());
189cf2edb65SMykola Nikishov 			else
190c310fa0cSRobin Rosenberg 				valueStr = ""; //$NON-NLS-1$
191cf2edb65SMykola Nikishov 			return super.getTableCellRendererComponent(table, valueStr,
192cf2edb65SMykola Nikishov 					isSelected, hasFocus, row, column);
193cf2edb65SMykola Nikishov 		}
194cf2edb65SMykola Nikishov 	}
195cf2edb65SMykola Nikishov 
1963a899a3aSRobin Rosenberg 	static class GraphCellRender extends DefaultTableCellRenderer {
197cf2edb65SMykola Nikishov 		private static final long serialVersionUID = 1L;
198cf2edb65SMykola Nikishov 
199cf2edb65SMykola Nikishov 		private final AWTPlotRenderer renderer = new AWTPlotRenderer(this);
200cf2edb65SMykola Nikishov 
201cf2edb65SMykola Nikishov 		PlotCommit<SwingLane> commit;
202cf2edb65SMykola Nikishov 
2037ac182f4SDavid Pursehouse 		@Override
204258f516aSRobin Stocker 		@SuppressWarnings("unchecked")
getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)205cf2edb65SMykola Nikishov 		public Component getTableCellRendererComponent(final JTable table,
206cf2edb65SMykola Nikishov 				final Object value, final boolean isSelected,
207cf2edb65SMykola Nikishov 				final boolean hasFocus, final int row, final int column) {
208cf2edb65SMykola Nikishov 			super.getTableCellRendererComponent(table, value, isSelected,
209cf2edb65SMykola Nikishov 					hasFocus, row, column);
210cf2edb65SMykola Nikishov 			commit = (PlotCommit<SwingLane>) value;
211cf2edb65SMykola Nikishov 			return this;
212cf2edb65SMykola Nikishov 		}
213cf2edb65SMykola Nikishov 
214cf2edb65SMykola Nikishov 		@Override
paintComponent(Graphics inputGraphics)2156d370d83SHan-Wen Nienhuys 		protected void paintComponent(Graphics inputGraphics) {
216cf2edb65SMykola Nikishov 			if (inputGraphics == null)
217cf2edb65SMykola Nikishov 				return;
218cf2edb65SMykola Nikishov 			renderer.paint(inputGraphics, commit);
219cf2edb65SMykola Nikishov 		}
220cf2edb65SMykola Nikishov 	}
221cf2edb65SMykola Nikishov 
222cf2edb65SMykola Nikishov 	static final Stroke[] strokeCache;
223cf2edb65SMykola Nikishov 
224cf2edb65SMykola Nikishov 	static {
225cf2edb65SMykola Nikishov 		strokeCache = new Stroke[4];
226cf2edb65SMykola Nikishov 		for (int i = 1; i < strokeCache.length; i++)
227cf2edb65SMykola Nikishov 			strokeCache[i] = new BasicStroke(i);
228cf2edb65SMykola Nikishov 	}
229cf2edb65SMykola Nikishov 
stroke(int width)230f3ec7cf3SHan-Wen Nienhuys 	static Stroke stroke(int width) {
231cf2edb65SMykola Nikishov 		if (width < strokeCache.length)
232cf2edb65SMykola Nikishov 			return strokeCache[width];
233cf2edb65SMykola Nikishov 		return new BasicStroke(width);
234cf2edb65SMykola Nikishov 	}
235cf2edb65SMykola Nikishov 
236cf2edb65SMykola Nikishov }
237