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