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