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 topicUrl = topicUrl.replaceAll("#[^#]*$", ""); // remove optional fragment 072 return topicUrl + "?action=edit"; 073 } 074 075 /** 076 * Extracts the relative help topic from an URL. Replies null, if 077 * no relative help topic is found. 078 * 079 * @param url the url 080 * @return the relative help topic in the URL, i.e. "/Action/New" 081 */ 082 public static String extractRelativeHelpTopic(String url) { 083 String topic = extractAbsoluteHelpTopic(url); 084 if (topic == null) 085 return null; 086 String pattern = "/[A-Z][a-z]{1,2}(_[A-Z]{2})?:" + getHelpTopicPrefix(LocaleType.ENGLISH).replaceAll("^\\/+", ""); 087 if (url.matches(pattern)) { 088 return topic.substring(pattern.length()); 089 } 090 return null; 091 } 092 093 /** 094 * Extracts the absolute help topic from an URL. Replies null, if 095 * no absolute help topic is found. 096 * 097 * @param url the url 098 * @return the absolute help topic in the URL, i.e. "/De:Help/Action/New" 099 */ 100 public static String extractAbsoluteHelpTopic(String url) { 101 if (url == null || !url.startsWith(getWikiBaseHelpUrl())) return null; 102 url = url.substring(getWikiBaseHelpUrl().length()); 103 String prefix = getHelpTopicPrefix(LocaleType.ENGLISH); 104 if (url.startsWith(prefix)) 105 return url; 106 107 String pattern = "/[A-Z][a-z]{1,2}(_[A-Z]{2})?:" + prefix.replaceAll("^\\/+", ""); 108 if (url.matches(pattern)) 109 return url; 110 111 return null; 112 } 113 114 /** 115 * Replies the help topic prefix for the given locale. Examples: 116 * <ul> 117 * <li>/Help if the locale is a locale with language "en"</li> 118 * <li>/De:Help if the locale is a locale with language "de"</li> 119 * </ul> 120 * 121 * @param type the type of the locale to use 122 * @return the help topic prefix 123 * @since 5915 124 */ 125 private static String getHelpTopicPrefix(LocaleType type) { 126 String ret = LanguageInfo.getWikiLanguagePrefix(type); 127 if (ret == null) 128 return ret; 129 ret = '/' + ret + Main.pref.get("help.pathhelp", "/Help").replaceAll("^\\/+", ""); // remove leading / 130 return ret.replaceAll("\\/+", "\\/"); // collapse sequences of // 131 } 132 133 /** 134 * Replies the absolute, localized help topic for the given topic. 135 * 136 * Example: for a topic "/Dialog/RelationEditor" and the locale "de", this method 137 * replies "/De:Help/Dialog/RelationEditor" 138 * 139 * @param topic the relative help topic. Home help topic assumed, if null. 140 * @param type the locale. {@link Locale#ENGLISH} assumed, if null. 141 * @return the absolute, localized help topic 142 * @since 5915 143 */ 144 public static String buildAbsoluteHelpTopic(String topic, LocaleType type) { 145 String prefix = getHelpTopicPrefix(type); 146 if (prefix == null || topic == null || topic.trim().isEmpty() || "/".equals(topic.trim())) 147 return prefix; 148 prefix += '/' + topic; 149 return prefix.replaceAll("\\/+", "\\/"); // collapse sequences of // 150 } 151 152 /** 153 * Replies the context specific help topic configured for <code>context</code>. 154 * @param context The UI object used as context 155 * 156 * @return the help topic. null, if no context specific help topic is found 157 */ 158 public static String getContextSpecificHelpTopic(Object context) { 159 if (context == null) 160 return null; 161 if (context instanceof Helpful) 162 return ((Helpful) context).helpTopic(); 163 if (context instanceof JMenu) { 164 JMenu b = (JMenu) context; 165 if (b.getClientProperty("help") != null) 166 return (String) b.getClientProperty("help"); 167 return null; 168 } 169 if (context instanceof AbstractButton) { 170 AbstractButton b = (AbstractButton) context; 171 if (b.getClientProperty("help") != null) 172 return (String) b.getClientProperty("help"); 173 return getContextSpecificHelpTopic(b.getAction()); 174 } 175 if (context instanceof Action) 176 return (String) ((Action) context).getValue("help"); 177 if (context instanceof JComponent && ((JComponent) context).getClientProperty("help") != null) 178 return (String) ((JComponent) context).getClientProperty("help"); 179 if (context instanceof Component) 180 return getContextSpecificHelpTopic(((Component) context).getParent()); 181 return null; 182 } 183 184 /** 185 * Replies the global help action, if available. Otherwise, creates an instance 186 * of {@link HelpAction}. 187 * 188 * @return instance of help action 189 */ 190 private static Action getHelpAction() { 191 if (Main.main != null && Main.main.menu != null) { 192 return Main.main.menu.help; 193 } 194 return new HelpAction(); 195 } 196 197 /** 198 * Makes a component aware of context sensitive help. 199 * 200 * A relative help topic doesn't start with /Help and doesn't include a locale 201 * code. Example: /Dialog/RelationEditor is a relative help topic, /De:Help/Dialog/RelationEditor 202 * is not. 203 * 204 * @param component the component 205 * @param relativeHelpTopic the help topic. Set to the default help topic if null. 206 */ 207 public static void setHelpContext(JComponent component, String relativeHelpTopic) { 208 component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F1"), "help"); 209 component.getActionMap().put("help", getHelpAction()); 210 component.putClientProperty("help", relativeHelpTopic == null ? "/" : relativeHelpTopic); 211 } 212 213 /** 214 * This is a simple marker method for help topic literals. If you declare a help 215 * topic literal in the source you should enclose it in ht(...). 216 * 217 * <strong>Example</strong> 218 * <pre> 219 * String helpTopic = ht("/Dialog/RelationEditor"); 220 * or 221 * putValue("help", ht("/Dialog/RelationEditor")); 222 * </pre> 223 * 224 * @param helpTopic Help topic to mark 225 * @return {@code helpTopic} 226 */ 227 public static String ht(String helpTopic) { 228 // this is just a marker method 229 return helpTopic; 230 } 231}