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;
007import java.awt.Rectangle;
008
009import javax.swing.JTable;
010import javax.swing.ListSelectionModel;
011
012import org.openstreetmap.josm.data.osm.PrimitiveId;
013import org.openstreetmap.josm.data.osm.RelationMemberData;
014import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
015import org.openstreetmap.josm.gui.util.TableHelper;
016import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
017
018/**
019 * RelationMemberListViewer is a UI component which displays the  list of relation members of two
020 * version of a {@link org.openstreetmap.josm.data.osm.Relation} in a {@link org.openstreetmap.josm.data.osm.history.History}.
021 *
022 * <ul>
023 *   <li>on the left, it displays the list of relation members for the version at {@link PointInTimeType#REFERENCE_POINT_IN_TIME}</li>
024 *   <li>on the right, it displays the list of relation members for the version at {@link PointInTimeType#CURRENT_POINT_IN_TIME}</li>
025 * </ul>
026 * @since 1709
027 */
028public class RelationMemberListViewer extends HistoryViewerPanel {
029
030    @Override
031    protected JTable buildReferenceTable() {
032        return buildTable(PointInTimeType.REFERENCE_POINT_IN_TIME, "table.referencememberlisttable");
033    }
034
035    @Override
036    protected JTable buildCurrentTable() {
037        return buildTable(PointInTimeType.CURRENT_POINT_IN_TIME, "table.currentmemberlisttable");
038    }
039
040    private JTable buildTable(PointInTimeType pointInTimeType, String name) {
041        final DiffTableModel tableModel = model.getRelationMemberTableModel(pointInTimeType);
042        final RelationMemberTableColumnModel columnModel = new RelationMemberTableColumnModel();
043        final JTable table = new JTable(tableModel, columnModel);
044        TableHelper.setFont(table, getClass());
045        tableModel.addTableModelListener(new ReversedChangeListener(
046                table, columnModel, tr("The members of this relation are in reverse order")));
047        table.setName(name);
048        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
049        selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
050        enableSemanticSelectionSynchronization(table.getSelectionModel(),
051                tableModel, model.getRelationMemberTableModel(pointInTimeType.opposite()),
052                this::isSemanticallyEquivalent);
053        table.getTableHeader().setReorderingAllowed(false);
054        table.addMouseListener(new InternalPopupMenuLauncher());
055        table.getModel().addTableModelListener(e -> {
056            Rectangle rect = table.getCellRect(((DiffTableModel) e.getSource()).getFirstChange(), 0, true);
057            table.scrollRectToVisible(rect);
058        });
059        table.addMouseListener(new ShowHistoryAction.DoubleClickAdapter(e -> {
060            int row = table.rowAtPoint(e.getPoint());
061            return primitiveIdAtRow(tableModel, row);
062        }));
063        return table;
064    }
065
066    private boolean isSemanticallyEquivalent(TwoColumnDiff.Item o1, TwoColumnDiff.Item o2) {
067        RelationMemberData rm1 = (RelationMemberData) o1.value;
068        RelationMemberData rm2 = (RelationMemberData) o2.value;
069        return rm1 != null && rm2 != null
070                && rm1.getMemberId() == rm2.getMemberId()
071                && rm1.getMemberType() == rm2.getMemberType();
072    }
073
074    /**
075     * Constructs a new {@code RelationMemberListViewer}.
076     * @param model The history browsing model
077     */
078    public RelationMemberListViewer(HistoryBrowserModel model) {
079        super(model);
080    }
081
082    private static PrimitiveId primitiveIdAtRow(DiffTableModel model, int row) {
083        if (row < 0)
084            return null;
085        RelationMemberData rm = (RelationMemberData) model.getValueAt(row, 0).value;
086        if (rm == null)
087            return null;
088        return new SimplePrimitiveId(rm.getUniqueId(), rm.getType());
089    }
090
091    static class InternalPopupMenuLauncher extends PopupMenuLauncher {
092        InternalPopupMenuLauncher() {
093            super(new ListPopupMenu(tr("Zoom to member"), tr("Zoom to this member in the current data layer")));
094
095        }
096
097        @Override
098        protected int checkTableSelection(JTable table, Point p) {
099            int row = super.checkTableSelection(table, p);
100            ((ListPopupMenu) menu).prepare(primitiveIdAtRow((DiffTableModel) table.getModel(), row));
101            return row;
102        }
103    }
104
105}