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 adjustColumnWidth(VersionTable.this, 5, 0); 061 } 062 }); 063 } 064 065 public VersionTable(HistoryBrowserModel model) { 066 super(model.getVersionTableModel(), new VersionTableColumnModel()); 067 model.addObserver(this); 068 build(); 069 } 070 071 // some kind of hack to prevent the table from scrolling to the 072 // right when clicking on the cells 073 @Override 074 public void scrollRectToVisible(Rectangle aRect) { 075 super.scrollRectToVisible(new Rectangle(0, aRect.y, aRect.width, aRect.height)); 076 } 077 078 protected HistoryBrowserModel.VersionTableModel getVersionTableModel() { 079 return (HistoryBrowserModel.VersionTableModel) getModel(); 080 } 081 082 public void update(Observable o, Object arg) { 083 repaint(); 084 } 085 086 protected void showPopupMenu(MouseEvent evt) { 087 HistoryBrowserModel.VersionTableModel model = getVersionTableModel(); 088 int row = rowAtPoint(evt.getPoint()); 089 if (row > -1 && !model.isLatest(row)) { 090 HistoryOsmPrimitive primitive = model.getPrimitive(row); 091 popupMenu.prepare(primitive); 092 popupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); 093 } 094 } 095 096 class PopupMenuTrigger extends MouseAdapter { 097 @Override 098 public void mousePressed(MouseEvent e) { 099 showPopup(e); 100 } 101 @Override 102 public void mouseReleased(MouseEvent e) { 103 showPopup(e); 104 } 105 private void showPopup(MouseEvent e) { 106 if (e.isPopupTrigger()) { 107 showPopupMenu(e); 108 } 109 } 110 } 111 112 static class ChangesetInfoAction extends AbstractInfoAction { 113 private HistoryOsmPrimitive primitive; 114 115 public ChangesetInfoAction() { 116 super(true); 117 putValue(NAME, tr("Changeset info")); 118 putValue(SHORT_DESCRIPTION, tr("Launch browser with information about the changeset")); 119 putValue(SMALL_ICON, ImageProvider.get("about")); 120 } 121 122 @Override 123 protected String createInfoUrl(Object infoObject) { 124 HistoryOsmPrimitive primitive = (HistoryOsmPrimitive) infoObject; 125 return getBaseBrowseUrl() + "/changeset/" + primitive.getChangesetId(); 126 } 127 128 @Override 129 public void actionPerformed(ActionEvent e) { 130 if (!isEnabled()) 131 return; 132 String url = createInfoUrl(primitive); 133 launchBrowser(url); 134 } 135 136 public void prepare(HistoryOsmPrimitive primitive) { 137 putValue(NAME, tr("Show changeset {0}", primitive.getChangesetId())); 138 this.primitive = primitive; 139 } 140 } 141 142 static class VersionTablePopupMenu extends JPopupMenu { 143 144 private ChangesetInfoAction changesetInfoAction; 145 146 protected void build() { 147 changesetInfoAction = new ChangesetInfoAction(); 148 add(changesetInfoAction); 149 } 150 public VersionTablePopupMenu() { 151 super(); 152 build(); 153 } 154 155 public void prepare(HistoryOsmPrimitive primitive) { 156 changesetInfoAction.prepare(primitive); 157 invalidate(); 158 } 159 } 160 161 public static class RadioButtonRenderer extends JRadioButton implements TableCellRenderer { 162 163 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) { 164 setSelected(value != null && (Boolean)value); 165 setHorizontalAlignment(SwingConstants.CENTER); 166 return this; 167 } 168 } 169 170 public static class RadioButtonEditor extends DefaultCellEditor implements ItemListener { 171 172 private JRadioButton btn; 173 174 public RadioButtonEditor() { 175 super(new JCheckBox()); 176 btn = new JRadioButton(); 177 btn.setHorizontalAlignment(SwingConstants.CENTER); 178 } 179 180 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 181 if (value == null) return null; 182 boolean val = (Boolean) value; 183 btn.setSelected(val); 184 btn.addItemListener(this); 185 return btn; 186 } 187 188 public Object getCellEditorValue() { 189 btn.removeItemListener(this); 190 return btn.isSelected(); 191 } 192 193 public void itemStateChanged(ItemEvent e) { 194 fireEditingStopped(); 195 } 196 } 197 198 public static class LabelRenderer implements TableCellRenderer { 199 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) { 200 return (Component) value; 201 } 202 } 203 204 public static class AlignedRenderer extends JLabel implements TableCellRenderer { 205 public AlignedRenderer(int hAlignment) { 206 setHorizontalAlignment(hAlignment); 207 } 208 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) { 209 String v = value.toString(); 210 setText(v); 211 return this; 212 } 213 } 214 215 private static void adjustColumnWidth(JTable tbl, int col, int cellInset) { 216 int maxwidth = 0; 217 218 for (int row=0; row<tbl.getRowCount(); row++) { 219 TableCellRenderer tcr = tbl.getCellRenderer(row, col); 220 Object val = tbl.getValueAt(row, col); 221 Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col); 222 maxwidth = Math.max(comp.getPreferredSize().width + cellInset, maxwidth); 223 } 224 TableCellRenderer tcr = tbl.getTableHeader().getDefaultRenderer(); 225 Object val = tbl.getColumnModel().getColumn(col).getHeaderValue(); 226 Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, -1, col); 227 maxwidth = Math.max(comp.getPreferredSize().width + Main.pref.getInteger("table.header-inset", 0), maxwidth); 228 229 int spacing = tbl.getIntercellSpacing().width; 230 tbl.getColumnModel().getColumn(col).setPreferredWidth(maxwidth + spacing); 231 } 232 233 }