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