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.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().length() == 0 || "/".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 * 155 * @return the help topic. null, if no context specific help topic is found 156 */ 157 public static String getContextSpecificHelpTopic(Object context) { 158 if (context == null) 159 return null; 160 if (context instanceof Helpful) 161 return ((Helpful)context).helpTopic(); 162 if (context instanceof JMenu) { 163 JMenu b = (JMenu)context; 164 if (b.getClientProperty("help") != null) 165 return (String)b.getClientProperty("help"); 166 return null; 167 } 168 if (context instanceof AbstractButton) { 169 AbstractButton b = (AbstractButton)context; 170 if (b.getClientProperty("help") != null) 171 return (String)b.getClientProperty("help"); 172 return getContextSpecificHelpTopic(b.getAction()); 173 } 174 if (context instanceof Action) 175 return (String)((Action)context).getValue("help"); 176 if (context instanceof JComponent && ((JComponent)context).getClientProperty("help") != null) 177 return (String)((JComponent)context).getClientProperty("help"); 178 if (context instanceof Component) 179 return getContextSpecificHelpTopic(((Component)context).getParent()); 180 return null; 181 } 182 183 /** 184 * Replies the global help action, if available. Otherwise, creates an instance 185 * of {@link HelpAction}. 186 * 187 * @return instance of help action 188 */ 189 private static Action getHelpAction() { 190 if (Main.main != null && Main.main.menu != null) { 191 return Main.main.menu.help; 192 } 193 return new HelpAction(); 194 } 195 196 /** 197 * Makes a component aware of context sensitive help. 198 * 199 * A relative help topic doesn't start with /Help and doesn't include a locale 200 * code. Example: /Dialog/RelationEditor is a relative help topic, /De:Help/Dialog/RelationEditor 201 * is not. 202 * 203 * @param component the component the component 204 * @param relativeHelpTopic the help topic. Set to the default help topic if null. 205 */ 206 public static void setHelpContext(JComponent component, String relativeHelpTopic) { 207 if (relativeHelpTopic == null) { 208 relativeHelpTopic = "/"; 209 } 210 component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F1"), "help"); 211 component.getActionMap().put("help", getHelpAction()); 212 component.putClientProperty("help", relativeHelpTopic); 213 } 214 215 /** 216 * This is a simple marker method for help topic literals. If you declare a help 217 * topic literal in the source you should enclose it in ht(...). 218 * 219 * <strong>Example</strong> 220 * <pre> 221 * String helpTopic = ht("/Dialog/RelationEditor"); 222 * or 223 * putValue("help", ht("/Dialog/RelationEditor")); 224 * </pre> 225 * 226 * @param helpTopic Help topic to mark 227 * @return {@code helpTopic} 228 */ 229 public static String ht(String helpTopic) { 230 // this is just a marker method 231 return helpTopic; 232 } 233}