JAL-2069 JAL-2808 utility method to build combobox with tooltips
[jalview.git] / src / jalview / gui / JvSwingUtils.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.util.MessageManager;
24
25 import java.awt.BorderLayout;
26 import java.awt.Color;
27 import java.awt.Component;
28 import java.awt.Font;
29 import java.awt.GridLayout;
30 import java.awt.Rectangle;
31 import java.awt.event.ActionListener;
32 import java.awt.event.MouseAdapter;
33 import java.awt.event.MouseEvent;
34 import java.util.List;
35 import java.util.Objects;
36
37 import javax.swing.AbstractButton;
38 import javax.swing.JButton;
39 import javax.swing.JComboBox;
40 import javax.swing.JComponent;
41 import javax.swing.JLabel;
42 import javax.swing.JMenu;
43 import javax.swing.JMenuItem;
44 import javax.swing.JPanel;
45 import javax.swing.JScrollBar;
46 import javax.swing.SwingConstants;
47
48 /**
49  * useful functions for building Swing GUIs
50  * 
51  * @author JimP
52  * 
53  */
54 public final class JvSwingUtils
55 {
56   /**
57    * wrap a bare html safe string to around 60 characters per line using a CSS
58    * style class specifying word-wrap and break-word
59    * 
60    * @param enclose
61    *          if true, add &lt;html&gt; wrapper tags
62    * @param ttext
63    * 
64    * @return
65    */
66   public static String wrapTooltip(boolean enclose, String ttext)
67   {
68     Objects.requireNonNull(ttext,
69             "Tootip text to format must not be null!");
70     ttext = ttext.trim();
71     boolean maxLengthExceeded = false;
72
73     if (ttext.contains("<br>"))
74     {
75       String[] htmllines = ttext.split("<br>");
76       for (String line : htmllines)
77       {
78         maxLengthExceeded = line.length() > 60;
79         if (maxLengthExceeded)
80         {
81           break;
82         }
83       }
84     }
85     else
86     {
87       maxLengthExceeded = ttext.length() > 60;
88     }
89
90     if (!maxLengthExceeded)
91     {
92       return enclose ? "<html>" + ttext + "</html>" : ttext;
93     }
94
95     return (enclose ? "<html>" : "")
96             + "<style> p.ttip {width: 350; text-align: justify; word-wrap: break-word;}</style><p class=\"ttip\">"
97             + ttext + "</p>" + ((enclose ? "</html>" : ""));
98
99   }
100
101   public static JButton makeButton(String label, String tooltip,
102           ActionListener action)
103   {
104     JButton button = new JButton();
105     button.setText(label);
106     // TODO: get the base font metrics for the Jalview gui from somewhere
107     button.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
108     button.setForeground(Color.black);
109     button.setHorizontalAlignment(SwingConstants.CENTER);
110     button.setToolTipText(tooltip);
111     button.addActionListener(action);
112     return button;
113   }
114
115   /**
116    * find or add a submenu with the given title in the given menu
117    * 
118    * @param menu
119    * @param submenu
120    * @return the new or existing submenu
121    */
122   public static JMenu findOrCreateMenu(JMenu menu, String submenu)
123   {
124     JMenu submenuinstance = null;
125     for (int i = 0, iSize = menu.getMenuComponentCount(); i < iSize; i++)
126     {
127       if (menu.getMenuComponent(i) instanceof JMenu
128               && ((JMenu) menu.getMenuComponent(i)).getText()
129                       .equals(submenu))
130       {
131         submenuinstance = (JMenu) menu.getMenuComponent(i);
132       }
133     }
134     if (submenuinstance == null)
135     {
136       submenuinstance = new JMenu(submenu);
137       menu.add(submenuinstance);
138     }
139     return submenuinstance;
140
141   }
142
143   /**
144    * 
145    * @param panel
146    * @param tooltip
147    * @param label
148    * @param valBox
149    * @return the GUI element created that was added to the layout so it's
150    *         attributes can be changed.
151    */
152   public static JPanel addtoLayout(JPanel panel, String tooltip,
153           JComponent label, JComponent valBox)
154   {
155     JPanel laypanel = new JPanel(new GridLayout(1, 2));
156     JPanel labPanel = new JPanel(new BorderLayout());
157     JPanel valPanel = new JPanel();
158     labPanel.setBounds(new Rectangle(7, 7, 158, 23));
159     valPanel.setBounds(new Rectangle(172, 7, 270, 23));
160     labPanel.add(label, BorderLayout.WEST);
161     valPanel.add(valBox);
162     laypanel.add(labPanel);
163     laypanel.add(valPanel);
164     valPanel.setToolTipText(tooltip);
165     labPanel.setToolTipText(tooltip);
166     valBox.setToolTipText(tooltip);
167     panel.add(laypanel);
168     panel.validate();
169     return laypanel;
170   }
171
172   public static void mgAddtoLayout(JPanel cpanel, String tooltip,
173           JLabel jLabel, JComponent name)
174   {
175     mgAddtoLayout(cpanel, tooltip, jLabel, name, null);
176   }
177
178   public static void mgAddtoLayout(JPanel cpanel, String tooltip,
179           JLabel jLabel, JComponent name, String params)
180   {
181     cpanel.add(jLabel);
182     if (params == null)
183     {
184       cpanel.add(name);
185     }
186     else
187     {
188       cpanel.add(name, params);
189     }
190     name.setToolTipText(tooltip);
191     jLabel.setToolTipText(tooltip);
192   }
193
194   /**
195    * standard font for labels and check boxes in dialog boxes
196    * 
197    * @return
198    */
199
200   public static Font getLabelFont()
201   {
202     return getLabelFont(false, false);
203   }
204
205   public static Font getLabelFont(boolean bold, boolean italic)
206   {
207     return new java.awt.Font("Verdana",
208             (!bold && !italic) ? Font.PLAIN
209                     : (bold ? Font.BOLD : 0) + (italic ? Font.ITALIC : 0),
210             11);
211   }
212
213   /**
214    * standard font for editable text areas
215    * 
216    * @return
217    */
218   public static Font getTextAreaFont()
219   {
220     return getLabelFont(false, false);
221   }
222
223   /**
224    * clean up a swing menu. Removes any empty submenus without selection
225    * listeners.
226    * 
227    * @param webService
228    */
229   public static void cleanMenu(JMenu webService)
230   {
231     for (int i = 0; i < webService.getItemCount();)
232     {
233       JMenuItem item = webService.getItem(i);
234       if (item instanceof JMenu && ((JMenu) item).getItemCount() == 0)
235       {
236         webService.remove(i);
237       }
238       else
239       {
240         i++;
241       }
242     }
243   }
244
245   /**
246    * Returns the proportion of its range that a scrollbar's position represents,
247    * as a value between 0 and 1. For example if the whole range is from 0 to
248    * 200, then a position of 40 gives proportion = 0.2.
249    * 
250    * @see http://www.javalobby.org/java/forums/t33050.html#91885334
251    * 
252    * @param scroll
253    * @return
254    */
255   public static float getScrollBarProportion(JScrollBar scroll)
256   {
257     /*
258      * The extent (scroll handle width) deduction gives the true operating range
259      * of possible positions.
260      */
261     int possibleRange = scroll.getMaximum() - scroll.getMinimum()
262             - scroll.getModel().getExtent();
263     float valueInRange = scroll.getValue()
264             - (scroll.getModel().getExtent() / 2f);
265     float proportion = valueInRange / possibleRange;
266     return proportion;
267   }
268
269   /**
270    * Returns the scroll bar position in its range that would match the given
271    * proportion (between 0 and 1) of the whole. For example if the whole range
272    * is from 0 to 200, then a proportion of 0.25 gives position 50.
273    * 
274    * @param scrollbar
275    * @param proportion
276    * @return
277    */
278   public static int getScrollValueForProportion(JScrollBar scrollbar,
279           float proportion)
280   {
281     /*
282      * The extent (scroll handle width) deduction gives the true operating range
283      * of possible positions.
284      */
285     float fraction = proportion
286             * (scrollbar.getMaximum() - scrollbar.getMinimum()
287                     - scrollbar.getModel().getExtent())
288             + (scrollbar.getModel().getExtent() / 2f);
289     return Math.min(Math.round(fraction), scrollbar.getMaximum());
290   }
291
292   public static void jvInitComponent(AbstractButton comp, String i18nString)
293   {
294     setColorAndFont(comp);
295     if (i18nString != null && !i18nString.isEmpty())
296     {
297       comp.setText(MessageManager.getString(i18nString));
298     }
299   }
300
301   public static void jvInitComponent(JComponent comp)
302   {
303     setColorAndFont(comp);
304   }
305
306   private static void setColorAndFont(JComponent comp)
307   {
308     comp.setBackground(Color.white);
309     comp.setFont(JvSwingUtils.getLabelFont());
310   }
311
312   /**
313    * A helper method to build a drop-down choice of values, with tooltips for
314    * the entries
315    * 
316    * @param entries
317    * @param tooltips
318    */
319   public static JComboBox<String> buildComboWithTooltips(
320           List<String> entries, List<String> tooltips)
321   {
322     JComboBox<String> combo = new JComboBox<>();
323     final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
324     combo.setRenderer(renderer);
325     for (String attName : entries)
326     {
327       combo.addItem(attName);
328     }
329     renderer.setTooltips(tooltips);
330     final MouseAdapter mouseListener = new MouseAdapter()
331     {
332       @Override
333       public void mouseEntered(MouseEvent e)
334       {
335         int j = combo.getSelectedIndex();
336         if (j > -1)
337         {
338           combo.setToolTipText(tooltips.get(j));
339         }
340       }
341       @Override
342       public void mouseExited(MouseEvent e)
343       {
344         combo.setToolTipText(null);
345       }
346     };
347     for (Component c : combo.getComponents())
348     {
349       c.addMouseListener(mouseListener);
350     }
351     return combo;
352   }
353
354 }