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 		public int getColumnCount() {
150 			return 3;
151 		}
152 
153 		public int getRowCount() {
154 			return allCommits != null ? allCommits.size() : 0;
155 		}
156 
157 		public Object getValueAt(final int rowIndex, final int columnIndex) {
158 			final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
159 			switch (columnIndex) {
160 			case 0:
161 				return c;
162 			case 1:
163 				return authorFor(c);
164 			case 2:
165 				return authorFor(c);
166 			default:
167 				return null;
168 			}
169 		}
170 
171 		PersonIdent authorFor(final PlotCommit<SwingLane> c) {
172 			if (c != lastCommit) {
173 				lastCommit = c;
174 				lastAuthor = c.getAuthorIdent();
175 			}
176 			return lastAuthor;
177 		}
178 	}
179 
180 	static class NameCellRender extends DefaultTableCellRenderer {
181 		private static final long serialVersionUID = 1L;
182 
183 		public Component getTableCellRendererComponent(final JTable table,
184 				final Object value, final boolean isSelected,
185 				final boolean hasFocus, final int row, final int column) {
186 			final PersonIdent pi = (PersonIdent) value;
187 
188 			final String valueStr;
189 			if (pi != null)
190 				valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
191 			else
192 				valueStr = ""; //$NON-NLS-1$
193 			return super.getTableCellRendererComponent(table, valueStr,
194 					isSelected, hasFocus, row, column);
195 		}
196 	}
197 
198 	static class DateCellRender extends DefaultTableCellRenderer {
199 		private static final long serialVersionUID = 1L;
200 
201 		private final DateFormat fmt = new SimpleDateFormat(
202 				"yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
203 
204 		public Component getTableCellRendererComponent(final JTable table,
205 				final Object value, final boolean isSelected,
206 				final boolean hasFocus, final int row, final int column) {
207 			final PersonIdent pi = (PersonIdent) value;
208 
209 			final String valueStr;
210 			if (pi != null)
211 				valueStr = fmt.format(pi.getWhen());
212 			else
213 				valueStr = ""; //$NON-NLS-1$
214 			return super.getTableCellRendererComponent(table, valueStr,
215 					isSelected, hasFocus, row, column);
216 		}
217 	}
218 
219 	static class GraphCellRender extends DefaultTableCellRenderer {
220 		private static final long serialVersionUID = 1L;
221 
222 		private final AWTPlotRenderer renderer = new AWTPlotRenderer(this);
223 
224 		PlotCommit<SwingLane> commit;
225 
226 		@SuppressWarnings("unchecked")
227 		public Component getTableCellRendererComponent(final JTable table,
228 				final Object value, final boolean isSelected,
229 				final boolean hasFocus, final int row, final int column) {
230 			super.getTableCellRendererComponent(table, value, isSelected,
231 					hasFocus, row, column);
232 			commit = (PlotCommit<SwingLane>) value;
233 			return this;
234 		}
235 
236 		@Override
237 		protected void paintComponent(final Graphics inputGraphics) {
238 			if (inputGraphics == null)
239 				return;
240 			renderer.paint(inputGraphics, commit);
241 		}
242 	}
243 
244 	static final Stroke[] strokeCache;
245 
246 	static {
247 		strokeCache = new Stroke[4];
248 		for (int i = 1; i < strokeCache.length; i++)
249 			strokeCache[i] = new BasicStroke(i);
250 	}
251 
252 	static Stroke stroke(final int width) {
253 		if (width < strokeCache.length)
254 			return strokeCache[width];
255 		return new BasicStroke(width);
256 	}
257 
258 }