source: josm/trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java@ 11366

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

findbugs - SIC_INNER_SHOULD_BE_STATIC_ANON

  • Property svn:eol-style set to native
File size: 13.1 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.GridBagConstraints;
7import java.awt.GridBagLayout;
8import java.awt.Insets;
9import java.awt.Point;
10import java.awt.event.ActionEvent;
11import java.awt.event.MouseAdapter;
12import java.awt.event.MouseEvent;
13
14import javax.swing.AbstractAction;
15import javax.swing.JPanel;
16import javax.swing.JPopupMenu;
17import javax.swing.JScrollPane;
18import javax.swing.JTable;
19import javax.swing.ListSelectionModel;
20import javax.swing.event.TableModelEvent;
21import javax.swing.event.TableModelListener;
22import javax.swing.table.TableModel;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.actions.AutoScaleAction;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
28import org.openstreetmap.josm.data.osm.PrimitiveId;
29import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
30import org.openstreetmap.josm.data.osm.history.History;
31import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
32import org.openstreetmap.josm.gui.layer.OsmDataLayer;
33import org.openstreetmap.josm.gui.util.AdjustmentSynchronizer;
34import org.openstreetmap.josm.gui.util.GuiHelper;
35import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
36import org.openstreetmap.josm.tools.ImageProvider;
37
38/**
39 * NodeListViewer is a UI component which displays the node list of two
40 * version of a {@link OsmPrimitive} in a {@link History}.
41 *
42 * <ul>
43 * <li>on the left, it displays the node list for the version at {@link PointInTimeType#REFERENCE_POINT_IN_TIME}</li>
44 * <li>on the right, it displays the node list for the version at {@link PointInTimeType#CURRENT_POINT_IN_TIME}</li>
45 * </ul>
46 * @since 1709
47 */
48public class NodeListViewer extends JPanel {
49
50 private transient HistoryBrowserModel model;
51 private VersionInfoPanel referenceInfoPanel;
52 private VersionInfoPanel currentInfoPanel;
53 private transient AdjustmentSynchronizer adjustmentSynchronizer;
54 private transient SelectionSynchronizer selectionSynchronizer;
55 private NodeListPopupMenu popupMenu;
56
57 /**
58 * Constructs a new {@code NodeListViewer}.
59 * @param model history browser model
60 */
61 public NodeListViewer(HistoryBrowserModel model) {
62 setModel(model);
63 build();
64 }
65
66 protected JScrollPane embeddInScrollPane(JTable table) {
67 JScrollPane pane = new JScrollPane(table);
68 adjustmentSynchronizer.participateInSynchronizedScrolling(pane.getVerticalScrollBar());
69 return pane;
70 }
71
72 protected JTable buildReferenceNodeListTable() {
73 final DiffTableModel tableModel = model.getNodeListTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
74 final NodeListTableColumnModel columnModel = new NodeListTableColumnModel();
75 final JTable table = new JTable(tableModel, columnModel);
76 tableModel.addTableModelListener(new ReversedChangeListener(table, columnModel));
77 table.setName("table.referencenodelisttable");
78 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
79 selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
80 table.addMouseListener(new InternalPopupMenuLauncher());
81 table.addMouseListener(new DoubleClickAdapter(table));
82 return table;
83 }
84
85 protected JTable buildCurrentNodeListTable() {
86 final DiffTableModel tableModel = model.getNodeListTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
87 final NodeListTableColumnModel columnModel = new NodeListTableColumnModel();
88 final JTable table = new JTable(tableModel, columnModel);
89 tableModel.addTableModelListener(new ReversedChangeListener(table, columnModel));
90 table.setName("table.currentnodelisttable");
91 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
92 selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
93 table.addMouseListener(new InternalPopupMenuLauncher());
94 table.addMouseListener(new DoubleClickAdapter(table));
95 return table;
96 }
97
98 protected void build() {
99 setLayout(new GridBagLayout());
100 GridBagConstraints gc = new GridBagConstraints();
101
102 // ---------------------------
103 gc.gridx = 0;
104 gc.gridy = 0;
105 gc.gridwidth = 1;
106 gc.gridheight = 1;
107 gc.weightx = 0.5;
108 gc.weighty = 0.0;
109 gc.insets = new Insets(5, 5, 5, 0);
110 gc.fill = GridBagConstraints.HORIZONTAL;
111 gc.anchor = GridBagConstraints.FIRST_LINE_START;
112 referenceInfoPanel = new VersionInfoPanel(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
113 add(referenceInfoPanel, gc);
114
115 gc.gridx = 1;
116 gc.gridy = 0;
117 gc.gridwidth = 1;
118 gc.gridheight = 1;
119 gc.fill = GridBagConstraints.HORIZONTAL;
120 gc.weightx = 0.5;
121 gc.weighty = 0.0;
122 gc.anchor = GridBagConstraints.FIRST_LINE_START;
123 currentInfoPanel = new VersionInfoPanel(model, PointInTimeType.CURRENT_POINT_IN_TIME);
124 add(currentInfoPanel, gc);
125
126 adjustmentSynchronizer = new AdjustmentSynchronizer();
127 selectionSynchronizer = new SelectionSynchronizer();
128
129 popupMenu = new NodeListPopupMenu();
130
131 // ---------------------------
132 gc.gridx = 0;
133 gc.gridy = 1;
134 gc.gridwidth = 1;
135 gc.gridheight = 1;
136 gc.weightx = 0.5;
137 gc.weighty = 1.0;
138 gc.fill = GridBagConstraints.BOTH;
139 gc.anchor = GridBagConstraints.NORTHWEST;
140 add(embeddInScrollPane(buildReferenceNodeListTable()), gc);
141
142 gc.gridx = 1;
143 gc.gridy = 1;
144 gc.gridwidth = 1;
145 gc.gridheight = 1;
146 gc.weightx = 0.5;
147 gc.weighty = 1.0;
148 gc.fill = GridBagConstraints.BOTH;
149 gc.anchor = GridBagConstraints.NORTHWEST;
150 add(embeddInScrollPane(buildCurrentNodeListTable()), gc);
151 }
152
153 protected void unregisterAsChangeListener(HistoryBrowserModel model) {
154 if (currentInfoPanel != null) {
155 model.removeChangeListener(currentInfoPanel);
156 }
157 if (referenceInfoPanel != null) {
158 model.removeChangeListener(referenceInfoPanel);
159 }
160 }
161
162 protected void registerAsChangeListener(HistoryBrowserModel model) {
163 if (currentInfoPanel != null) {
164 model.addChangeListener(currentInfoPanel);
165 }
166 if (referenceInfoPanel != null) {
167 model.addChangeListener(referenceInfoPanel);
168 }
169 }
170
171 /**
172 * Sets the history browser model.
173 * @param model the history browser model
174 */
175 public void setModel(HistoryBrowserModel model) {
176 if (this.model != null) {
177 unregisterAsChangeListener(model);
178 }
179 this.model = model;
180 if (this.model != null) {
181 registerAsChangeListener(model);
182 }
183 }
184
185 static final class ReversedChangeListener implements TableModelListener {
186 private final NodeListTableColumnModel columnModel;
187 private final JTable table;
188 private Boolean reversed;
189 private final String nonReversedText;
190 private final String reversedText;
191
192 ReversedChangeListener(JTable table, NodeListTableColumnModel columnModel) {
193 this.columnModel = columnModel;
194 this.table = table;
195 nonReversedText = tr("Nodes") + (table.getFont().canDisplay('\u25bc') ? " \u25bc" : " (1-n)");
196 reversedText = tr("Nodes") + (table.getFont().canDisplay('\u25b2') ? " \u25b2" : " (n-1)");
197 }
198
199 @Override
200 public void tableChanged(TableModelEvent e) {
201 if (e.getSource() instanceof DiffTableModel) {
202 final DiffTableModel mod = (DiffTableModel) e.getSource();
203 if (reversed == null || reversed != mod.isReversed()) {
204 reversed = mod.isReversed();
205 columnModel.getColumn(0).setHeaderValue(reversed ? reversedText : nonReversedText);
206 table.getTableHeader().setToolTipText(
207 reversed ? tr("The nodes of this way are in reverse order") : null);
208 table.getTableHeader().repaint();
209 }
210 }
211 }
212 }
213
214 static class NodeListPopupMenu extends JPopupMenu {
215 private final ZoomToNodeAction zoomToNodeAction;
216 private final ShowHistoryAction showHistoryAction;
217
218 NodeListPopupMenu() {
219 zoomToNodeAction = new ZoomToNodeAction();
220 add(zoomToNodeAction);
221 showHistoryAction = new ShowHistoryAction();
222 add(showHistoryAction);
223 }
224
225 public void prepare(PrimitiveId pid) {
226 zoomToNodeAction.setPrimitiveId(pid);
227 zoomToNodeAction.updateEnabledState();
228
229 showHistoryAction.setPrimitiveId(pid);
230 showHistoryAction.updateEnabledState();
231 }
232 }
233
234 static class ZoomToNodeAction extends AbstractAction {
235 private transient PrimitiveId primitiveId;
236
237 /**
238 * Constructs a new {@code ZoomToNodeAction}.
239 */
240 ZoomToNodeAction() {
241 putValue(NAME, tr("Zoom to node"));
242 putValue(SHORT_DESCRIPTION, tr("Zoom to this node in the current data layer"));
243 putValue(SMALL_ICON, ImageProvider.get("dialogs", "zoomin"));
244 }
245
246 @Override
247 public void actionPerformed(ActionEvent e) {
248 if (!isEnabled())
249 return;
250 OsmPrimitive p = getPrimitiveToZoom();
251 if (p != null) {
252 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer();
253 if (editLayer != null) {
254 editLayer.data.setSelected(p.getPrimitiveId());
255 AutoScaleAction.autoScale("selection");
256 }
257 }
258 }
259
260 public void setPrimitiveId(PrimitiveId pid) {
261 this.primitiveId = pid;
262 updateEnabledState();
263 }
264
265 protected OsmPrimitive getPrimitiveToZoom() {
266 if (primitiveId == null)
267 return null;
268 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer();
269 if (editLayer == null)
270 return null;
271 return editLayer.data.getPrimitiveById(primitiveId);
272 }
273
274 public void updateEnabledState() {
275 setEnabled(Main.getLayerManager().getEditLayer() != null && getPrimitiveToZoom() != null);
276 }
277 }
278
279 static class ShowHistoryAction extends AbstractAction {
280 private transient PrimitiveId primitiveId;
281
282 /**
283 * Constructs a new {@code ShowHistoryAction}.
284 */
285 ShowHistoryAction() {
286 putValue(NAME, tr("Show history"));
287 putValue(SHORT_DESCRIPTION, tr("Open a history browser with the history of this node"));
288 putValue(SMALL_ICON, ImageProvider.get("dialogs", "history"));
289 }
290
291 @Override
292 public void actionPerformed(ActionEvent e) {
293 if (isEnabled()) {
294 run();
295 }
296 }
297
298 public void setPrimitiveId(PrimitiveId pid) {
299 this.primitiveId = pid;
300 updateEnabledState();
301 }
302
303 public void run() {
304 if (HistoryDataSet.getInstance().getHistory(primitiveId) == null) {
305 Main.worker.submit(new HistoryLoadTask().add(primitiveId));
306 }
307 Main.worker.submit(() -> {
308 final History h = HistoryDataSet.getInstance().getHistory(primitiveId);
309 if (h == null)
310 return;
311 GuiHelper.runInEDT(() -> HistoryBrowserDialogManager.getInstance().show(h));
312 });
313 }
314
315 public void updateEnabledState() {
316 setEnabled(primitiveId != null && !primitiveId.isNew());
317 }
318 }
319
320 private static PrimitiveId primitiveIdAtRow(TableModel model, int row) {
321 DiffTableModel castedModel = (DiffTableModel) model;
322 Long id = (Long) castedModel.getValueAt(row, 0).value;
323 return id == null ? null : new SimplePrimitiveId(id, OsmPrimitiveType.NODE);
324 }
325
326 class InternalPopupMenuLauncher extends PopupMenuLauncher {
327 InternalPopupMenuLauncher() {
328 super(popupMenu);
329 }
330
331 @Override
332 protected int checkTableSelection(JTable table, Point p) {
333 int row = super.checkTableSelection(table, p);
334 popupMenu.prepare(primitiveIdAtRow(table.getModel(), row));
335 return row;
336 }
337 }
338
339 static class DoubleClickAdapter extends MouseAdapter {
340 private final JTable table;
341 private final ShowHistoryAction showHistoryAction;
342
343 DoubleClickAdapter(JTable table) {
344 this.table = table;
345 showHistoryAction = new ShowHistoryAction();
346 }
347
348 @Override
349 public void mouseClicked(MouseEvent e) {
350 if (e.getClickCount() < 2)
351 return;
352 int row = table.rowAtPoint(e.getPoint());
353 if (row <= 0)
354 return;
355 PrimitiveId pid = primitiveIdAtRow(table.getModel(), row);
356 if (pid == null || pid.isNew())
357 return;
358 showHistoryAction.setPrimitiveId(pid);
359 showHistoryAction.run();
360 }
361 }
362}
Note: See TracBrowser for help on using the repository browser.