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

Last change on this file since 12043 was 11646, checked in by Don-vip, 7 years ago

see #14175 - refactor HistoryBrowserModel

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