[2512] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.gui.history;
|
---|
| 3 |
|
---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
| 6 | import java.awt.GridBagConstraints;
|
---|
| 7 | import java.awt.GridBagLayout;
|
---|
| 8 | import java.awt.Insets;
|
---|
| 9 | import java.awt.Point;
|
---|
| 10 | import java.awt.event.ActionEvent;
|
---|
| 11 | import java.awt.event.MouseAdapter;
|
---|
| 12 | import java.awt.event.MouseEvent;
|
---|
| 13 |
|
---|
| 14 | import javax.swing.AbstractAction;
|
---|
| 15 | import javax.swing.JPanel;
|
---|
| 16 | import javax.swing.JPopupMenu;
|
---|
| 17 | import javax.swing.JScrollPane;
|
---|
| 18 | import javax.swing.JTable;
|
---|
| 19 | import javax.swing.ListSelectionModel;
|
---|
| 20 |
|
---|
| 21 | import org.openstreetmap.josm.Main;
|
---|
| 22 | import org.openstreetmap.josm.actions.AutoScaleAction;
|
---|
| 23 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
| 24 | import org.openstreetmap.josm.data.osm.PrimitiveId;
|
---|
| 25 | import org.openstreetmap.josm.data.osm.history.History;
|
---|
| 26 | import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
|
---|
| 27 | import org.openstreetmap.josm.gui.history.HistoryBrowserModel.NodeListTableModel;
|
---|
| 28 | import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
---|
| 29 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
| 30 |
|
---|
| 31 | /**
|
---|
| 32 | * NodeListViewer is a UI component which displays the node list of two
|
---|
| 33 | * version of a {@see OsmPrimitive} in a {@see History}.
|
---|
| 34 | *
|
---|
| 35 | * <ul>
|
---|
| 36 | * <li>on the left, it displays the node list for the version at {@see PointInTimeType#REFERENCE_POINT_IN_TIME}</li>
|
---|
| 37 | * <li>on the right, it displays the node list for the version at {@see PointInTimeType#CURRENT_POINT_IN_TIME}</li>
|
---|
| 38 | * </ul>
|
---|
| 39 | *
|
---|
| 40 | */
|
---|
| 41 | public class NodeListViewer extends JPanel {
|
---|
| 42 |
|
---|
| 43 | private HistoryBrowserModel model;
|
---|
| 44 | private VersionInfoPanel referenceInfoPanel;
|
---|
| 45 | private VersionInfoPanel currentInfoPanel;
|
---|
| 46 | private AdjustmentSynchronizer adjustmentSynchronizer;
|
---|
| 47 | private SelectionSynchronizer selectionSynchronizer;
|
---|
| 48 | private NodeListPopupMenu popupMenu;
|
---|
| 49 |
|
---|
| 50 | protected JScrollPane embeddInScrollPane(JTable table) {
|
---|
| 51 | JScrollPane pane = new JScrollPane(table);
|
---|
| 52 | pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
---|
| 53 | pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
|
---|
| 54 | adjustmentSynchronizer.participateInSynchronizedScrolling(pane.getVerticalScrollBar());
|
---|
| 55 | return pane;
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | protected JTable buildReferenceNodeListTable() {
|
---|
| 59 | JTable table = new JTable(
|
---|
| 60 | model.getNodeListTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME),
|
---|
| 61 | new NodeListTableColumnModel()
|
---|
| 62 | );
|
---|
| 63 | table.setName("table.referencenodelisttable");
|
---|
| 64 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
---|
| 65 | selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
|
---|
| 66 | table.addMouseListener(new PopupMenuLauncher(table));
|
---|
| 67 | table.addMouseListener(new DoubleClickAdapter(table));
|
---|
| 68 | return table;
|
---|
| 69 | }
|
---|
| 70 |
|
---|
| 71 | protected JTable buildCurrentNodeListTable() {
|
---|
| 72 | JTable table = new JTable(
|
---|
| 73 | model.getNodeListTableModel(PointInTimeType.CURRENT_POINT_IN_TIME),
|
---|
| 74 | new NodeListTableColumnModel()
|
---|
| 75 | );
|
---|
| 76 | table.setName("table.currentnodelisttable");
|
---|
| 77 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
---|
| 78 | selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
|
---|
| 79 | table.addMouseListener(new PopupMenuLauncher(table));
|
---|
| 80 | table.addMouseListener(new DoubleClickAdapter(table));
|
---|
| 81 | return table;
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | protected void build() {
|
---|
| 85 | setLayout(new GridBagLayout());
|
---|
| 86 | GridBagConstraints gc = new GridBagConstraints();
|
---|
| 87 |
|
---|
| 88 | // ---------------------------
|
---|
| 89 | gc.gridx = 0;
|
---|
| 90 | gc.gridy = 0;
|
---|
| 91 | gc.gridwidth = 1;
|
---|
| 92 | gc.gridheight = 1;
|
---|
| 93 | gc.weightx = 0.5;
|
---|
| 94 | gc.weighty = 0.0;
|
---|
| 95 | gc.insets = new Insets(5,5,5,0);
|
---|
| 96 | gc.fill = GridBagConstraints.HORIZONTAL;
|
---|
| 97 | gc.anchor = GridBagConstraints.FIRST_LINE_START;
|
---|
| 98 | referenceInfoPanel = new VersionInfoPanel(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
|
---|
| 99 | add(referenceInfoPanel,gc);
|
---|
| 100 |
|
---|
| 101 | gc.gridx = 1;
|
---|
| 102 | gc.gridy = 0;
|
---|
| 103 | gc.gridwidth = 1;
|
---|
| 104 | gc.gridheight = 1;
|
---|
| 105 | gc.fill = GridBagConstraints.HORIZONTAL;
|
---|
| 106 | gc.weightx = 0.5;
|
---|
| 107 | gc.weighty = 0.0;
|
---|
| 108 | gc.anchor = GridBagConstraints.FIRST_LINE_START;
|
---|
| 109 | currentInfoPanel = new VersionInfoPanel(model, PointInTimeType.CURRENT_POINT_IN_TIME);
|
---|
| 110 | add(currentInfoPanel,gc);
|
---|
| 111 |
|
---|
| 112 | adjustmentSynchronizer = new AdjustmentSynchronizer();
|
---|
| 113 | selectionSynchronizer = new SelectionSynchronizer();
|
---|
| 114 |
|
---|
| 115 | // ---------------------------
|
---|
| 116 | gc.gridx = 0;
|
---|
| 117 | gc.gridy = 1;
|
---|
| 118 | gc.gridwidth = 1;
|
---|
| 119 | gc.gridheight = 1;
|
---|
| 120 | gc.weightx = 0.5;
|
---|
| 121 | gc.weighty = 1.0;
|
---|
| 122 | gc.fill = GridBagConstraints.BOTH;
|
---|
| 123 | gc.anchor = GridBagConstraints.NORTHWEST;
|
---|
| 124 | add(embeddInScrollPane(buildReferenceNodeListTable()),gc);
|
---|
| 125 |
|
---|
| 126 | gc.gridx = 1;
|
---|
| 127 | gc.gridy = 1;
|
---|
| 128 | gc.gridwidth = 1;
|
---|
| 129 | gc.gridheight = 1;
|
---|
| 130 | gc.weightx = 0.5;
|
---|
| 131 | gc.weighty = 1.0;
|
---|
| 132 | gc.fill = GridBagConstraints.BOTH;
|
---|
| 133 | gc.anchor = GridBagConstraints.NORTHWEST;
|
---|
| 134 | add(embeddInScrollPane(buildCurrentNodeListTable()),gc);
|
---|
| 135 |
|
---|
| 136 | popupMenu = new NodeListPopupMenu();
|
---|
| 137 | }
|
---|
| 138 |
|
---|
| 139 | public NodeListViewer(HistoryBrowserModel model) {
|
---|
| 140 | setModel(model);
|
---|
| 141 | build();
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | protected void unregisterAsObserver(HistoryBrowserModel model) {
|
---|
| 145 | if (currentInfoPanel != null) {
|
---|
| 146 | model.deleteObserver(currentInfoPanel);
|
---|
| 147 | }
|
---|
| 148 | if (referenceInfoPanel != null) {
|
---|
| 149 | model.deleteObserver(referenceInfoPanel);
|
---|
| 150 | }
|
---|
| 151 | }
|
---|
| 152 | protected void registerAsObserver(HistoryBrowserModel model) {
|
---|
| 153 | if (currentInfoPanel != null) {
|
---|
| 154 | model.addObserver(currentInfoPanel);
|
---|
| 155 | }
|
---|
| 156 | if (referenceInfoPanel != null) {
|
---|
| 157 | model.addObserver(referenceInfoPanel);
|
---|
| 158 | }
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | public void setModel(HistoryBrowserModel model) {
|
---|
| 162 | if (this.model != null) {
|
---|
| 163 | unregisterAsObserver(model);
|
---|
| 164 | }
|
---|
| 165 | this.model = model;
|
---|
| 166 | if (this.model != null) {
|
---|
| 167 | registerAsObserver(model);
|
---|
| 168 | }
|
---|
| 169 | }
|
---|
| 170 |
|
---|
[2626] | 171 | static class NodeListPopupMenu extends JPopupMenu {
|
---|
[2512] | 172 | private ZoomToNodeAction zoomToNodeAction;
|
---|
| 173 | private ShowHistoryAction showHistoryAction;
|
---|
| 174 |
|
---|
| 175 | public NodeListPopupMenu() {
|
---|
| 176 | zoomToNodeAction = new ZoomToNodeAction();
|
---|
| 177 | add(zoomToNodeAction);
|
---|
| 178 | showHistoryAction = new ShowHistoryAction();
|
---|
| 179 | add(showHistoryAction);
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | public void prepare(PrimitiveId pid){
|
---|
| 183 | zoomToNodeAction.setPrimitiveId(pid);
|
---|
| 184 | zoomToNodeAction.updateEnabledState();
|
---|
| 185 |
|
---|
| 186 | showHistoryAction.setPrimitiveId(pid);
|
---|
| 187 | showHistoryAction.updateEnabledState();
|
---|
| 188 | }
|
---|
| 189 | }
|
---|
| 190 |
|
---|
[2626] | 191 | static class ZoomToNodeAction extends AbstractAction {
|
---|
[2512] | 192 | private PrimitiveId primitiveId;
|
---|
| 193 |
|
---|
| 194 | public ZoomToNodeAction() {
|
---|
| 195 | putValue(NAME, tr("Zoom to node"));
|
---|
| 196 | putValue(SHORT_DESCRIPTION, tr("Zoom to this node in the current data layer"));
|
---|
| 197 | putValue(SMALL_ICON, ImageProvider.get("dialogs", "zoomin"));
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | public void actionPerformed(ActionEvent e) {
|
---|
| 201 | if (!isEnabled()) return;
|
---|
| 202 | OsmPrimitive p = getPrimitiveToZoom();
|
---|
| 203 | if (p!= null) {
|
---|
| 204 | getEditLayer().data.setSelected(p.getPrimitiveId());
|
---|
[3327] | 205 | AutoScaleAction.autoScale("selection");
|
---|
[2512] | 206 | }
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | public void setPrimitiveId(PrimitiveId pid) {
|
---|
| 210 | this.primitiveId = pid;
|
---|
| 211 | updateEnabledState();
|
---|
| 212 | }
|
---|
| 213 |
|
---|
| 214 | protected OsmDataLayer getEditLayer() {
|
---|
| 215 | try {
|
---|
| 216 | return Main.map.mapView.getEditLayer();
|
---|
| 217 | } catch(NullPointerException e) {
|
---|
| 218 | return null;
|
---|
| 219 | }
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | protected OsmPrimitive getPrimitiveToZoom() {
|
---|
| 223 | if (primitiveId == null) return null;
|
---|
| 224 | OsmPrimitive p = getEditLayer().data.getPrimitiveById(primitiveId);
|
---|
| 225 | return p;
|
---|
| 226 | }
|
---|
| 227 |
|
---|
| 228 | public void updateEnabledState() {
|
---|
| 229 | if (getEditLayer() == null) {
|
---|
| 230 | setEnabled(false);
|
---|
| 231 | return;
|
---|
| 232 | }
|
---|
| 233 | setEnabled(getPrimitiveToZoom() != null);
|
---|
| 234 | }
|
---|
| 235 | }
|
---|
| 236 |
|
---|
[2626] | 237 | static class ShowHistoryAction extends AbstractAction {
|
---|
[2512] | 238 | private PrimitiveId primitiveId;
|
---|
| 239 |
|
---|
| 240 | public ShowHistoryAction() {
|
---|
| 241 | putValue(NAME, tr("Show history"));
|
---|
| 242 | putValue(SHORT_DESCRIPTION, tr("Open a history browser with the history of this node"));
|
---|
| 243 | putValue(SMALL_ICON, ImageProvider.get("dialogs", "history"));
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | public void actionPerformed(ActionEvent e) {
|
---|
| 247 | if (!isEnabled()) return;
|
---|
| 248 | run();
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 | public void setPrimitiveId(PrimitiveId pid) {
|
---|
| 252 | this.primitiveId = pid;
|
---|
| 253 | updateEnabledState();
|
---|
| 254 | }
|
---|
| 255 |
|
---|
| 256 | public void run() {
|
---|
| 257 | if (HistoryDataSet.getInstance().getHistory(primitiveId) == null) {
|
---|
| 258 | Main.worker.submit(new HistoryLoadTask().add(primitiveId));
|
---|
| 259 | }
|
---|
| 260 | Runnable r = new Runnable() {
|
---|
| 261 | public void run() {
|
---|
| 262 | History h = HistoryDataSet.getInstance().getHistory(primitiveId);
|
---|
| 263 | if (h == null)
|
---|
| 264 | return;
|
---|
| 265 | HistoryBrowserDialogManager.getInstance().show(h);
|
---|
| 266 | }
|
---|
| 267 | };
|
---|
| 268 | Main.worker.submit(r);
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | public void updateEnabledState() {
|
---|
| 272 | setEnabled(primitiveId != null && primitiveId.getUniqueId() > 0);
|
---|
| 273 | }
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | class PopupMenuLauncher extends MouseAdapter {
|
---|
| 277 | private JTable table;
|
---|
| 278 |
|
---|
| 279 | public PopupMenuLauncher(JTable table) {
|
---|
| 280 | this.table = table;
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | @Override
|
---|
| 284 | public void mousePressed(MouseEvent e) {
|
---|
| 285 | showPopup(e);
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | @Override
|
---|
| 289 | public void mouseReleased(MouseEvent e) {
|
---|
| 290 | showPopup(e);
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | private void showPopup(MouseEvent e) {
|
---|
| 294 | if (!e.isPopupTrigger()) return;
|
---|
| 295 | Point p = e.getPoint();
|
---|
| 296 | int row = table.rowAtPoint(p);
|
---|
| 297 | NodeListTableModel model = (NodeListTableModel) table.getModel();
|
---|
| 298 | PrimitiveId pid = model.getNodeId(row);
|
---|
| 299 | popupMenu.prepare(pid);
|
---|
| 300 | popupMenu.show(e.getComponent(), e.getX(), e.getY());
|
---|
| 301 | }
|
---|
| 302 | }
|
---|
| 303 |
|
---|
[2626] | 304 | static class DoubleClickAdapter extends MouseAdapter {
|
---|
[2512] | 305 | private JTable table;
|
---|
| 306 | private ShowHistoryAction showHistoryAction;
|
---|
| 307 |
|
---|
| 308 | public DoubleClickAdapter(JTable table) {
|
---|
| 309 | this.table = table;
|
---|
| 310 | showHistoryAction = new ShowHistoryAction();
|
---|
| 311 | }
|
---|
| 312 |
|
---|
| 313 | protected NodeListTableModel getModel() {
|
---|
| 314 | return (NodeListTableModel)table.getModel();
|
---|
| 315 | }
|
---|
| 316 |
|
---|
| 317 | @Override
|
---|
| 318 | public void mouseClicked(MouseEvent e) {
|
---|
| 319 | if (e.getClickCount() < 2) return;
|
---|
| 320 | int row = table.rowAtPoint(e.getPoint());
|
---|
| 321 | PrimitiveId pid = getModel().getNodeId(row);
|
---|
| 322 | if (pid == null)
|
---|
| 323 | return;
|
---|
| 324 | showHistoryAction.setPrimitiveId(pid);
|
---|
| 325 | showHistoryAction.run();
|
---|
| 326 | }
|
---|
| 327 | }
|
---|
| 328 | }
|
---|