001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.history; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.Dimension; 008import java.awt.Point; 009import java.awt.Rectangle; 010import java.awt.event.ActionEvent; 011import java.awt.event.ItemEvent; 012import java.awt.event.ItemListener; 013import java.awt.event.MouseEvent; 014import java.util.Observable; 015import java.util.Observer; 016 017import javax.swing.DefaultCellEditor; 018import javax.swing.JCheckBox; 019import javax.swing.JLabel; 020import javax.swing.JPopupMenu; 021import javax.swing.JRadioButton; 022import javax.swing.JTable; 023import javax.swing.SwingConstants; 024import javax.swing.UIManager; 025import javax.swing.event.TableModelEvent; 026import javax.swing.event.TableModelListener; 027import javax.swing.table.TableCellRenderer; 028 029import org.openstreetmap.josm.Main; 030import org.openstreetmap.josm.actions.AbstractInfoAction; 031import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive; 032import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 033import org.openstreetmap.josm.tools.ImageProvider; 034 035/** 036 * VersionTable shows a list of version in a {@link org.openstreetmap.josm.data.osm.history.History} 037 * of an {@link org.openstreetmap.josm.data.osm.OsmPrimitive}. 038 * 039 */ 040public class VersionTable extends JTable implements Observer{ 041 private VersionTablePopupMenu popupMenu; 042 private final HistoryBrowserModel model; 043 044 protected void build() { 045 getTableHeader().setFont(getTableHeader().getFont().deriveFont(9f)); 046 setRowSelectionAllowed(false); 047 setShowGrid(false); 048 setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 049 setBackground(UIManager.getColor("Button.background")); 050 setIntercellSpacing(new Dimension(6, 0)); 051 putClientProperty("terminateEditOnFocusLost", true); 052 popupMenu = new VersionTablePopupMenu(); 053 addMouseListener(new MouseListener()); 054 getModel().addTableModelListener(new TableModelListener() { 055 @Override 056 public void tableChanged(TableModelEvent e) { 057 adjustColumnWidth(VersionTable.this, 0, 0); 058 adjustColumnWidth(VersionTable.this, 1, -8); 059 adjustColumnWidth(VersionTable.this, 2, -8); 060 adjustColumnWidth(VersionTable.this, 3, 0); 061 adjustColumnWidth(VersionTable.this, 4, 0); 062 } 063 }); 064 } 065 066 public VersionTable(HistoryBrowserModel model) { 067 super(model.getVersionTableModel(), new VersionTableColumnModel()); 068 model.addObserver(this); 069 build(); 070 this.model = model; 071 } 072 073 // some kind of hack to prevent the table from scrolling to the 074 // right when clicking on the cells 075 @Override 076 public void scrollRectToVisible(Rectangle aRect) { 077 super.scrollRectToVisible(new Rectangle(0, aRect.y, aRect.width, aRect.height)); 078 } 079 080 protected HistoryBrowserModel.VersionTableModel getVersionTableModel() { 081 return (HistoryBrowserModel.VersionTableModel) getModel(); 082 } 083 084 @Override 085 public void update(Observable o, Object arg) { 086 repaint(); 087 } 088 089 class MouseListener extends PopupMenuLauncher { 090 public MouseListener() { 091 super(popupMenu); 092 } 093 @Override 094 public void mousePressed(MouseEvent e) { 095 super.mousePressed(e); 096 if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) { 097 int row = rowAtPoint(e.getPoint()); 098 int col = columnAtPoint(e.getPoint()); 099 if (row > 0 && (col == VersionTableColumnModel.COL_DATE || col == VersionTableColumnModel.COL_USER)) { 100 model.getVersionTableModel().setCurrentPointInTime(row); 101 model.getVersionTableModel().setReferencePointInTime(row - 1); 102 } 103 } 104 } 105 @Override 106 protected int checkTableSelection(JTable table, Point p) { 107 HistoryBrowserModel.VersionTableModel model = getVersionTableModel(); 108 int row = rowAtPoint(p); 109 if (row > -1 && !model.isLatest(row)) { 110 popupMenu.prepare(model.getPrimitive(row)); 111 } 112 return row; 113 } 114 } 115 116 static class ChangesetInfoAction extends AbstractInfoAction { 117 private HistoryOsmPrimitive primitive; 118 119 public ChangesetInfoAction() { 120 super(true); 121 putValue(NAME, tr("Changeset info")); 122 putValue(SHORT_DESCRIPTION, tr("Launch browser with information about the changeset")); 123 putValue(SMALL_ICON, ImageProvider.get("about")); 124 } 125 126 @Override 127 protected String createInfoUrl(Object infoObject) { 128 HistoryOsmPrimitive primitive = (HistoryOsmPrimitive) infoObject; 129 return getBaseBrowseUrl() + "/changeset/" + primitive.getChangesetId(); 130 } 131 132 @Override 133 public void actionPerformed(ActionEvent e) { 134 if (!isEnabled()) 135 return; 136 String url = createInfoUrl(primitive); 137 launchBrowser(url); 138 } 139 140 public void prepare(HistoryOsmPrimitive primitive) { 141 putValue(NAME, tr("Show changeset {0}", primitive.getChangesetId())); 142 this.primitive = primitive; 143 } 144 } 145 146 static class VersionTablePopupMenu extends JPopupMenu { 147 148 private ChangesetInfoAction changesetInfoAction; 149 150 protected void build() { 151 changesetInfoAction = new ChangesetInfoAction(); 152 add(changesetInfoAction); 153 } 154 public VersionTablePopupMenu() { 155 super(); 156 build(); 157 } 158 159 public void prepare(HistoryOsmPrimitive primitive) { 160 changesetInfoAction.prepare(primitive); 161 invalidate(); 162 } 163 } 164 165 public static class RadioButtonRenderer extends JRadioButton implements TableCellRenderer { 166 167 @Override 168 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) { 169 setSelected(value != null && (Boolean)value); 170 setHorizontalAlignment(SwingConstants.CENTER); 171 return this; 172 } 173 } 174 175 public static class RadioButtonEditor extends DefaultCellEditor implements ItemListener { 176 177 private JRadioButton btn; 178 179 public RadioButtonEditor() { 180 super(new JCheckBox()); 181 btn = new JRadioButton(); 182 btn.setHorizontalAlignment(SwingConstants.CENTER); 183 } 184 185 @Override 186 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 187 if (value == null) return null; 188 boolean val = (Boolean) value; 189 btn.setSelected(val); 190 btn.addItemListener(this); 191 return btn; 192 } 193 194 @Override 195 public Object getCellEditorValue() { 196 btn.removeItemListener(this); 197 return btn.isSelected(); 198 } 199 200 @Override 201 public void itemStateChanged(ItemEvent e) { 202 fireEditingStopped(); 203 } 204 } 205 206 public static class AlignedRenderer extends JLabel implements TableCellRenderer { 207 public AlignedRenderer(int hAlignment) { 208 setHorizontalAlignment(hAlignment); 209 } 210 @Override 211 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) { 212 String v = value.toString(); 213 setText(v); 214 return this; 215 } 216 } 217 218 private static void adjustColumnWidth(JTable tbl, int col, int cellInset) { 219 int maxwidth = 0; 220 221 for (int row=0; row<tbl.getRowCount(); row++) { 222 TableCellRenderer tcr = tbl.getCellRenderer(row, col); 223 Object val = tbl.getValueAt(row, col); 224 Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col); 225 maxwidth = Math.max(comp.getPreferredSize().width + cellInset, maxwidth); 226 } 227 TableCellRenderer tcr = tbl.getTableHeader().getDefaultRenderer(); 228 Object val = tbl.getColumnModel().getColumn(col).getHeaderValue(); 229 Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, -1, col); 230 maxwidth = Math.max(comp.getPreferredSize().width + Main.pref.getInteger("table.header-inset", 0), maxwidth); 231 232 int spacing = tbl.getIntercellSpacing().width; 233 tbl.getColumnModel().getColumn(col).setPreferredWidth(maxwidth + spacing); 234 } 235 236}