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