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

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

add more unit tests, javadoc, fix code style issues

  • Property svn:eol-style set to native
File size: 13.2 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 *
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(newReversedChangeListener(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(newReversedChangeListener(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 TableModelListener newReversedChangeListener(final JTable table, final NodeListTableColumnModel columnModel) {
99 return new TableModelListener() {
100 private Boolean reversed;
101 private final String nonReversedText = tr("Nodes") + (table.getFont().canDisplay('\u25bc') ? " \u25bc" : " (1-n)");
102 private final String reversedText = tr("Nodes") + (table.getFont().canDisplay('\u25b2') ? " \u25b2" : " (n-1)");
103
104 @Override
105 public void tableChanged(TableModelEvent e) {
106 if (e.getSource() instanceof DiffTableModel) {
107 final DiffTableModel mod = (DiffTableModel) e.getSource();
108 if (reversed == null || reversed != mod.isReversed()) {
109 reversed = mod.isReversed();
110 columnModel.getColumn(0).setHeaderValue(reversed ? reversedText : nonReversedText);
111 table.getTableHeader().setToolTipText(
112 reversed ? tr("The nodes of this way are in reverse order") : null);
113 table.getTableHeader().repaint();
114 }
115 }
116 }
117 };
118 }
119
120 protected void build() {
121 setLayout(new GridBagLayout());
122 GridBagConstraints gc = new GridBagConstraints();
123
124 // ---------------------------
125 gc.gridx = 0;
126 gc.gridy = 0;
127 gc.gridwidth = 1;
128 gc.gridheight = 1;
129 gc.weightx = 0.5;
130 gc.weighty = 0.0;
131 gc.insets = new Insets(5, 5, 5, 0);
132 gc.fill = GridBagConstraints.HORIZONTAL;
133 gc.anchor = GridBagConstraints.FIRST_LINE_START;
134 referenceInfoPanel = new VersionInfoPanel(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
135 add(referenceInfoPanel, gc);
136
137 gc.gridx = 1;
138 gc.gridy = 0;
139 gc.gridwidth = 1;
140 gc.gridheight = 1;
141 gc.fill = GridBagConstraints.HORIZONTAL;
142 gc.weightx = 0.5;
143 gc.weighty = 0.0;
144 gc.anchor = GridBagConstraints.FIRST_LINE_START;
145 currentInfoPanel = new VersionInfoPanel(model, PointInTimeType.CURRENT_POINT_IN_TIME);
146 add(currentInfoPanel, gc);
147
148 adjustmentSynchronizer = new AdjustmentSynchronizer();
149 selectionSynchronizer = new SelectionSynchronizer();
150
151 popupMenu = new NodeListPopupMenu();
152
153 // ---------------------------
154 gc.gridx = 0;
155 gc.gridy = 1;
156 gc.gridwidth = 1;
157 gc.gridheight = 1;
158 gc.weightx = 0.5;
159 gc.weighty = 1.0;
160 gc.fill = GridBagConstraints.BOTH;
161 gc.anchor = GridBagConstraints.NORTHWEST;
162 add(embeddInScrollPane(buildReferenceNodeListTable()), gc);
163
164 gc.gridx = 1;
165 gc.gridy = 1;
166 gc.gridwidth = 1;
167 gc.gridheight = 1;
168 gc.weightx = 0.5;
169 gc.weighty = 1.0;
170 gc.fill = GridBagConstraints.BOTH;
171 gc.anchor = GridBagConstraints.NORTHWEST;
172 add(embeddInScrollPane(buildCurrentNodeListTable()), gc);
173 }
174
175 protected void unregisterAsObserver(HistoryBrowserModel model) {
176 if (currentInfoPanel != null) {
177 model.deleteObserver(currentInfoPanel);
178 }
179 if (referenceInfoPanel != null) {
180 model.deleteObserver(referenceInfoPanel);
181 }
182 }
183
184 protected void registerAsObserver(HistoryBrowserModel model) {
185 if (currentInfoPanel != null) {
186 model.addObserver(currentInfoPanel);
187 }
188 if (referenceInfoPanel != null) {
189 model.addObserver(referenceInfoPanel);
190 }
191 }
192
193 /**
194 * Sets the history browser model.
195 * @param model the history browser model
196 */
197 public void setModel(HistoryBrowserModel model) {
198 if (this.model != null) {
199 unregisterAsObserver(model);
200 }
201 this.model = model;
202 if (this.model != null) {
203 registerAsObserver(model);
204 }
205 }
206
207 static class NodeListPopupMenu extends JPopupMenu {
208 private final ZoomToNodeAction zoomToNodeAction;
209 private final ShowHistoryAction showHistoryAction;
210
211 NodeListPopupMenu() {
212 zoomToNodeAction = new ZoomToNodeAction();
213 add(zoomToNodeAction);
214 showHistoryAction = new ShowHistoryAction();
215 add(showHistoryAction);
216 }
217
218 public void prepare(PrimitiveId pid) {
219 zoomToNodeAction.setPrimitiveId(pid);
220 zoomToNodeAction.updateEnabledState();
221
222 showHistoryAction.setPrimitiveId(pid);
223 showHistoryAction.updateEnabledState();
224 }
225 }
226
227 static class ZoomToNodeAction extends AbstractAction {
228 private transient PrimitiveId primitiveId;
229
230 /**
231 * Constructs a new {@code ZoomToNodeAction}.
232 */
233 ZoomToNodeAction() {
234 putValue(NAME, tr("Zoom to node"));
235 putValue(SHORT_DESCRIPTION, tr("Zoom to this node in the current data layer"));
236 putValue(SMALL_ICON, ImageProvider.get("dialogs", "zoomin"));
237 }
238
239 @Override
240 public void actionPerformed(ActionEvent e) {
241 if (!isEnabled())
242 return;
243 OsmPrimitive p = getPrimitiveToZoom();
244 if (p != null) {
245 OsmDataLayer editLayer = Main.main.getEditLayer();
246 if (editLayer != null) {
247 editLayer.data.setSelected(p.getPrimitiveId());
248 AutoScaleAction.autoScale("selection");
249 }
250 }
251 }
252
253 public void setPrimitiveId(PrimitiveId pid) {
254 this.primitiveId = pid;
255 updateEnabledState();
256 }
257
258 protected OsmPrimitive getPrimitiveToZoom() {
259 if (primitiveId == null)
260 return null;
261 OsmDataLayer editLayer = Main.main.getEditLayer();
262 if (editLayer == null)
263 return null;
264 return editLayer.data.getPrimitiveById(primitiveId);
265 }
266
267 public void updateEnabledState() {
268 if (!Main.main.hasEditLayer()) {
269 setEnabled(false);
270 return;
271 }
272 setEnabled(getPrimitiveToZoom() != null);
273 }
274 }
275
276 static class ShowHistoryAction extends AbstractAction {
277 private transient PrimitiveId primitiveId;
278
279 /**
280 * Constructs a new {@code ShowHistoryAction}.
281 */
282 ShowHistoryAction() {
283 putValue(NAME, tr("Show history"));
284 putValue(SHORT_DESCRIPTION, tr("Open a history browser with the history of this node"));
285 putValue(SMALL_ICON, ImageProvider.get("dialogs", "history"));
286 }
287
288 @Override
289 public void actionPerformed(ActionEvent e) {
290 if (isEnabled()) {
291 run();
292 }
293 }
294
295 public void setPrimitiveId(PrimitiveId pid) {
296 this.primitiveId = pid;
297 updateEnabledState();
298 }
299
300 public void run() {
301 if (HistoryDataSet.getInstance().getHistory(primitiveId) == null) {
302 Main.worker.submit(new HistoryLoadTask().add(primitiveId));
303 }
304 Runnable r = new Runnable() {
305 @Override
306 public void run() {
307 final History h = HistoryDataSet.getInstance().getHistory(primitiveId);
308 if (h == null)
309 return;
310 GuiHelper.runInEDT(new Runnable() {
311 @Override public void run() {
312 HistoryBrowserDialogManager.getInstance().show(h);
313 }
314 });
315 }
316 };
317 Main.worker.submit(r);
318 }
319
320 public void updateEnabledState() {
321 setEnabled(primitiveId != null && !primitiveId.isNew());
322 }
323 }
324
325 private static PrimitiveId primitiveIdAtRow(TableModel model, int row) {
326 DiffTableModel castedModel = (DiffTableModel) model;
327 Long id = (Long) castedModel.getValueAt(row, 0).value;
328 return id == null ? null : new SimplePrimitiveId(id, OsmPrimitiveType.NODE);
329 }
330
331 class InternalPopupMenuLauncher extends PopupMenuLauncher {
332 InternalPopupMenuLauncher() {
333 super(popupMenu);
334 }
335
336 @Override
337 protected int checkTableSelection(JTable table, Point p) {
338 int row = super.checkTableSelection(table, p);
339 popupMenu.prepare(primitiveIdAtRow(table.getModel(), row));
340 return row;
341 }
342 }
343
344 static class DoubleClickAdapter extends MouseAdapter {
345 private final JTable table;
346 private final ShowHistoryAction showHistoryAction;
347
348 DoubleClickAdapter(JTable table) {
349 this.table = table;
350 showHistoryAction = new ShowHistoryAction();
351 }
352
353 @Override
354 public void mouseClicked(MouseEvent e) {
355 if (e.getClickCount() < 2)
356 return;
357 int row = table.rowAtPoint(e.getPoint());
358 if (row <= 0)
359 return;
360 PrimitiveId pid = primitiveIdAtRow(table.getModel(), row);
361 if (pid == null || pid.isNew())
362 return;
363 showHistoryAction.setPrimitiveId(pid);
364 showHistoryAction.run();
365 }
366 }
367}
Note: See TracBrowser for help on using the repository browser.