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 */ CommitGraphPane()55 public CommitGraphPane() { 56 allCommits = new SwingCommitList(); 57 configureHeader(); 58 setShowHorizontalLines(false); 59 setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 60 configureRowHeight(); 61 } 62 configureRowHeight()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 */ getCommitList()79 public PlotCommitList getCommitList() { 80 return allCommits; 81 } 82 83 /** {@inheritDoc} */ 84 @Override setModel(TableModel dataModel)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 createDefaultDataModel()93 protected TableModel createDefaultDataModel() { 94 return new CommitTableModel(); 95 } 96 configureHeader()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 getColumnCount()122 public int getColumnCount() { 123 return 3; 124 } 125 126 @Override getRowCount()127 public int getRowCount() { 128 return allCommits != null ? allCommits.size() : 0; 129 } 130 131 @Override getValueAt(int rowIndex, int columnIndex)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 authorFor(PlotCommit<SwingLane> c)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 getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)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 getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)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") getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)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 paintComponent(Graphics inputGraphics)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 stroke(int width)230 static Stroke stroke(int width) { 231 if (width < strokeCache.length) 232 return strokeCache[width]; 233 return new BasicStroke(width); 234 } 235 236 } 237