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.Rectangle;
007import java.awt.event.ActionEvent;
008import java.text.MessageFormat;
009import java.util.Arrays;
010import java.util.List;
011
012import javax.swing.AbstractAction;
013import javax.swing.Action;
014import javax.swing.JComponent;
015import javax.swing.JPopupMenu;
016
017import org.openstreetmap.josm.Main;
018import org.openstreetmap.josm.data.StructUtils;
019import org.openstreetmap.josm.data.StructUtils.StructEntry;
020import org.openstreetmap.josm.spi.preferences.Config;
021import org.openstreetmap.josm.tools.ImageProvider;
022import org.openstreetmap.josm.tools.OpenBrowser;
023
024/**
025 * A menu displaying links to external history viewers for a changeset.
026 *
027 * @since 12871
028 */
029public class OpenChangesetPopupMenu extends JPopupMenu {
030
031    /**
032     * Constructs a new {@code OpenChangesetPopupMenu} for the given changeset id.
033     *
034     * @param changesetId the changeset id
035     */
036    public OpenChangesetPopupMenu(final long changesetId) {
037        StructUtils.getListOfStructs(Config.getPref(), "history-dialog.tools", DEFAULT_ENTRIES, ChangesetViewerEntry.class)
038                .stream()
039                .map(entry -> entry.toAction(changesetId))
040                .forEach(this::add);
041    }
042
043    /**
044     * Displays the popup menu at the lower-left corner of {@code parent}.
045     *
046     * @param parent the parent component to use for positioning this menu
047     */
048    public void show(final JComponent parent) {
049        final Rectangle r = parent.getBounds();
050        show(parent.getParent(), r.x, r.y + r.height);
051    }
052
053    private static final List<ChangesetViewerEntry> DEFAULT_ENTRIES = Arrays.asList(
054            new ChangesetViewerEntry(tr("View changeset in web browser"), Main.getBaseBrowseUrl() + "/changeset/{0}"),
055            new ChangesetViewerEntry(tr("Open {0}", "achavi (Augmented OSM Change Viewer)"), "https://overpass-api.de/achavi/?changeset={0}"),
056            new ChangesetViewerEntry(tr("Open {0}", "OSMCha (OSM Changeset Analyzer)"), "https://osmcha.mapbox.com/changesets/{0}"),
057            new ChangesetViewerEntry(tr("Open {0}", "OSM History Viewer"), "http://osmhv.openstreetmap.de/changeset.jsp?id={0}"),
058            new ChangesetViewerEntry(tr("Open {0}", "WhoDidIt (OSM Changeset Analyzer)"),
059                    "http://simon04.dev.openstreetmap.org/whodidit/index.html?changeset={0}&show=1")
060    );
061
062    /**
063     * Auxiliary class to save a link to a history viewer in the preferences.
064     */
065    public static class ChangesetViewerEntry {
066        /** Name to be displayed in popup menu */
067        @StructEntry
068        public String name;
069        /** Templated service url. <code>{0}</code> will be replaced by changeset id */
070        @StructEntry
071        public String url;
072
073        /**
074         * Constructs a new {@code ChangesetViewerEntry}.
075         */
076        public ChangesetViewerEntry() {
077        }
078
079        ChangesetViewerEntry(String name, String url) {
080            this.name = name;
081            this.url = url;
082        }
083
084        Action toAction(final long changesetId) {
085            return new OpenBrowserAction(name, MessageFormat.format(this.url, Long.toString(changesetId)));
086        }
087    }
088
089    static class OpenBrowserAction extends AbstractAction {
090        final String url;
091
092        OpenBrowserAction(String name, String url) {
093            super(name);
094            putValue(SHORT_DESCRIPTION, tr("Open {0}", url));
095            new ImageProvider("help/internet").getResource().attachImageIcon(this, true);
096            this.url = url;
097        }
098
099        @Override
100        public void actionPerformed(ActionEvent e) {
101            OpenBrowser.displayUrl(url);
102        }
103    }
104}