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