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  
68  /**
69   * Draws a commit graph in a JTable.
70   * <p>
71   * This class is currently a very primitive commit visualization tool. It shows
72   * a table of 3 columns:
73   * <ol>
74   * <li>Commit graph and short message</li>
75   * <li>Author name and email address</li>
76   * <li>Author date and time</li>
77   * </ol>
78   */
79  public class CommitGraphPane extends JTable {
80  	private static final long serialVersionUID = 1L;
81  
82  	private final SwingCommitList allCommits;
83  
84  	/**
85  	 * Create a new empty panel.
86  	 */
87  	public CommitGraphPane() {
88  		allCommits = new SwingCommitList();
89  		configureHeader();
90  		setShowHorizontalLines(false);
91  		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
92  		configureRowHeight();
93  	}
94  
95  	private void configureRowHeight() {
96  		int h = 0;
97  		for (int i = 0; i<getColumnCount(); ++i) {
98  			TableCellRenderer renderer = getDefaultRenderer(getColumnClass(i));
99  			Component c = renderer.getTableCellRendererComponent(this,
100 					"ÅOj", false, false, 0, i); //$NON-NLS-1$
101 			h = Math.max(h, c.getPreferredSize().height);
102 		}
103 		setRowHeight(h + getRowMargin());
104 	}
105 
106 	/**
107 	 * Get the commit list this pane renders from.
108 	 *
109 	 * @return the list the caller must populate.
110 	 */
111 	public PlotCommitList getCommitList() {
112 		return allCommits;
113 	}
114 
115 	/** {@inheritDoc} */
116 	@Override
117 	public void setModel(TableModel dataModel) {
118 		if (dataModel != null && !(dataModel instanceof CommitTableModel))
119 			throw new ClassCastException(UIText.get().mustBeSpecialTableModel);
120 		super.setModel(dataModel);
121 	}
122 
123 	/** {@inheritDoc} */
124 	@Override
125 	protected TableModel createDefaultDataModel() {
126 		return new CommitTableModel();
127 	}
128 
129 	private void configureHeader() {
130 		final JTableHeader th = getTableHeader();
131 		final TableColumnModel cols = th.getColumnModel();
132 
133 		final TableColumn graph = cols.getColumn(0);
134 		final TableColumn author = cols.getColumn(1);
135 		final TableColumn date = cols.getColumn(2);
136 
137 		graph.setHeaderValue(""); //$NON-NLS-1$
138 		author.setHeaderValue(UIText.get().author);
139 		date.setHeaderValue(UIText.get().date);
140 
141 		graph.setCellRenderer(new GraphCellRender());
142 		author.setCellRenderer(new NameCellRender());
143 		date.setCellRenderer(new DateCellRender());
144 	}
145 
146 	class CommitTableModel extends AbstractTableModel {
147 		private static final long serialVersionUID = 1L;
148 
149 		PlotCommit<SwingLane> lastCommit;
150 
151 		PersonIdent lastAuthor;
152 
153 		@Override
154 		public int getColumnCount() {
155 			return 3;
156 		}
157 
158 		@Override
159 		public int getRowCount() {
160 			return allCommits != null ? allCommits.size() : 0;
161 		}
162 
163 		@Override
164 		public Object getValueAt(int rowIndex, int columnIndex) {
165 			final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
166 			switch (columnIndex) {
167 			case 0:
168 				return c;
169 			case 1:
170 				return authorFor(c);
171 			case 2:
172 				return authorFor(c);
173 			default:
174 				return null;
175 			}
176 		}
177 
178 		PersonIdent authorFor(PlotCommit<SwingLane> c) {
179 			if (c != lastCommit) {
180 				lastCommit = c;
181 				lastAuthor = c.getAuthorIdent();
182 			}
183 			return lastAuthor;
184 		}
185 	}
186 
187 	static class NameCellRender extends DefaultTableCellRenderer {
188 		private static final long serialVersionUID = 1L;
189 
190 		@Override
191 		public Component getTableCellRendererComponent(final JTable table,
192 				final Object value, final boolean isSelected,
193 				final boolean hasFocus, final int row, final int column) {
194 			final PersonIdent./../../../org/eclipse/jgit/lib/PersonIdent.html#PersonIdent">PersonIdent pi = (PersonIdent) value;
195 
196 			final String valueStr;
197 			if (pi != null)
198 				valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
199 			else
200 				valueStr = ""; //$NON-NLS-1$
201 			return super.getTableCellRendererComponent(table, valueStr,
202 					isSelected, hasFocus, row, column);
203 		}
204 	}
205 
206 	static class DateCellRender extends DefaultTableCellRenderer {
207 		private static final long serialVersionUID = 1L;
208 
209 		private final DateFormat fmt = new SimpleDateFormat(
210 				"yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
211 
212 		@Override
213 		public Component getTableCellRendererComponent(final JTable table,
214 				final Object value, final boolean isSelected,
215 				final boolean hasFocus, final int row, final int column) {
216 			final PersonIdent./../../../org/eclipse/jgit/lib/PersonIdent.html#PersonIdent">PersonIdent pi = (PersonIdent) value;
217 
218 			final String valueStr;
219 			if (pi != null)
220 				valueStr = fmt.format(pi.getWhen());
221 			else
222 				valueStr = ""; //$NON-NLS-1$
223 			return super.getTableCellRendererComponent(table, valueStr,
224 					isSelected, hasFocus, row, column);
225 		}
226 	}
227 
228 	static class GraphCellRender extends DefaultTableCellRenderer {
229 		private static final long serialVersionUID = 1L;
230 
231 		private final AWTPlotRendererhtml#AWTPlotRenderer">AWTPlotRenderer renderer = new AWTPlotRenderer(this);
232 
233 		PlotCommit<SwingLane> commit;
234 
235 		@Override
236 		@SuppressWarnings("unchecked")
237 		public Component getTableCellRendererComponent(final JTable table,
238 				final Object value, final boolean isSelected,
239 				final boolean hasFocus, final int row, final int column) {
240 			super.getTableCellRendererComponent(table, value, isSelected,
241 					hasFocus, row, column);
242 			commit = (PlotCommit<SwingLane>) value;
243 			return this;
244 		}
245 
246 		@Override
247 		protected void paintComponent(Graphics inputGraphics) {
248 			if (inputGraphics == null)
249 				return;
250 			renderer.paint(inputGraphics, commit);
251 		}
252 	}
253 
254 	static final Stroke[] strokeCache;
255 
256 	static {
257 		strokeCache = new Stroke[4];
258 		for (int i = 1; i < strokeCache.length; i++)
259 			strokeCache[i] = new BasicStroke(i);
260 	}
261 
262 	static Stroke stroke(int width) {
263 		if (width < strokeCache.length)
264 			return strokeCache[width];
265 		return new BasicStroke(width);
266 	}
267 
268 }