001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.history;
003
004import java.awt.event.FocusEvent;
005import java.awt.event.FocusListener;
006import java.util.Collection;
007import java.util.Collections;
008import java.util.Map;
009import java.util.function.IntFunction;
010import java.util.function.Supplier;
011
012import javax.swing.JPopupMenu;
013import javax.swing.JTable;
014import javax.swing.ListSelectionModel;
015
016import org.openstreetmap.josm.actions.RestorePropertyAction;
017import org.openstreetmap.josm.data.osm.OsmPrimitive;
018import org.openstreetmap.josm.data.osm.Tagged;
019import org.openstreetmap.josm.gui.dialogs.properties.CopyAllKeyValueAction;
020import org.openstreetmap.josm.gui.dialogs.properties.CopyKeyValueAction;
021import org.openstreetmap.josm.gui.dialogs.properties.CopyValueAction;
022import org.openstreetmap.josm.gui.dialogs.properties.HelpTagAction;
023import org.openstreetmap.josm.gui.dialogs.properties.TaginfoAction;
024import org.openstreetmap.josm.gui.util.TableHelper;
025import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
026
027/**
028 * TagInfoViewer is a UI component which displays the list of tags of two
029 * version of a {@link org.openstreetmap.josm.data.osm.OsmPrimitive} in a {@link org.openstreetmap.josm.data.osm.history.History}.
030 *
031 * <ul>
032 *   <li>on the left, it displays the list of tags for the version at {@link PointInTimeType#REFERENCE_POINT_IN_TIME}</li>
033 *   <li>on the right, it displays the list of tags for the version at {@link PointInTimeType#CURRENT_POINT_IN_TIME}</li>
034 * </ul>
035 * @since 1709
036 */
037public class TagInfoViewer extends HistoryViewerPanel {
038    private static final class RepaintOnFocusChange implements FocusListener {
039        @Override
040        public void focusLost(FocusEvent e) {
041            repaintSelected(e);
042        }
043
044        @Override
045        public void focusGained(FocusEvent e) {
046            repaintSelected(e);
047        }
048
049        private static void repaintSelected(FocusEvent e) {
050            // we would only need the selected rows, but this is easier:
051            e.getComponent().repaint();
052        }
053    }
054
055    /**
056     * Constructs a new {@code TagInfoViewer}.
057     * @param model The history browsing model
058     */
059    public TagInfoViewer(HistoryBrowserModel model) {
060        super(model);
061    }
062
063    @Override
064    protected JTable buildReferenceTable() {
065        return buildTable(PointInTimeType.REFERENCE_POINT_IN_TIME);
066    }
067
068    @Override
069    protected JTable buildCurrentTable() {
070        return buildTable(PointInTimeType.CURRENT_POINT_IN_TIME);
071    }
072
073    private JTable buildTable(PointInTimeType pointInTime) {
074        TagTableModel tagTableModel = model.getTagTableModel(pointInTime);
075        JTable table = new JTable(tagTableModel, new TagTableColumnModel());
076        TableHelper.setFont(table, getClass());
077        table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
078        selectionSynchronizer.participateInSynchronizedSelection(table.getSelectionModel());
079        table.getTableHeader().setReorderingAllowed(false);
080        table.setTransferHandler(new TagInfoTransferHandler());
081        table.addFocusListener(new RepaintOnFocusChange());
082        JPopupMenu tagMenu = new JPopupMenu();
083
084        IntFunction<String> tagKeyFn = x -> (String) table.getValueAt(x, 0);
085        IntFunction<String> tagValueFn = x -> tagTableModel.getValue(tagKeyFn.apply(x));
086        IntFunction<Map<String, Integer>> tagValuesFn = x -> {
087            String value = tagValueFn.apply(x);
088            return value != null ? Collections.singletonMap(value, 1) : Collections.emptyMap();
089        };
090        Supplier<Collection<? extends Tagged>> objectSp = () -> Collections.singletonList(model.getPointInTime(pointInTime));
091        Supplier<OsmPrimitive> primitiveSupplier = () -> getPrimitiveFromDataSet(pointInTime);
092
093        tagMenu.add(trackJosmAction(new CopyValueAction(table, tagKeyFn, objectSp)));
094        final CopyKeyValueAction copyKeyValueAction = new CopyKeyValueAction(table, tagKeyFn, objectSp);
095        tagMenu.add(trackJosmAction(copyKeyValueAction));
096        tagMenu.addPopupMenuListener(copyKeyValueAction);
097        tagMenu.add(trackJosmAction(new CopyAllKeyValueAction(table, tagKeyFn, objectSp)));
098        tagMenu.add(new RestorePropertyAction(tagKeyFn, tagValueFn, primitiveSupplier, table.getSelectionModel()));
099        tagMenu.addSeparator();
100        tagMenu.add(trackJosmAction(new HelpTagAction(table, tagKeyFn, tagValuesFn)));
101        TaginfoAction taginfoAction = new TaginfoAction(table, tagKeyFn, tagValuesFn, null, null);
102        tagMenu.add(trackJosmAction(taginfoAction.toTagHistoryAction()));
103        tagMenu.add(trackJosmAction(taginfoAction));
104
105        table.addMouseListener(new PopupMenuLauncher(tagMenu));
106        return table;
107    }
108}