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

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

sonar - Immutable Field

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