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