1cf2edb65SMykola Nikishov /* 2*5c5f7c6bSMatthias Sohn * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others 3cf2edb65SMykola Nikishov * 4*5c5f7c6bSMatthias Sohn * This program and the accompanying materials are made available under the 5*5c5f7c6bSMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 6*5c5f7c6bSMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 7cf2edb65SMykola Nikishov * 8*5c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 9cf2edb65SMykola Nikishov */ 10cf2edb65SMykola Nikishov 11cf2edb65SMykola Nikishov package org.eclipse.jgit.awtui; 12cf2edb65SMykola Nikishov 13cf2edb65SMykola Nikishov import java.awt.BasicStroke; 14cf2edb65SMykola Nikishov import java.awt.Component; 15cf2edb65SMykola Nikishov import java.awt.Graphics; 16cf2edb65SMykola Nikishov import java.awt.Stroke; 17cf2edb65SMykola Nikishov import java.text.DateFormat; 18cf2edb65SMykola Nikishov import java.text.SimpleDateFormat; 19cf2edb65SMykola Nikishov 20cf2edb65SMykola Nikishov import javax.swing.JTable; 21cf2edb65SMykola Nikishov import javax.swing.ListSelectionModel; 22cf2edb65SMykola Nikishov import javax.swing.table.AbstractTableModel; 23cf2edb65SMykola Nikishov import javax.swing.table.DefaultTableCellRenderer; 24cf2edb65SMykola Nikishov import javax.swing.table.JTableHeader; 25cf2edb65SMykola Nikishov import javax.swing.table.TableCellRenderer; 26cf2edb65SMykola Nikishov import javax.swing.table.TableColumn; 27cf2edb65SMykola Nikishov import javax.swing.table.TableColumnModel; 28cf2edb65SMykola Nikishov import javax.swing.table.TableModel; 29cf2edb65SMykola Nikishov 30cf2edb65SMykola Nikishov import org.eclipse.jgit.awtui.SwingCommitList.SwingLane; 31cf2edb65SMykola Nikishov import org.eclipse.jgit.lib.PersonIdent; 32cf2edb65SMykola Nikishov import org.eclipse.jgit.revplot.PlotCommit; 33cf2edb65SMykola Nikishov import org.eclipse.jgit.revplot.PlotCommitList; 3466cb2d9dSMatthias Sohn import org.eclipse.jgit.util.References; 35cf2edb65SMykola Nikishov 36cf2edb65SMykola Nikishov /** 37cf2edb65SMykola Nikishov * Draws a commit graph in a JTable. 38cf2edb65SMykola Nikishov * <p> 39cf2edb65SMykola Nikishov * This class is currently a very primitive commit visualization tool. It shows 40cf2edb65SMykola Nikishov * a table of 3 columns: 41cf2edb65SMykola Nikishov * <ol> 42cf2edb65SMykola Nikishov * <li>Commit graph and short message</li> 43cf2edb65SMykola Nikishov * <li>Author name and email address</li> 44cf2edb65SMykola Nikishov * <li>Author date and time</li> 4532ff57a2SRobin Rosenberg * </ol> 46cf2edb65SMykola Nikishov */ 47cf2edb65SMykola Nikishov public class CommitGraphPane extends JTable { 48cf2edb65SMykola Nikishov private static final long serialVersionUID = 1L; 49cf2edb65SMykola Nikishov 50cf2edb65SMykola Nikishov private final SwingCommitList allCommits; 51cf2edb65SMykola Nikishov 526ac27ee2SMatthias Sohn /** 536ac27ee2SMatthias Sohn * Create a new empty panel. 546ac27ee2SMatthias Sohn */ CommitGraphPane()55cf2edb65SMykola Nikishov public CommitGraphPane() { 56cf2edb65SMykola Nikishov allCommits = new SwingCommitList(); 57cf2edb65SMykola Nikishov configureHeader(); 58cf2edb65SMykola Nikishov setShowHorizontalLines(false); 59cf2edb65SMykola Nikishov setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 60cf2edb65SMykola Nikishov configureRowHeight(); 61cf2edb65SMykola Nikishov } 62cf2edb65SMykola Nikishov configureRowHeight()63cf2edb65SMykola Nikishov private void configureRowHeight() { 64cf2edb65SMykola Nikishov int h = 0; 65cf2edb65SMykola Nikishov for (int i = 0; i<getColumnCount(); ++i) { 66cf2edb65SMykola Nikishov TableCellRenderer renderer = getDefaultRenderer(getColumnClass(i)); 67c310fa0cSRobin Rosenberg Component c = renderer.getTableCellRendererComponent(this, 68c310fa0cSRobin Rosenberg "ÅOj", false, false, 0, i); //$NON-NLS-1$ 69cf2edb65SMykola Nikishov h = Math.max(h, c.getPreferredSize().height); 70cf2edb65SMykola Nikishov } 71cf2edb65SMykola Nikishov setRowHeight(h + getRowMargin()); 72cf2edb65SMykola Nikishov } 73cf2edb65SMykola Nikishov 74cf2edb65SMykola Nikishov /** 75cf2edb65SMykola Nikishov * Get the commit list this pane renders from. 76cf2edb65SMykola Nikishov * 77cf2edb65SMykola Nikishov * @return the list the caller must populate. 78cf2edb65SMykola Nikishov */ getCommitList()79cf2edb65SMykola Nikishov public PlotCommitList getCommitList() { 80cf2edb65SMykola Nikishov return allCommits; 81cf2edb65SMykola Nikishov } 82cf2edb65SMykola Nikishov 836ac27ee2SMatthias Sohn /** {@inheritDoc} */ 84cf2edb65SMykola Nikishov @Override setModel(TableModel dataModel)856d370d83SHan-Wen Nienhuys public void setModel(TableModel dataModel) { 86cf2edb65SMykola Nikishov if (dataModel != null && !(dataModel instanceof CommitTableModel)) 87f3d8a8ecSSasa Zivkov throw new ClassCastException(UIText.get().mustBeSpecialTableModel); 88cf2edb65SMykola Nikishov super.setModel(dataModel); 89cf2edb65SMykola Nikishov } 90cf2edb65SMykola Nikishov 916ac27ee2SMatthias Sohn /** {@inheritDoc} */ 92cf2edb65SMykola Nikishov @Override createDefaultDataModel()93cf2edb65SMykola Nikishov protected TableModel createDefaultDataModel() { 94cf2edb65SMykola Nikishov return new CommitTableModel(); 95cf2edb65SMykola Nikishov } 96cf2edb65SMykola Nikishov configureHeader()97cf2edb65SMykola Nikishov private void configureHeader() { 98cf2edb65SMykola Nikishov final JTableHeader th = getTableHeader(); 99cf2edb65SMykola Nikishov final TableColumnModel cols = th.getColumnModel(); 100cf2edb65SMykola Nikishov 101cf2edb65SMykola Nikishov final TableColumn graph = cols.getColumn(0); 102cf2edb65SMykola Nikishov final TableColumn author = cols.getColumn(1); 103cf2edb65SMykola Nikishov final TableColumn date = cols.getColumn(2); 104cf2edb65SMykola Nikishov 105c310fa0cSRobin Rosenberg graph.setHeaderValue(""); //$NON-NLS-1$ 106f3d8a8ecSSasa Zivkov author.setHeaderValue(UIText.get().author); 107f3d8a8ecSSasa Zivkov date.setHeaderValue(UIText.get().date); 108cf2edb65SMykola Nikishov 109cf2edb65SMykola Nikishov graph.setCellRenderer(new GraphCellRender()); 110cf2edb65SMykola Nikishov author.setCellRenderer(new NameCellRender()); 111cf2edb65SMykola Nikishov date.setCellRenderer(new DateCellRender()); 112cf2edb65SMykola Nikishov } 113cf2edb65SMykola Nikishov 114cf2edb65SMykola Nikishov class CommitTableModel extends AbstractTableModel { 115cf2edb65SMykola Nikishov private static final long serialVersionUID = 1L; 116cf2edb65SMykola Nikishov 117cf2edb65SMykola Nikishov PlotCommit<SwingLane> lastCommit; 118cf2edb65SMykola Nikishov 119cf2edb65SMykola Nikishov PersonIdent lastAuthor; 120cf2edb65SMykola Nikishov 1217ac182f4SDavid Pursehouse @Override getColumnCount()122cf2edb65SMykola Nikishov public int getColumnCount() { 123cf2edb65SMykola Nikishov return 3; 124cf2edb65SMykola Nikishov } 125cf2edb65SMykola Nikishov 1267ac182f4SDavid Pursehouse @Override getRowCount()127cf2edb65SMykola Nikishov public int getRowCount() { 128cf2edb65SMykola Nikishov return allCommits != null ? allCommits.size() : 0; 129cf2edb65SMykola Nikishov } 130cf2edb65SMykola Nikishov 1317ac182f4SDavid Pursehouse @Override getValueAt(int rowIndex, int columnIndex)1326d370d83SHan-Wen Nienhuys public Object getValueAt(int rowIndex, int columnIndex) { 133cf2edb65SMykola Nikishov final PlotCommit<SwingLane> c = allCommits.get(rowIndex); 134cf2edb65SMykola Nikishov switch (columnIndex) { 135cf2edb65SMykola Nikishov case 0: 136cf2edb65SMykola Nikishov return c; 137cf2edb65SMykola Nikishov case 1: 138cf2edb65SMykola Nikishov return authorFor(c); 139cf2edb65SMykola Nikishov case 2: 140cf2edb65SMykola Nikishov return authorFor(c); 141cf2edb65SMykola Nikishov default: 142cf2edb65SMykola Nikishov return null; 143cf2edb65SMykola Nikishov } 144cf2edb65SMykola Nikishov } 145cf2edb65SMykola Nikishov authorFor(PlotCommit<SwingLane> c)146f3ec7cf3SHan-Wen Nienhuys PersonIdent authorFor(PlotCommit<SwingLane> c) { 14766cb2d9dSMatthias Sohn if (!References.isSameObject(c, lastCommit)) { 148cf2edb65SMykola Nikishov lastCommit = c; 149cf2edb65SMykola Nikishov lastAuthor = c.getAuthorIdent(); 150cf2edb65SMykola Nikishov } 151cf2edb65SMykola Nikishov return lastAuthor; 152cf2edb65SMykola Nikishov } 153cf2edb65SMykola Nikishov } 154cf2edb65SMykola Nikishov 1553a899a3aSRobin Rosenberg static class NameCellRender extends DefaultTableCellRenderer { 156cf2edb65SMykola Nikishov private static final long serialVersionUID = 1L; 157cf2edb65SMykola Nikishov 1587ac182f4SDavid Pursehouse @Override getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)159cf2edb65SMykola Nikishov public Component getTableCellRendererComponent(final JTable table, 160cf2edb65SMykola Nikishov final Object value, final boolean isSelected, 161cf2edb65SMykola Nikishov final boolean hasFocus, final int row, final int column) { 162cf2edb65SMykola Nikishov final PersonIdent pi = (PersonIdent) value; 163cf2edb65SMykola Nikishov 164cf2edb65SMykola Nikishov final String valueStr; 165cf2edb65SMykola Nikishov if (pi != null) 166c310fa0cSRobin Rosenberg valueStr = pi.getName() + " <" + pi.getEmailAddress() + ">"; //$NON-NLS-1$ //$NON-NLS-2$ 167cf2edb65SMykola Nikishov else 168c310fa0cSRobin Rosenberg valueStr = ""; //$NON-NLS-1$ 169cf2edb65SMykola Nikishov return super.getTableCellRendererComponent(table, valueStr, 170cf2edb65SMykola Nikishov isSelected, hasFocus, row, column); 171cf2edb65SMykola Nikishov } 172cf2edb65SMykola Nikishov } 173cf2edb65SMykola Nikishov 1743a899a3aSRobin Rosenberg static class DateCellRender extends DefaultTableCellRenderer { 175cf2edb65SMykola Nikishov private static final long serialVersionUID = 1L; 176cf2edb65SMykola Nikishov 177cf2edb65SMykola Nikishov private final DateFormat fmt = new SimpleDateFormat( 178c310fa0cSRobin Rosenberg "yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ 179cf2edb65SMykola Nikishov 1807ac182f4SDavid Pursehouse @Override getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)181cf2edb65SMykola Nikishov public Component getTableCellRendererComponent(final JTable table, 182cf2edb65SMykola Nikishov final Object value, final boolean isSelected, 183cf2edb65SMykola Nikishov final boolean hasFocus, final int row, final int column) { 184cf2edb65SMykola Nikishov final PersonIdent pi = (PersonIdent) value; 185cf2edb65SMykola Nikishov 186cf2edb65SMykola Nikishov final String valueStr; 187cf2edb65SMykola Nikishov if (pi != null) 188cf2edb65SMykola Nikishov valueStr = fmt.format(pi.getWhen()); 189cf2edb65SMykola Nikishov else 190c310fa0cSRobin Rosenberg valueStr = ""; //$NON-NLS-1$ 191cf2edb65SMykola Nikishov return super.getTableCellRendererComponent(table, valueStr, 192cf2edb65SMykola Nikishov isSelected, hasFocus, row, column); 193cf2edb65SMykola Nikishov } 194cf2edb65SMykola Nikishov } 195cf2edb65SMykola Nikishov 1963a899a3aSRobin Rosenberg static class GraphCellRender extends DefaultTableCellRenderer { 197cf2edb65SMykola Nikishov private static final long serialVersionUID = 1L; 198cf2edb65SMykola Nikishov 199cf2edb65SMykola Nikishov private final AWTPlotRenderer renderer = new AWTPlotRenderer(this); 200cf2edb65SMykola Nikishov 201cf2edb65SMykola Nikishov PlotCommit<SwingLane> commit; 202cf2edb65SMykola Nikishov 2037ac182f4SDavid Pursehouse @Override 204258f516aSRobin Stocker @SuppressWarnings("unchecked") getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column)205cf2edb65SMykola Nikishov public Component getTableCellRendererComponent(final JTable table, 206cf2edb65SMykola Nikishov final Object value, final boolean isSelected, 207cf2edb65SMykola Nikishov final boolean hasFocus, final int row, final int column) { 208cf2edb65SMykola Nikishov super.getTableCellRendererComponent(table, value, isSelected, 209cf2edb65SMykola Nikishov hasFocus, row, column); 210cf2edb65SMykola Nikishov commit = (PlotCommit<SwingLane>) value; 211cf2edb65SMykola Nikishov return this; 212cf2edb65SMykola Nikishov } 213cf2edb65SMykola Nikishov 214cf2edb65SMykola Nikishov @Override paintComponent(Graphics inputGraphics)2156d370d83SHan-Wen Nienhuys protected void paintComponent(Graphics inputGraphics) { 216cf2edb65SMykola Nikishov if (inputGraphics == null) 217cf2edb65SMykola Nikishov return; 218cf2edb65SMykola Nikishov renderer.paint(inputGraphics, commit); 219cf2edb65SMykola Nikishov } 220cf2edb65SMykola Nikishov } 221cf2edb65SMykola Nikishov 222cf2edb65SMykola Nikishov static final Stroke[] strokeCache; 223cf2edb65SMykola Nikishov 224cf2edb65SMykola Nikishov static { 225cf2edb65SMykola Nikishov strokeCache = new Stroke[4]; 226cf2edb65SMykola Nikishov for (int i = 1; i < strokeCache.length; i++) 227cf2edb65SMykola Nikishov strokeCache[i] = new BasicStroke(i); 228cf2edb65SMykola Nikishov } 229cf2edb65SMykola Nikishov stroke(int width)230f3ec7cf3SHan-Wen Nienhuys static Stroke stroke(int width) { 231cf2edb65SMykola Nikishov if (width < strokeCache.length) 232cf2edb65SMykola Nikishov return strokeCache[width]; 233cf2edb65SMykola Nikishov return new BasicStroke(width); 234cf2edb65SMykola Nikishov } 235cf2edb65SMykola Nikishov 236cf2edb65SMykola Nikishov } 237