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