View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.awtui;
45  
46  import java.awt.BasicStroke;
47  import java.awt.Component;
48  import java.awt.Graphics;
49  import java.awt.Stroke;
50  import java.text.DateFormat;
51  import java.text.SimpleDateFormat;
52  
53  import javax.swing.JTable;
54  import javax.swing.ListSelectionModel;
55  import javax.swing.table.AbstractTableModel;
56  import javax.swing.table.DefaultTableCellRenderer;
57  import javax.swing.table.JTableHeader;
58  import javax.swing.table.TableCellRenderer;
59  import javax.swing.table.TableColumn;
60  import javax.swing.table.TableColumnModel;
61  import javax.swing.table.TableModel;
62  
63  import org.eclipse.jgit.awtui.SwingCommitList.SwingLane;
64  import org.eclipse.jgit.lib.PersonIdent;
65  import org.eclipse.jgit.revplot.PlotCommit;
66  import org.eclipse.jgit.revplot.PlotCommitList;
67  import org.eclipse.jgit.util.References;
68  
69  /**
70   * Draws a commit graph in a JTable.
71   * <p>
72   * This class is currently a very primitive commit visualization tool. It shows
73   * a table of 3 columns:
74   * <ol>
75   * <li>Commit graph and short message</li>
76   * <li>Author name and email address</li>
77   * <li>Author date and time</li>
78   * </ol>
79   */
80  public class CommitGraphPane extends JTable {
81  	private static final long serialVersionUID = 1L;
82  
83  	private final SwingCommitList allCommits;
84  
85  	/**
86  	 * Create a new empty panel.
87  	 */
88  	public CommitGraphPane() {
89  		allCommits = new SwingCommitList();
90  		configureHeader();
91  		setShowHorizontalLines(false);
92  		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
93  		configureRowHeight();
94  	}
95  
96  	private void configureRowHeight() {
97  		int h = 0;
98  		for (int i = 0; i<getColumnCount(); ++i) {
99  			TableCellRenderer renderer = getDefaultRenderer(getColumnClass(i));
100 			Component c = renderer.getTableCellRendererComponent(this,
101 					"ÅOj", false, false, 0, i); //$NON-NLS-1$
102 			h = Math.max(h, c.getPreferredSize().height);
103 		}
104 		setRowHeight(h + getRowMargin());
105 	}
106 
107 	/**
108 	 * Get the commit list this pane renders from.
109 	 *
110 	 * @return the list the caller must populate.
111 	 */
112 	public PlotCommitList getCommitList() {
113 		return allCommits;
114 	}
115 
116 	/** {@inheritDoc} */
117 	@Override
118 	public void setModel(TableModel dataModel) {
119 		if (dataModel != null && !(dataModel instanceof CommitTableModel))
120 			throw new ClassCastException(UIText.get().mustBeSpecialTableModel);
121 		super.setModel(dataModel);
122 	}
123 
124 	/** {@inheritDoc} */
125 	@Override
126 	protected TableModel createDefaultDataModel() {
127 		return new CommitTableModel();
128 	}
129 
130 	private void configureHeader() {
131 		final JTableHeader th = getTableHeader();
132 		final TableColumnModel cols = th.getColumnModel();
133 
134 		final TableColumn graph = cols.getColumn(0);
135 		final TableColumn author = cols.getColumn(1);
136 		final TableColumn date = cols.getColumn(2);
137 
138 		graph.setHeaderValue(""); //$NON-NLS-1$
139 		author.setHeaderValue(UIText.get().author);
140 		date.setHeaderValue(UIText.get().date);
141 
142 		graph.setCellRenderer(new GraphCellRender());
143 		author.setCellRenderer(new NameCellRender());
144 		date.setCellRenderer(new DateCellRender());
145 	}
146 
147 	class CommitTableModel extends AbstractTableModel {
148 		private static final long serialVersionUID = 1L;
149 
150 		PlotCommit<SwingLane> lastCommit;
151 
152 		PersonIdent lastAuthor;
153 
154 		@Override
155 		public int getColumnCount() {
156 			return 3;
157 		}
158 
159 		@Override
160 		public int getRowCount() {
161 			return allCommits != null ? allCommits.size() : 0;
162 		}
163 
164 		@Override
165 		public Object getValueAt(int rowIndex, int columnIndex) {
166 			final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
167 			switch (columnIndex) {
168 			case 0:
169 				return c;
170 			case 1:
171 				return authorFor(c);
172 			case 2:
173 				return authorFor(c);
174 			default:
175 				return null;
176 			}
177 		}
178 
179 		PersonIdent authorFor(PlotCommit<SwingLane> c) {
180 			if (!References.isSameObject(c, lastCommit)) {
181 				lastCommit = c;
182 				lastAuthor = c.getAuthorIdent();
183 			}
184 			return lastAuthor;
185 		}
186 	}
187 
188 	static class NameCellRender extends DefaultTableCellRenderer {
189 		private static final long serialVersionUID = 1L;
190 
191 		@Override
192 		public Component getTableCellRendererComponent(final JTable table,
193 				final Object value, final boolean isSelected,
194 				final boolean hasFocus, final int row, final int column) {
195 			final PersonIdent pi = (PersonIdent) value;
196 
197 			final String valueStr;
198 			if (pi != null)
199 				valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
200 			else
201 				valueStr = ""; //$NON-NLS-1$
202 			return super.getTableCellRendererComponent(table, valueStr,
203 					isSelected, hasFocus, row, column);
204 		}
205 	}
206 
207 	static class DateCellRender extends DefaultTableCellRenderer {
208 		private static final long serialVersionUID = 1L;
209 
210 		private final DateFormat fmt = new SimpleDateFormat(
211 				"yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
212 
213 		@Override
214 		public Component getTableCellRendererComponent(final JTable table,
215 				final Object value, final boolean isSelected,
216 				final boolean hasFocus, final int row, final int column) {
217 			final PersonIdent pi = (PersonIdent) value;
218 
219 			final String valueStr;
220 			if (pi != null)
221 				valueStr = fmt.format(pi.getWhen());
222 			else
223 				valueStr = ""; //$NON-NLS-1$
224 			return super.getTableCellRendererComponent(table, valueStr,
225 					isSelected, hasFocus, row, column);
226 		}
227 	}
228 
229 	static class GraphCellRender extends DefaultTableCellRenderer {
230 		private static final long serialVersionUID = 1L;
231 
232 		private final AWTPlotRendererhtml#AWTPlotRenderer">AWTPlotRenderer renderer = new AWTPlotRenderer(this);
233 
234 		PlotCommit<SwingLane> commit;
235 
236 		@Override
237 		@SuppressWarnings("unchecked")
238 		public Component getTableCellRendererComponent(final JTable table,
239 				final Object value, final boolean isSelected,
240 				final boolean hasFocus, final int row, final int column) {
241 			super.getTableCellRendererComponent(table, value, isSelected,
242 					hasFocus, row, column);
243 			commit = (PlotCommit<SwingLane>) value;
244 			return this;
245 		}
246 
247 		@Override
248 		protected void paintComponent(Graphics inputGraphics) {
249 			if (inputGraphics == null)
250 				return;
251 			renderer.paint(inputGraphics, commit);
252 		}
253 	}
254 
255 	static final Stroke[] strokeCache;
256 
257 	static {
258 		strokeCache = new Stroke[4];
259 		for (int i = 1; i < strokeCache.length; i++)
260 			strokeCache[i] = new BasicStroke(i);
261 	}
262 
263 	static Stroke stroke(int width) {
264 		if (width < strokeCache.length)
265 			return strokeCache[width];
266 		return new BasicStroke(width);
267 	}
268 
269 }