001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.properties; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionEvent; 007import java.awt.event.KeyEvent; 008import java.io.IOException; 009import java.util.ArrayList; 010import java.util.Arrays; 011import java.util.List; 012import java.util.Map; 013import java.util.Objects; 014import java.util.function.IntFunction; 015 016import javax.swing.AbstractAction; 017import javax.swing.JTable; 018import javax.swing.KeyStroke; 019import javax.xml.parsers.ParserConfigurationException; 020import javax.xml.xpath.XPathExpressionException; 021 022import org.openstreetmap.josm.data.osm.IRelation; 023import org.openstreetmap.josm.gui.MainApplication; 024import org.openstreetmap.josm.spi.preferences.Config; 025import org.openstreetmap.josm.tools.ImageProvider; 026import org.openstreetmap.josm.tools.LanguageInfo; 027import org.openstreetmap.josm.tools.Logging; 028import org.openstreetmap.josm.tools.Mediawiki; 029import org.openstreetmap.josm.tools.OpenBrowser; 030import org.openstreetmap.josm.tools.Utils; 031import org.xml.sax.SAXException; 032 033/** 034 * Launch browser with wiki help for selected object. 035 * @since 13521 036 */ 037public class HelpAction extends AbstractAction { 038 private final JTable tagTable; 039 private final IntFunction<String> tagKeySupplier; 040 private final IntFunction<Map<String, Integer>> tagValuesSupplier; 041 042 private final JTable membershipTable; 043 private final IntFunction<IRelation<?>> memberValueSupplier; 044 045 /** 046 * Constructs a new {@code HelpAction}. 047 * @param tagTable The tag table. Cannot be null 048 * @param tagKeySupplier Finds the key from given row of tag table. Cannot be null 049 * @param tagValuesSupplier Finds the values from given row of tag table (map of values and number of occurrences). Cannot be null 050 * @param membershipTable The membership table. Can be null 051 * @param memberValueSupplier Finds the parent relation from given row of membership table. Can be null 052 * @since 13959 (signature) 053 */ 054 public HelpAction(JTable tagTable, IntFunction<String> tagKeySupplier, IntFunction<Map<String, Integer>> tagValuesSupplier, 055 JTable membershipTable, IntFunction<IRelation<?>> memberValueSupplier) { 056 this.tagTable = Objects.requireNonNull(tagTable); 057 this.tagKeySupplier = Objects.requireNonNull(tagKeySupplier); 058 this.tagValuesSupplier = Objects.requireNonNull(tagValuesSupplier); 059 this.membershipTable = membershipTable; 060 this.memberValueSupplier = memberValueSupplier; 061 putValue(NAME, tr("Go to OSM wiki for tag help")); 062 putValue(SHORT_DESCRIPTION, tr("Launch browser with wiki help for selected object")); 063 new ImageProvider("dialogs", "search").getResource().attachImageIcon(this, true); 064 putValue(ACCELERATOR_KEY, getKeyStroke()); 065 } 066 067 /** 068 * Returns the keystroke launching this action (F1). 069 * @return the keystroke launching this action 070 */ 071 public KeyStroke getKeyStroke() { 072 return KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0); 073 } 074 075 @Override 076 public void actionPerformed(ActionEvent e) { 077 if (tagTable.getSelectedRowCount() == 1) { 078 int row = tagTable.getSelectedRow(); 079 String key = Utils.encodeUrl(tagKeySupplier.apply(row)); 080 Map<String, Integer> m = tagValuesSupplier.apply(row); 081 if (!m.isEmpty()) { 082 String val = Utils.encodeUrl(m.entrySet().iterator().next().getKey()); 083 MainApplication.worker.execute(() -> displayTagHelp(key, val)); 084 } 085 } else if (membershipTable != null && membershipTable.getSelectedRowCount() == 1) { 086 int row = membershipTable.getSelectedRow(); 087 final IRelation<?> relation = memberValueSupplier.apply(row); 088 MainApplication.worker.execute(() -> displayRelationHelp(relation)); 089 } else { 090 // give the generic help page, if more than one element is selected 091 MainApplication.worker.execute(HelpAction::displayGenericHelp); 092 } 093 } 094 095 /** 096 * Displays the most specific wiki page for the given key/value. 097 * @param key Key 098 * @param val Value 099 * @since 14208 100 */ 101 public static void displayTagHelp(String key, String val) { 102 final String lang = LanguageInfo.getWikiLanguagePrefix(LanguageInfo.LocaleType.OSM_WIKI); 103 final List<String> pages = Arrays.asList( 104 String.format("%sTag:%s=%s", lang, key, val), 105 String.format("Tag:%s=%s", key, val), 106 String.format("%sKey:%s", lang, key), 107 String.format("Key:%s", key), 108 String.format("%sMap_Features", lang), 109 "Map_Features" 110 ); 111 displayHelp(pages); 112 } 113 114 /** 115 * Displays the most specific wiki page for the given relation. 116 * @param rel Relation 117 * @since 14208 118 */ 119 public static void displayRelationHelp(IRelation<?> rel) { 120 final String lang = LanguageInfo.getWikiLanguagePrefix(LanguageInfo.LocaleType.OSM_WIKI); 121 final List<String> pages = new ArrayList<>(); 122 String type = rel.get("type"); 123 if (type != null) { 124 type = Utils.encodeUrl(type); 125 } 126 127 if (type != null && !type.isEmpty()) { 128 pages.add(String.format("%sRelation:%s", lang, type)); 129 pages.add(String.format("Relation:%s", type)); 130 } 131 132 pages.add(String.format("%sRelations", lang)); 133 pages.add("Relations"); 134 displayHelp(pages); 135 } 136 137 /** 138 * Displays the localized Map Features. 139 * @since 14208 140 */ 141 public static void displayGenericHelp() { 142 final String lang = LanguageInfo.getWikiLanguagePrefix(LanguageInfo.LocaleType.OSM_WIKI); 143 final List<String> pages = Arrays.asList( 144 String.format("%sMap_Features", lang), 145 "Map_Features" 146 ); 147 displayHelp(pages); 148 } 149 150 /** 151 * Display help by opening the first existing wiki page in the given list. 152 * @param pages list of wiki page names to test 153 * @since 14208 154 */ 155 public static void displayHelp(final List<String> pages) { 156 try { 157 new Mediawiki(Config.getUrls().getOSMWiki()) 158 .findExistingPage(pages) 159 .ifPresent(page -> OpenBrowser.displayUrl(Config.getUrls().getOSMWiki() + "/wiki/" + page)); 160 } catch (IOException | ParserConfigurationException | XPathExpressionException | SAXException e1) { 161 Logging.error(e1); 162 } 163 } 164}