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

Last change on this file since 8377 was 8308, checked in by Don-vip, 9 years ago

fix potential NPEs and Sonar issues related to serialization

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