001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.history; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Point; 007 008import javax.swing.JTable; 009import javax.swing.ListSelectionModel; 010 011import org.openstreetmap.josm.data.osm.OsmPrimitive; 012import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 013import org.openstreetmap.josm.data.osm.PrimitiveId; 014import org.openstreetmap.josm.data.osm.SimplePrimitiveId; 015import org.openstreetmap.josm.data.osm.history.History; 016import org.openstreetmap.josm.gui.util.TableHelper; 017import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 018 019/** 020 * NodeListViewer is a UI component which displays the node list of two 021 * version of a {@link OsmPrimitive} in a {@link History}. 022 * 023 * <ul> 024 * <li>on the left, it displays the node list for the version at {@link PointInTimeType#REFERENCE_POINT_IN_TIME}</li> 025 * <li>on the right, it displays the node list for the version at {@link PointInTimeType#CURRENT_POINT_IN_TIME}</li> 026 * </ul> 027 * @since 1709 028 */ 029public class NodeListViewer extends HistoryViewerPanel { 030 031 /** 032 * Constructs a new {@code NodeListViewer}. 033 * @param model history browser model 034 */ 035 public NodeListViewer(HistoryBrowserModel model) { 036 super(model); 037 } 038 039 @Override 040 protected JTable buildReferenceTable() { 041 return buildTable(PointInTimeType.REFERENCE_POINT_IN_TIME, "table.referencenodelisttable"); 042 } 043 044 @Override 045 protected JTable buildCurrentTable() { 046 return buildTable(PointInTimeType.CURRENT_POINT_IN_TIME, "table.currentnodelisttable"); 047 } 048 049 private JTable buildTable(PointInTimeType pointInTimeType, String name) { 050 final DiffTableModel tableModel = model.getNodeListTableModel(pointInTimeType); 051 final NodeListTableColumnModel columnModel = new NodeListTableColumnModel(); 052 final JTable table = new JTable(tableModel, columnModel); 053 TableHelper.setFont(table, getClass()); 054 tableModel.addTableModelListener(new ReversedChangeListener(table, columnModel, tr("The nodes of this way are in reverse order"))); 055 table.setName(name); 056 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 057 selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel()); 058 table.getTableHeader().setReorderingAllowed(false); 059 table.addMouseListener(new InternalPopupMenuLauncher()); 060 table.addMouseListener(new ShowHistoryAction.DoubleClickAdapter(e -> { 061 int row = table.rowAtPoint(e.getPoint()); 062 return primitiveIdAtRow(tableModel, row); 063 })); 064 enableSemanticSelectionSynchronization(table.getSelectionModel(), 065 tableModel, model.getNodeListTableModel(pointInTimeType.opposite()), 066 this::isSemanticallyEquivalent); 067 return table; 068 } 069 070 private boolean isSemanticallyEquivalent(TwoColumnDiff.Item o1, TwoColumnDiff.Item o2) { 071 return o1.value != null && o1.value.equals(o2.value); //compare node IDs 072 } 073 074 private static PrimitiveId primitiveIdAtRow(DiffTableModel model, int row) { 075 if (row < 0) 076 return null; 077 Long id = (Long) model.getValueAt(row, 0).value; 078 return id == null ? null : new SimplePrimitiveId(id, OsmPrimitiveType.NODE); 079 } 080 081 static class InternalPopupMenuLauncher extends PopupMenuLauncher { 082 InternalPopupMenuLauncher() { 083 super(new ListPopupMenu(tr("Zoom to node"), tr("Zoom to this node in the current data layer"))); 084 } 085 086 @Override 087 protected int checkTableSelection(JTable table, Point p) { 088 int row = super.checkTableSelection(table, p); 089 ((ListPopupMenu) menu).prepare(primitiveIdAtRow((DiffTableModel) table.getModel(), row)); 090 return row; 091 } 092 } 093 094}