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