307d2424c18a85d5276781a10263b6b2f0e5ded4
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / bridge / helpbrowser / SwingScilabHelpBrowserViewer.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Sylvestre Ledru
4  * Copyright (C) 2011 - Calixte DENIZET
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13 package org.scilab.modules.gui.bridge.helpbrowser;
14
15 import java.awt.event.ActionEvent;
16 import java.awt.event.ActionListener;
17 import java.awt.event.MouseWheelEvent;
18 import java.awt.event.MouseWheelListener;
19 import java.beans.PropertyChangeEvent;
20 import java.beans.PropertyChangeListener;
21 import java.io.File;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.net.JarURLConnection;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.util.ArrayList;
29 import java.util.Enumeration;
30 import java.util.List;
31 import java.util.regex.Matcher;
32
33 import javax.help.DefaultHelpHistoryModel;
34 import javax.help.HelpSet;
35 import javax.help.JHelpContentViewer;
36 import javax.help.plaf.basic.BasicContentViewerUI;
37 import javax.swing.AbstractAction;
38 import javax.swing.JComponent;
39 import javax.swing.JEditorPane;
40 import javax.swing.JMenuItem;
41 import javax.swing.JPopupMenu;
42 import javax.swing.JScrollPane;
43 import javax.swing.SwingUtilities;
44 import javax.swing.event.HyperlinkEvent;
45 import javax.swing.text.BadLocationException;
46 import javax.swing.text.DefaultEditorKit;
47 import javax.swing.text.Document;
48 import javax.swing.text.Element;
49 import javax.swing.text.MutableAttributeSet;
50 import javax.swing.text.StyleConstants;
51 import javax.swing.text.StyleContext;
52 import javax.swing.text.html.HTMLDocument;
53
54 import org.scilab.modules.commons.ScilabConstants;
55 import org.scilab.modules.commons.gui.ScilabKeyStroke;
56 import org.scilab.modules.gui.console.ScilabConsole;
57 import org.scilab.modules.gui.helpbrowser.ScilabHelpBrowser;
58 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
59 import org.scilab.modules.gui.tab.Tab;
60 import org.scilab.modules.gui.utils.ConfigManager;
61 import org.scilab.modules.gui.utils.WebBrowser;
62 import org.scilab.modules.localization.Messages;
63
64 /**
65  * This class inherits from BasicContentViewerUI from Javahelp.
66  * Through this class, we are adding some features on the javahelp browser
67  * We are adding a popup menu on the right click of the mouse
68  * In this menu, we are providing:
69  *  - Execute in Scilab
70  *  - Edit in the text editor
71  *  - Copy
72  *  - Select all
73  *  - History
74  *
75  * @author Sylvestre LEDRU
76  * @author Calixte DENIZET
77  */
78 public class SwingScilabHelpBrowserViewer extends BasicContentViewerUI implements MouseWheelListener {
79
80     private static final String SCILAB_PROTO = "scilab://";
81     private static final String FILE_PROTO = "file://";
82     private static final String SCI = ScilabConstants.SCI.getPath().replaceAll("\\\\", "/");;
83     private static final String SHIFTEQ = "shiftEquals";
84     private static final long serialVersionUID = -2593697956426596790L;
85     private static final int[] fontSizes = new int[]{8, 10, 12, 14, 18, 24, 36};
86     private static final boolean isMac = System.getProperty("os.name").toLowerCase().indexOf("mac") != -1;
87
88     private static int currentFontSize = ConfigManager.getHelpFontSize();
89
90     /* This field is a copy of BasicContentViewerUI which is privated.
91      * Therefor, I am changing the permission here to make it available
92      * to the methods of this object
93      */
94     private JEditorPane accessibleHtml;
95
96     private JHelpContentViewer x;
97     private List<HelpSet> helpSets;
98
99     public SwingScilabHelpBrowserViewer(JHelpContentViewer x) {
100         super(x);
101         this.x = x;
102     }
103
104     public static javax.swing.plaf.ComponentUI createUI(JComponent x) {
105         return new SwingScilabHelpBrowserViewer((JHelpContentViewer) x);
106     }
107
108     /**
109      * @return the JEditorPane used in the HTML view
110      */
111     public JEditorPane getAccessibleHTML() {
112         return accessibleHtml;
113     }
114
115     /**
116      * Update the browser links
117      */
118     public void hyperlinkUpdate(HyperlinkEvent event) {
119         if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
120             if (event.getDescription().startsWith("http://")) {
121                 WebBrowser.openUrl(event.getURL(), event.getDescription());
122             } else if (event.getDescription().startsWith(SCILAB_PROTO)) {
123                 if (helpSets == null) {
124                     initHelpSets(x.getModel().getHelpSet());
125                 }
126                 URL url = resolvScilabLink(event);
127                 if (url != null) {
128                     super.hyperlinkUpdate(new HyperlinkEvent(event.getSource(), event.getEventType(), url, ""));
129                 }
130             } else if (event.getDescription().startsWith(FILE_PROTO)) {
131                 String url = event.getDescription();
132                 url = url.replaceFirst("SCI", Matcher.quoteReplacement(SCI));
133                 WebBrowser.openUrl(url);
134             } else {
135                 super.hyperlinkUpdate(event);
136             }
137         }
138     }
139
140     private void initHelpSets(HelpSet hs) {
141         helpSets = new ArrayList();
142         helpSets.add(hs);
143         for (Enumeration<HelpSet> e = hs.getHelpSets(); e.hasMoreElements();) {
144             helpSets.add(e.nextElement());
145         }
146     }
147
148     /**
149      * Try to find an id
150      * @param id the id to find
151      * @return the URL corresponding to the id
152      */
153     public URL getURLFromID(String id) {
154         URL url = null;
155         try {
156             for (HelpSet hs : helpSets) {
157                 javax.help.Map map = hs.getLocalMap();
158                 if (map.isValidID(id, hs)) {
159                     url = map.getURLFromID(javax.help.Map.ID.create(id, hs));
160                     if (url != null) {
161                         return url;
162                     }
163                 }
164             }
165             url = new URL(helpSets.get(0).getHelpSetURL().toString().replace("jhelpset.hs", "ScilabErrorPage.html"));
166         } catch (MalformedURLException ex) { }
167
168         return url;
169     }
170
171     /**
172      * Try to find an id in a toolbox
173      * @param tbxName the toolbox's name
174      * @param id the id to find
175      * @return the URL corresponding to the id
176      */
177     public URL getURLFromID(String tbxName, String id) {
178         if (tbxName == null) {
179             return getURLFromID(id);
180         }
181         URL url = null;
182         try {
183             for (HelpSet hs : helpSets) {
184                 if (hs.getHelpSetURL().toString().replaceAll("\\\\", "/").indexOf("/" + tbxName + "/") !=  -1) {
185                     javax.help.Map map = hs.getLocalMap();
186                     if (map.isValidID(id, hs)) {
187                         url = map.getURLFromID(javax.help.Map.ID.create(id, hs));
188                         if (url != null) {
189                             return url;
190                         }
191                     }
192                 }
193             }
194             url = new URL(helpSets.get(0).getHelpSetURL().toString().replace("jhelpset.hs", "ScilabErrorPage.html"));
195         } catch (MalformedURLException ex) { }
196
197         return url;
198     }
199
200     /**
201      * Try to transform an address such as scilab://scilab.help/bvode into a conform URL
202      * pointing to the corresponding file in using jar: protocol.
203      * E.g. scilab://scilab.help/bvode will be transform into
204      * jar:file:SCI/modules/helptools/jar/scilab_fr_FR_help.jar!/scilab_fr_FR_help/bvode.html
205      * (where SCI has the good value)
206      * @param address the address to convert
207      * @return the correct address in using jar:// protocol
208      **/
209     public URL resolvScilabLink(HyperlinkEvent event) {
210         int pos = SCILAB_PROTO.length();
211         String addr = event.getDescription();
212         addr = addr.trim().replaceAll("\\\\", "/");
213         addr = addr.substring(pos);
214
215         pos = addr.indexOf("/");
216         String location;
217         String path = "";
218         if (pos != -1) {
219             location = addr.substring(0, pos);
220             if (pos != addr.length()) {
221                 path = addr.substring(pos + 1);
222             }
223         } else {
224             return getURLFromID(addr);
225         }
226
227         String[] splitLoc = location.split("\\.");
228         String mainLocation = null;
229         String subLocation = null;
230
231         if (splitLoc.length >= 1) {
232             mainLocation = splitLoc[0];
233         }
234         if (splitLoc.length >= 2) {
235             subLocation = splitLoc[1];
236         }
237
238         if (subLocation.equals("help")) {
239             if (mainLocation.equals("scilab")) {
240                 return getURLFromID(path);
241             } else {
242                 return getURLFromID(mainLocation, path);
243             }
244         } else if (subLocation.equals("xcos") || subLocation.equals("scinotes")) {
245             if (!mainLocation.equals("scilab")) {
246                 exec(subLocation, getToolboxPath() + "/" + path);
247             } else {
248                 exec(subLocation, SCI + "/modules/" + path);
249             }
250         } else if (subLocation.equals("demos")) {
251             if (!mainLocation.equals("scilab")) {
252                 exec(getToolboxPath() + "/demos/" + path + ".sce");
253             } else {
254                 exec(SCI + "/modules/" + path + ".sce");
255             }
256         } else if (subLocation.equals("execexample")) {
257             execExample(event.getSourceElement().getParentElement().getParentElement().getParentElement().getElement(0).getElement(0));
258         } else if (subLocation.equals("editexample")) {
259             editExample(event.getSourceElement().getParentElement().getParentElement().getParentElement().getElement(0).getElement(0));
260         } else if (subLocation.equals("exec")) {
261             if (!mainLocation.equals("scilab")) {
262                 exec(getToolboxPath() + "/" + path);
263             } else {
264                 exec(SCI + "/modules/" + path);
265             }
266         }
267
268         return null;
269     }
270
271     /**
272      * @return the path of the toolbox
273      */
274     public String getToolboxPath() {
275         try {
276             URL url = ((JarURLConnection) x.getCurrentURL().openConnection()).getJarFileURL();
277             return new File(url.toURI()).getParentFile().getParent();
278         } catch (Exception e) { }
279
280         return "";
281     }
282
283     /**
284      * Execute the code in example
285      * @param pre the preformatted Element containing Scilab's code
286      */
287     public void execExample(Element pre) {
288         String code = getCode(pre);
289         try {
290             ScilabConsole.getConsole().getAsSimpleConsole().sendCommandsToScilab(code, true /* display */, false /* store in history */);
291         } catch (NoClassDefFoundError e) {
292             ScilabModalDialog.show((Tab) SwingUtilities.getAncestorOfClass(Tab.class, x), Messages.gettext("Feature not available in this mode..."));
293         }
294     }
295
296     /**
297      * Edit the code in example
298      * @param pre the preformatted Element containing Scilab's code
299      */
300     public static void editExample(Element pre) {
301         edit(getCode(pre));
302     }
303
304     /**
305      * Edit the code
306      * @param code the code to edit
307      */
308     private static void edit(String code) {
309         try {
310             /* Dynamic load of the SciNotes class.
311              * This is done to avoid a cyclic dependency on gui <=> scinotes
312              */
313             Class scinotesClass = Class.forName("org.scilab.modules.scinotes.SciNotes");
314             Class[] arguments = new Class[] {String.class};
315             Method method = scinotesClass.getMethod("scinotesWithText", arguments);
316             method.invoke(scinotesClass, new Object[]{code});
317
318         } catch (ClassNotFoundException e) {
319             System.err.println("Could not find SciNotes class");
320             e.printStackTrace();
321         } catch (SecurityException e) {
322             System.err.println("Security error: Could not access to SciNotes class");
323             e.printStackTrace();
324         } catch (NoSuchMethodException e) {
325             System.err.println("Could not access to scinotesWithText method from object SciNotes");
326             e.printStackTrace();
327         } catch (IllegalArgumentException e) {
328             System.err.println("Wrong argument used with scinotesWithText method from object SciNotes");
329             e.printStackTrace();
330         } catch (IllegalAccessException e) {
331             System.err.println("Illegal access with scinotesWithText method from object SciNotes");
332             e.printStackTrace();
333         } catch (InvocationTargetException e) {
334             System.err.println("Error of invocation with scinotesWithText method from object SciNotes");
335             e.printStackTrace();
336         }
337     }
338
339     /**
340      * @param pre the preformatted Element containing Scilab's code
341      * @return the code
342      */
343     private static String getCode(Element pre) {
344         int size = pre.getElementCount();
345         Document doc = pre.getDocument();
346         StringBuilder buffer = new StringBuilder();
347         for (int i = 0; i < size; i++) {
348             Element line = pre.getElement(i);
349             int ssize = line.getElementCount();
350             for (int j = 0; j < ssize; j++) {
351                 Element content = line.getElement(j);
352                 if (content.isLeaf()) {
353                     try {
354                         buffer.append(doc.getText(content.getStartOffset(), content.getEndOffset() - content.getStartOffset()));
355                     } catch (BadLocationException e) { }
356                 }
357             }
358         }
359
360         return buffer.toString().trim();
361     }
362
363     /**
364      * Execute a file given by its path
365      * @param the file path
366      */
367     public void exec(String path) {
368         String cmd = "exec('" + path + "', -1)";
369         try {
370             ScilabConsole.getConsole().getAsSimpleConsole().sendCommandsToScilab(cmd, true, false);
371         } catch (NoClassDefFoundError e) {
372             ScilabModalDialog.show((Tab) SwingUtilities.getAncestorOfClass(Tab.class, x), Messages.gettext("Feature not available in this mode..."));
373         }
374     }
375
376     /**
377      * Execute with the command and a file given by its path
378      * @param command the command to execute
379      * @param the file path
380      */
381     public void exec(String command, String path) {
382         String cmd = command + "('" + path + "')";
383         try {
384             ScilabConsole.getConsole().getAsSimpleConsole().sendCommandsToScilab(cmd, false, false);
385         } catch (NoClassDefFoundError e) {
386             ScilabModalDialog.show((Tab) SwingUtilities.getAncestorOfClass(Tab.class, x), Messages.gettext("Feature not available in this mode..."));
387         }
388     }
389
390     /**
391      * Create the UI interface
392      * @see javax.help.plaf.basic.BasicContentViewerUI#installUI(javax.swing.JComponent)
393      * @param c The component
394      */
395     public void installUI(JComponent c) {
396         super.installUI(c);
397         this.retrievePrivateFieldFromBasicContentViewerUI();
398         this.createPopupMenu(c);
399     }
400
401     /**
402      * Retrieve the field "html" from BasicContentViewerUI and change
403      * permission (it is private by default)
404      */
405     private void retrievePrivateFieldFromBasicContentViewerUI() {
406         Field privateField = null;
407         try {
408             privateField = BasicContentViewerUI.class.getDeclaredField("html");
409             privateField.setAccessible(true);
410         } catch (SecurityException e) {
411             System.err.println("Security error: Could not change the accessibility on the html component of the help browser.");
412             System.err.println("Please submit a bug report: http://bugzilla.scilab.org");
413             e.printStackTrace();
414         } catch (NoSuchFieldException e) {
415             System.err.println("Could not find the field of the html component of the help browser.");
416             System.err.println("Please submit a bug report: http://bugzilla.scilab.org");
417             e.printStackTrace();
418         }
419
420         try {
421             this.accessibleHtml = (javax.swing.JEditorPane) privateField.get(this);
422             accessibleHtml.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
423                     public void propertyChange(java.beans.PropertyChangeEvent evt) {
424                         if (evt.getPropertyName().equals("document")) {
425                             accessibleHtml.setVisible(false);
426                         }
427                         if (evt.getPropertyName().equals("page")) {
428                             if (!accessibleHtml.isVisible()) {
429                                 modifyFont(0);
430                                 SwingUtilities.invokeLater(new Runnable() {
431                                         public void run() {
432                                             accessibleHtml.setVisible(true);
433                                         }
434                                     });
435                             }
436                         }
437                     }
438                 });
439
440             accessibleHtml.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ScilabKeyStroke.getKeyStroke("OSSCKEY shift EQUALS"), SHIFTEQ);
441             accessibleHtml.getActionMap().put(SHIFTEQ, new AbstractAction() {
442                     public void actionPerformed(ActionEvent e) {
443                         SwingScilabHelpBrowserViewer.this.increaseFont();
444                     }
445                 });
446             SwingUtilities.getAncestorOfClass(JScrollPane.class, accessibleHtml).addMouseWheelListener(this);
447         } catch (IllegalArgumentException e) {
448             System.err.println("Illegal argument in the retrieval of the html component of Javahelp");
449             e.printStackTrace();
450         } catch (IllegalAccessException e) {
451             System.err.println("Illegal access in the retrieval of the html component of Javahelp");
452             e.printStackTrace();
453         }
454     }
455
456     /**
457      * Create the popup menu on the help
458      * @param c The graphic component
459      */
460     private void createPopupMenu(JComponent c) {
461         final JPopupMenu popup = new JPopupMenu();
462
463         JMenuItem menuItem = null;
464
465         /* Execute into Scilab */
466         ActionListener actionListenerExecuteIntoScilab = new ActionListener() {
467             public void actionPerformed(ActionEvent actionEvent) {
468                 String selection = accessibleHtml.getSelectedText();
469                 if (selection == null) {
470                     ScilabHelpBrowser.getHelpBrowser().getInfoBar().setText(Messages.gettext("No text selected"));
471                 } else {
472                     ScilabConsole.getConsole().getAsSimpleConsole().sendCommandsToScilab(selection, true /* display */, true /* store in history */);
473                 }
474             }
475         };
476         menuItem = new JMenuItem(Messages.gettext("Execute into Scilab"));
477         menuItem.addActionListener(actionListenerExecuteIntoScilab);
478         if (!ScilabConsole.isExistingConsole()) { /* Only available in STD mode */
479             menuItem.setEnabled(false);
480         }
481         popup.add(menuItem);
482
483
484         /* Edit in the Scilab Text Editor */
485         ActionListener actionListenerLoadIntoTextEditor = new ActionListener() {
486             public void actionPerformed(ActionEvent actionEvent) {
487                 String selection = accessibleHtml.getSelectedText();
488                 if (selection == null) {
489                     ScilabHelpBrowser.getHelpBrowser().getInfoBar().setText(Messages.gettext("No text selected"));
490                 } else {
491                     edit(selection);
492                 }
493             }
494         };
495
496         menuItem = new JMenuItem(Messages.gettext("Edit in the Scilab Text Editor"));
497         try {
498             Class scinotesClass = Class.forName("org.scilab.modules.scinotes.SciNotes");
499         } catch (ClassNotFoundException e) {
500             /* SciNotes not available */
501             menuItem.setEnabled(false);
502         }
503         menuItem.addActionListener(actionListenerLoadIntoTextEditor);
504         popup.add(menuItem);
505         popup.addSeparator();
506
507         /* Back in the history*/
508         ActionListener actionListenerBackHistory = new ActionListener() {
509             public void actionPerformed(ActionEvent actionEvent) {
510                 DefaultHelpHistoryModel history = SwingScilabHelpBrowser.getHelpHistory();
511                 /* Not at the first position */
512                 if (history.getIndex() > 0) {
513                     SwingScilabHelpBrowser.getHelpHistory().goBack();
514                 }
515             }
516         };
517
518         menuItem = new JMenuItem(Messages.gettext("Back"));
519         menuItem.addActionListener(actionListenerBackHistory);
520         popup.add(menuItem);
521
522         /* Forward in the history*/
523         ActionListener actionListenerForwardHistory = new ActionListener() {
524             public void actionPerformed(ActionEvent actionEvent) {
525                 DefaultHelpHistoryModel history = SwingScilabHelpBrowser.getHelpHistory();
526                 /* Not at the last position */
527                 if (history.getHistory().size() != (history.getIndex() + 1)) {
528                     SwingScilabHelpBrowser.getHelpHistory().goForward();
529                 }
530             }
531         };
532
533         menuItem = new JMenuItem(Messages.gettext("Forward"));
534         menuItem.addActionListener(actionListenerForwardHistory);
535         popup.add(menuItem);
536         popup.addSeparator();
537
538         /* Copy */
539         menuItem = new JMenuItem(new DefaultEditorKit.CopyAction());
540         menuItem.setText(Messages.gettext("Copy"));
541         popup.add(menuItem);
542         popup.addSeparator();
543
544         /* Select all */
545         ActionListener actionListenerSelectAll = new ActionListener() {
546             public void actionPerformed(ActionEvent actionEvent) {
547                 accessibleHtml.selectAll();
548             }
549         };
550         menuItem = new JMenuItem(Messages.gettext("Select All"));
551         menuItem.addActionListener(actionListenerSelectAll);
552         popup.add(menuItem);
553
554         /* Edit in the Scilab Text Editor */
555         final JMenuItem helpMenuItem = new JMenuItem("Help on the selected text");
556
557         ActionListener actionListenerHelpOnKeyword= new ActionListener() {
558             public void actionPerformed(ActionEvent actionEvent) {
559                 String selection = accessibleHtml.getSelectedText();
560                 if (selection == null) {
561                     ScilabHelpBrowser.getHelpBrowser().getInfoBar().setText(Messages.gettext("No text selected"));
562                 } else {
563                     ScilabHelpBrowser.getHelpBrowser().searchKeywork(selection);
564                 }
565             }
566         };
567         PropertyChangeListener listenerTextItem = new PropertyChangeListener() {
568             public void propertyChange(PropertyChangeEvent arg0) {
569                 String keyword = accessibleHtml.getSelectedText();
570                 if (keyword == null) {
571                     helpMenuItem.setText(Messages.gettext("Help about a selected text"));
572                 } else {
573                     int nbOfDisplayedOnlyXChar = 10;
574                     if (keyword.length() > nbOfDisplayedOnlyXChar) {
575                         keyword = keyword.substring(0, nbOfDisplayedOnlyXChar) + "...";
576                     }
577                     helpMenuItem.setText(Messages.gettext("Help about '") +keyword+"'");
578                 }
579             }
580         };
581         helpMenuItem.addPropertyChangeListener(listenerTextItem);
582         helpMenuItem.addActionListener(actionListenerHelpOnKeyword);
583         popup.add(helpMenuItem);
584
585         /* Creates the Popupmenu on the component */
586         accessibleHtml.setComponentPopupMenu(popup);
587     }
588
589     /**
590      * {@inheritedDoc}
591      */
592     public void mouseWheelMoved(MouseWheelEvent e) {
593         if ((isMac && e.isMetaDown()) || e.isControlDown()) {
594             int n = e.getWheelRotation();
595             if (currentFontSize != Math.min(Math.max(0, currentFontSize + n), 6)) {
596                 modifyFont(n);
597                 ConfigManager.setHelpFontSize(currentFontSize);
598             }
599             e.consume();
600         }
601     }
602
603     /**
604      * Modify the current base font size
605      * @param s the size to add to the current size
606      */
607     public void modifyFont(final int s) {
608         SwingUtilities.invokeLater(new Runnable() {
609                 public void run() {
610                     try {
611                         HTMLDocument doc = (HTMLDocument) accessibleHtml.getDocument();
612                         StyleContext.NamedStyle style = (StyleContext.NamedStyle) doc.getStyleSheet().getStyle("body");
613                         MutableAttributeSet attr = (MutableAttributeSet) style.getResolveParent();
614                         currentFontSize = Math.min(Math.max(0, currentFontSize + s), 6);
615                         StyleConstants.setFontSize(attr, fontSizes[currentFontSize]);
616                         style.setResolveParent(attr);
617                     } catch (NullPointerException e) {
618                         // Can occur if the user is changing quickly the document
619                     }
620                 }
621             });
622     }
623
624     /**
625      * Increase the font size +1
626      */
627     public void increaseFont() {
628         if (currentFontSize != Math.min(Math.max(0, currentFontSize + 1), 6)) {
629             modifyFont(1);
630             ConfigManager.setHelpFontSize(currentFontSize);
631         }
632     }
633
634     /**
635      * Decrease the font size -1
636      */
637     public void decreaseFont() {
638         if (currentFontSize != Math.min(Math.max(0, currentFontSize - 1), 6)) {
639             modifyFont(-1);
640             ConfigManager.setHelpFontSize(currentFontSize);
641         }
642     }
643 }