source: josm/trunk/src/org/openstreetmap/josm/gui/history/VersionTable.java@ 9246

Last change on this file since 9246 was 9223, checked in by Don-vip, 8 years ago

fix #12259 - adjust colors contrast in relation editor and history dialog

  • Property svn:eol-style set to native
File size: 12.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.history;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Dimension;
8import java.awt.Point;
9import java.awt.Rectangle;
10import java.awt.event.ActionEvent;
11import java.awt.event.ItemEvent;
12import java.awt.event.ItemListener;
13import java.awt.event.KeyAdapter;
14import java.awt.event.KeyEvent;
15import java.awt.event.MouseEvent;
16import java.util.Observable;
17import java.util.Observer;
18
19import javax.swing.DefaultCellEditor;
20import javax.swing.JCheckBox;
21import javax.swing.JLabel;
22import javax.swing.JPopupMenu;
23import javax.swing.JRadioButton;
24import javax.swing.JTable;
25import javax.swing.SwingConstants;
26import javax.swing.UIManager;
27import javax.swing.event.TableModelEvent;
28import javax.swing.event.TableModelListener;
29import javax.swing.table.TableCellRenderer;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.actions.AbstractInfoAction;
33import org.openstreetmap.josm.data.osm.User;
34import org.openstreetmap.josm.data.osm.history.History;
35import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
36import org.openstreetmap.josm.gui.util.GuiHelper;
37import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
38import org.openstreetmap.josm.io.XmlWriter;
39import org.openstreetmap.josm.tools.ImageProvider;
40import org.openstreetmap.josm.tools.OpenBrowser;
41
42/**
43 * VersionTable shows a list of version in a {@link org.openstreetmap.josm.data.osm.history.History}
44 * of an {@link org.openstreetmap.josm.data.osm.OsmPrimitive}.
45 * @since 1709
46 */
47public class VersionTable extends JTable implements Observer {
48 private VersionTablePopupMenu popupMenu;
49 private final transient HistoryBrowserModel model;
50
51 /**
52 * Constructs a new {@code VersionTable}.
53 * @param model model used by the history browser
54 */
55 public VersionTable(HistoryBrowserModel model) {
56 super(model.getVersionTableModel(), new VersionTableColumnModel());
57 model.addObserver(this);
58 build();
59 this.model = model;
60 }
61
62 protected void build() {
63 getTableHeader().setFont(getTableHeader().getFont().deriveFont(9f));
64 setRowSelectionAllowed(false);
65 setShowGrid(false);
66 setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
67 GuiHelper.setBackgroundReadable(this, UIManager.getColor("Button.background"));
68 setIntercellSpacing(new Dimension(6, 0));
69 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
70 popupMenu = new VersionTablePopupMenu();
71 addMouseListener(new MouseListener());
72 addKeyListener(new KeyAdapter() {
73 @Override
74 public void keyReleased(KeyEvent e) {
75 // navigate history down/up using the corresponding arrow keys.
76 long ref = model.getReferencePointInTime().getVersion();
77 long cur = model.getCurrentPointInTime().getVersion();
78 if (e.getKeyCode() == KeyEvent.VK_DOWN) {
79 History refNext = model.getHistory().from(ref);
80 History curNext = model.getHistory().from(cur);
81 if (refNext.getNumVersions() > 1 && curNext.getNumVersions() > 1) {
82 model.setReferencePointInTime(refNext.sortAscending().get(1));
83 model.setCurrentPointInTime(curNext.sortAscending().get(1));
84 }
85 } else if (e.getKeyCode() == KeyEvent.VK_UP) {
86 History refNext = model.getHistory().until(ref);
87 History curNext = model.getHistory().until(cur);
88 if (refNext.getNumVersions() > 1 && curNext.getNumVersions() > 1) {
89 model.setReferencePointInTime(refNext.sortDescending().get(1));
90 model.setCurrentPointInTime(curNext.sortDescending().get(1));
91 }
92 }
93 }
94 });
95 getModel().addTableModelListener(new TableModelListener() {
96 @Override
97 public void tableChanged(TableModelEvent e) {
98 adjustColumnWidth(VersionTable.this, 0, 0);
99 adjustColumnWidth(VersionTable.this, 1, -8);
100 adjustColumnWidth(VersionTable.this, 2, -8);
101 adjustColumnWidth(VersionTable.this, 3, 0);
102 adjustColumnWidth(VersionTable.this, 4, 0);
103 }
104 });
105 }
106
107 // some kind of hack to prevent the table from scrolling to the
108 // right when clicking on the cells
109 @Override
110 public void scrollRectToVisible(Rectangle aRect) {
111 super.scrollRectToVisible(new Rectangle(0, aRect.y, aRect.width, aRect.height));
112 }
113
114 protected HistoryBrowserModel.VersionTableModel getVersionTableModel() {
115 return (HistoryBrowserModel.VersionTableModel) getModel();
116 }
117
118 @Override
119 public void update(Observable o, Object arg) {
120 repaint();
121 }
122
123 final class MouseListener extends PopupMenuLauncher {
124 private MouseListener() {
125 super(popupMenu);
126 }
127
128 @Override
129 public void mousePressed(MouseEvent e) {
130 super.mousePressed(e);
131 if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) {
132 int row = rowAtPoint(e.getPoint());
133 int col = columnAtPoint(e.getPoint());
134 if (row >= 0 && (col == VersionTableColumnModel.COL_DATE || col == VersionTableColumnModel.COL_USER)) {
135 model.getVersionTableModel().setCurrentPointInTime(row);
136 model.getVersionTableModel().setReferencePointInTime(Math.max(0, row - 1));
137 }
138 }
139 }
140
141 @Override
142 protected int checkTableSelection(JTable table, Point p) {
143 HistoryBrowserModel.VersionTableModel tableModel = getVersionTableModel();
144 int row = rowAtPoint(p);
145 if (row > -1 && !tableModel.isLatest(row)) {
146 popupMenu.prepare(tableModel.getPrimitive(row));
147 }
148 return row;
149 }
150 }
151
152 static class ChangesetInfoAction extends AbstractInfoAction {
153 private transient HistoryOsmPrimitive primitive;
154
155 /**
156 * Constructs a new {@code ChangesetInfoAction}.
157 */
158 ChangesetInfoAction() {
159 super(true);
160 putValue(NAME, tr("Changeset info"));
161 putValue(SHORT_DESCRIPTION, tr("Launch browser with information about the changeset"));
162 putValue(SMALL_ICON, ImageProvider.get("data/changeset"));
163 }
164
165 @Override
166 protected String createInfoUrl(Object infoObject) {
167 if (infoObject instanceof HistoryOsmPrimitive) {
168 HistoryOsmPrimitive prim = (HistoryOsmPrimitive) infoObject;
169 return Main.getBaseBrowseUrl() + "/changeset/" + prim.getChangesetId();
170 } else {
171 return null;
172 }
173 }
174
175 @Override
176 public void actionPerformed(ActionEvent e) {
177 if (!isEnabled())
178 return;
179 String url = createInfoUrl(primitive);
180 OpenBrowser.displayUrl(url);
181 }
182
183 public void prepare(HistoryOsmPrimitive primitive) {
184 putValue(NAME, tr("Show changeset {0}", primitive.getChangesetId()));
185 this.primitive = primitive;
186 }
187 }
188
189 static class UserInfoAction extends AbstractInfoAction {
190 private transient HistoryOsmPrimitive primitive;
191
192 /**
193 * Constructs a new {@code UserInfoAction}.
194 */
195 UserInfoAction() {
196 super(true);
197 putValue(NAME, tr("User info"));
198 putValue(SHORT_DESCRIPTION, tr("Launch browser with information about the user"));
199 putValue(SMALL_ICON, ImageProvider.get("data/user"));
200 }
201
202 @Override
203 protected String createInfoUrl(Object infoObject) {
204 if (infoObject instanceof HistoryOsmPrimitive) {
205 HistoryOsmPrimitive hp = (HistoryOsmPrimitive) infoObject;
206 return hp.getUser() == null ? null : Main.getBaseUserUrl() + '/' + hp.getUser().getName();
207 } else {
208 return null;
209 }
210 }
211
212 @Override
213 public void actionPerformed(ActionEvent e) {
214 if (!isEnabled())
215 return;
216 String url = createInfoUrl(primitive);
217 OpenBrowser.displayUrl(url);
218 }
219
220 public void prepare(HistoryOsmPrimitive primitive) {
221 final User user = primitive.getUser();
222 putValue(NAME, "<html>" + tr("Show user {0}", user == null ? "?" :
223 XmlWriter.encode(user.getName(), true) + " <font color=gray>(" + user.getId() + ")</font>") + "</html>");
224 this.primitive = primitive;
225 }
226 }
227
228 static class VersionTablePopupMenu extends JPopupMenu {
229
230 private ChangesetInfoAction changesetInfoAction;
231 private UserInfoAction userInfoAction;
232
233 /**
234 * Constructs a new {@code VersionTablePopupMenu}.
235 */
236 VersionTablePopupMenu() {
237 super();
238 build();
239 }
240
241 protected void build() {
242 changesetInfoAction = new ChangesetInfoAction();
243 add(changesetInfoAction);
244 userInfoAction = new UserInfoAction();
245 add(userInfoAction);
246 }
247
248 public void prepare(HistoryOsmPrimitive primitive) {
249 changesetInfoAction.prepare(primitive);
250 userInfoAction.prepare(primitive);
251 invalidate();
252 }
253 }
254
255 /**
256 * Renderer for history radio buttons in columns A and B.
257 */
258 public static class RadioButtonRenderer extends JRadioButton implements TableCellRenderer {
259
260 @Override
261 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
262 int row, int column) {
263 setSelected(value != null && (Boolean) value);
264 setHorizontalAlignment(SwingConstants.CENTER);
265 return this;
266 }
267 }
268
269 /**
270 * Editor for history radio buttons in columns A and B.
271 */
272 public static class RadioButtonEditor extends DefaultCellEditor implements ItemListener {
273
274 private final JRadioButton btn;
275
276 /**
277 * Constructs a new {@code RadioButtonEditor}.
278 */
279 public RadioButtonEditor() {
280 super(new JCheckBox());
281 btn = new JRadioButton();
282 btn.setHorizontalAlignment(SwingConstants.CENTER);
283 }
284
285 @Override
286 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
287 if (value == null)
288 return null;
289 boolean val = (Boolean) value;
290 btn.setSelected(val);
291 btn.addItemListener(this);
292 return btn;
293 }
294
295 @Override
296 public Object getCellEditorValue() {
297 btn.removeItemListener(this);
298 return btn.isSelected();
299 }
300
301 @Override
302 public void itemStateChanged(ItemEvent e) {
303 fireEditingStopped();
304 }
305 }
306
307 /**
308 * Renderer for history version labels, allowing to define horizontal alignment.
309 */
310 public static class AlignedRenderer extends JLabel implements TableCellRenderer {
311
312 /**
313 * Constructs a new {@code AlignedRenderer}.
314 * @param hAlignment Horizontal alignement. One of the following constants defined in SwingConstants:
315 * LEFT, CENTER (the default for image-only labels), RIGHT, LEADING (the default for text-only labels) or TRAILING
316 */
317 public AlignedRenderer(int hAlignment) {
318 setHorizontalAlignment(hAlignment);
319 }
320
321 @Override
322 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
323 int row, int column) {
324 String v = "";
325 if (value != null) {
326 v = value.toString();
327 }
328 setText(v);
329 return this;
330 }
331 }
332
333 private static void adjustColumnWidth(JTable tbl, int col, int cellInset) {
334 int maxwidth = 0;
335
336 for (int row = 0; row < tbl.getRowCount(); row++) {
337 TableCellRenderer tcr = tbl.getCellRenderer(row, col);
338 Object val = tbl.getValueAt(row, col);
339 Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col);
340 maxwidth = Math.max(comp.getPreferredSize().width + cellInset, maxwidth);
341 }
342 TableCellRenderer tcr = tbl.getTableHeader().getDefaultRenderer();
343 Object val = tbl.getColumnModel().getColumn(col).getHeaderValue();
344 Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, -1, col);
345 maxwidth = Math.max(comp.getPreferredSize().width + Main.pref.getInteger("table.header-inset", 0), maxwidth);
346
347 int spacing = tbl.getIntercellSpacing().width;
348 tbl.getColumnModel().getColumn(col).setPreferredWidth(maxwidth + spacing);
349 }
350}
Note: See TracBrowser for help on using the repository browser.