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 }