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}