001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.help; 003 004import java.awt.Component; 005import java.util.Locale; 006 007import javax.swing.AbstractButton; 008import javax.swing.Action; 009import javax.swing.JComponent; 010import javax.swing.JMenu; 011import javax.swing.KeyStroke; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.actions.HelpAction; 015import org.openstreetmap.josm.tools.LanguageInfo; 016import org.openstreetmap.josm.tools.LanguageInfo.LocaleType; 017 018/** 019 * Provides utility methods for help system. 020 * @since 2252 021 */ 022public final class HelpUtil { 023 024 private HelpUtil() { 025 // Hide default constructor for utils classes 026 } 027 028 /** 029 * Replies the base wiki URL. 030 * 031 * @return the base wiki URL 032 */ 033 public static String getWikiBaseUrl() { 034 return Main.pref.get("help.baseurl", Main.getJOSMWebsite()); 035 } 036 037 /** 038 * Replies the base wiki URL for help pages 039 * 040 * @return the base wiki URL for help pages 041 */ 042 public static String getWikiBaseHelpUrl() { 043 return getWikiBaseUrl() + "/wiki"; 044 } 045 046 /** 047 * Replies the URL on the wiki for an absolute help topic. The URL is encoded in UTF-8. 048 * 049 * @param absoluteHelpTopic the absolute help topic 050 * @return the url 051 * @see #buildAbsoluteHelpTopic 052 */ 053 public static String getHelpTopicUrl(String absoluteHelpTopic) { 054 if (absoluteHelpTopic == null) 055 return null; 056 String ret = getWikiBaseHelpUrl(); 057 ret = ret.replaceAll("\\/+$", ""); 058 absoluteHelpTopic = absoluteHelpTopic.replace(" ", "%20"); 059 absoluteHelpTopic = absoluteHelpTopic.replaceAll("^\\/+", "/"); 060 return ret + absoluteHelpTopic; 061 } 062 063 /** 064 * Replies the URL to the edit page for the absolute help topic. 065 * 066 * @param absoluteHelpTopic the absolute help topic 067 * @return the URL to the edit page 068 */ 069 public static String getHelpTopicEditUrl(String absoluteHelpTopic) { 070 String topicUrl = getHelpTopicUrl(absoluteHelpTopic); 071 if (topicUrl != null) 072 topicUrl = topicUrl.replaceAll("#[^#]*$", ""); // remove optional fragment 073 return topicUrl + "?action=edit"; 074 } 075 076 /** 077 * Extracts the relative help topic from an URL. Replies null, if 078 * no relative help topic is found. 079 * 080 * @param url the url 081 * @return the relative help topic in the URL, i.e. "/Action/New" 082 */ 083 public static String extractRelativeHelpTopic(String url) { 084 String topic = extractAbsoluteHelpTopic(url); 085 if (topic == null) 086 return null; 087 String topicPrefix = getHelpTopicPrefix(LocaleType.ENGLISH); 088 if (topicPrefix != null) 089 topicPrefix = topicPrefix.replaceAll("^\\/+", ""); 090 String pattern = "/[A-Z][a-z]{1,2}(_[A-Z]{2})?:" + topicPrefix; 091 if (url.matches(pattern)) { 092 return topic.substring(pattern.length()); 093 } 094 return null; 095 } 096 097 /** 098 * Extracts the absolute help topic from an URL. Replies null, if 099 * no absolute help topic is found. 100 * 101 * @param url the url 102 * @return the absolute help topic in the URL, i.e. "/De:Help/Action/New" 103 */ 104 public static String extractAbsoluteHelpTopic(String url) { 105 if (url == null || !url.startsWith(getWikiBaseHelpUrl())) return null; 106 String topic = url.substring(getWikiBaseHelpUrl().length()); 107 String prefix = getHelpTopicPrefix(LocaleType.ENGLISH); 108 if (prefix == null || topic.startsWith(prefix)) 109 return topic; 110 111 String pattern = "/[A-Z][a-z]{1,2}(_[A-Z]{2})?:" + prefix.replaceAll("^\\/+", ""); 112 if (topic.matches(pattern)) 113 return topic; 114 115 return null; 116 } 117 118 /** 119 * Replies the help topic prefix for the given locale. Examples: 120 * <ul> 121 * <li>/Help if the locale is a locale with language "en"</li> 122 * <li>/De:Help if the locale is a locale with language "de"</li> 123 * </ul> 124 * 125 * @param type the type of the locale to use 126 * @return the help topic prefix 127 * @since 5915 128 */ 129 private static String getHelpTopicPrefix(LocaleType type) { 130 String ret = LanguageInfo.getWikiLanguagePrefix(type); 131 if (ret == null) 132 return ret; 133 ret = '/' + ret + Main.pref.get("help.pathhelp", "/Help").replaceAll("^\\/+", ""); // remove leading / 134 return ret.replaceAll("\\/+", "\\/"); // collapse sequences of // 135 } 136 137 /** 138 * Replies the absolute, localized help topic for the given topic. 139 * 140 * Example: for a topic "/Dialog/RelationEditor" and the locale "de", this method 141 * replies "/De:Help/Dialog/RelationEditor" 142 * 143 * @param topic the relative help topic. Home help topic assumed, if null. 144 * @param type the locale. {@link Locale#ENGLISH} assumed, if null. 145 * @return the absolute, localized help topic 146 * @since 5915 147 */ 148 public static String buildAbsoluteHelpTopic(String topic, LocaleType type) { 149 String prefix = getHelpTopicPrefix(type); 150 if (prefix == null || topic == null || topic.trim().isEmpty() || "/".equals(topic.trim())) 151 return prefix; 152 prefix += '/' + topic; 153 return prefix.replaceAll("\\/+", "\\/"); // collapse sequences of // 154 } 155 156 /** 157 * Replies the context specific help topic configured for <code>context</code>. 158 * @param context The UI object used as context 159 * 160 * @return the help topic. null, if no context specific help topic is found 161 */ 162 public static String getContextSpecificHelpTopic(Object context) { 163 if (context == null) 164 return null; 165 if (context instanceof Helpful) 166 return ((Helpful) context).helpTopic(); 167 if (context instanceof JMenu) { 168 JMenu b = (JMenu) context; 169 if (b.getClientProperty("help") != null) 170 return (String) b.getClientProperty("help"); 171 return null; 172 } 173 if (context instanceof AbstractButton) { 174 AbstractButton b = (AbstractButton) context; 175 if (b.getClientProperty("help") != null) 176 return (String) b.getClientProperty("help"); 177 return getContextSpecificHelpTopic(b.getAction()); 178 } 179 if (context instanceof Action) 180 return (String) ((Action) context).getValue("help"); 181 if (context instanceof JComponent && ((JComponent) context).getClientProperty("help") != null) 182 return (String) ((JComponent) context).getClientProperty("help"); 183 if (context instanceof Component) 184 return getContextSpecificHelpTopic(((Component) context).getParent()); 185 return null; 186 } 187 188 /** 189 * Replies the global help action, if available. Otherwise, creates an instance 190 * of {@link HelpAction}. 191 * 192 * @return instance of help action 193 */ 194 private static Action getHelpAction() { 195 if (Main.main != null && Main.main.menu != null) { 196 return Main.main.menu.help; 197 } 198 return HelpAction.createWithoutShortcut(); 199 } 200 201 /** 202 * Makes a component aware of context sensitive help. 203 * 204 * A relative help topic doesn't start with /Help and doesn't include a locale 205 * code. Example: /Dialog/RelationEditor is a relative help topic, /De:Help/Dialog/RelationEditor 206 * is not. 207 * 208 * @param component the component 209 * @param relativeHelpTopic the help topic. Set to the default help topic if null. 210 */ 211 public static void setHelpContext(JComponent component, String relativeHelpTopic) { 212 component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F1"), "help"); 213 component.getActionMap().put("help", getHelpAction()); 214 component.putClientProperty("help", relativeHelpTopic == null ? "/" : relativeHelpTopic); 215 } 216 217 /** 218 * This is a simple marker method for help topic literals. If you declare a help 219 * topic literal in the source you should enclose it in ht(...). 220 * 221 * <strong>Example</strong> 222 * <pre> 223 * String helpTopic = ht("/Dialog/RelationEditor"); 224 * or 225 * putValue("help", ht("/Dialog/RelationEditor")); 226 * </pre> 227 * 228 * @param helpTopic Help topic to mark 229 * @return {@code helpTopic} 230 */ 231 public static String ht(String helpTopic) { 232 // this is just a marker method 233 return helpTopic; 234 } 235}