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