Merge branch 'develop' into developtomchmmer
[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.Color;
26 import java.awt.Component;
27 import java.awt.Container;
28 import java.awt.Font;
29 import java.awt.event.ActionListener;
30 import java.awt.event.MouseAdapter;
31 import java.awt.event.MouseEvent;
32 import java.util.List;
33 import java.util.Objects;
34
35 import javax.swing.AbstractButton;
36 import javax.swing.BorderFactory;
37 import javax.swing.JButton;
38 import javax.swing.JComboBox;
39 import javax.swing.JComponent;
40 import javax.swing.JMenu;
41 import javax.swing.JMenuItem;
42 import javax.swing.JScrollBar;
43 import javax.swing.SwingConstants;
44 import javax.swing.border.Border;
45 import javax.swing.border.TitledBorder;
46
47 /**
48  * useful functions for building Swing GUIs
49  * 
50  * @author JimP
51  * 
52  */
53 public final class JvSwingUtils
54 {
55   /**
56    * wrap a bare html safe string to around 60 characters per line using a CSS
57    * style class specifying word-wrap and break-word
58    * 
59    * @param enclose
60    *          if true, add &lt;html&gt; wrapper tags
61    * @param ttext
62    * 
63    * @return
64    */
65   public static String wrapTooltip(boolean enclose, String ttext)
66   {
67     Objects.requireNonNull(ttext,
68             "Tootip text to format must not be null!");
69     ttext = ttext.trim();
70     boolean maxLengthExceeded = false;
71
72     if (ttext.contains("<br>"))
73     {
74       String[] htmllines = ttext.split("<br>");
75       for (String line : htmllines)
76       {
77         maxLengthExceeded = line.length() > 60;
78         if (maxLengthExceeded)
79         {
80           break;
81         }
82       }
83     }
84     else
85     {
86       maxLengthExceeded = ttext.length() > 60;
87     }
88
89     if (!maxLengthExceeded)
90     {
91       return enclose ? "<html>" + ttext + "</html>" : ttext;
92     }
93
94     return (enclose ? "<html>" : "")
95             + "<style> p.ttip {width: 350; text-align: justify; word-wrap: break-word;}</style><p class=\"ttip\">"
96             + ttext + "</p>" + ((enclose ? "</html>" : ""));
97
98   }
99
100   public static JButton makeButton(String label, String tooltip,
101           ActionListener action)
102   {
103     JButton button = new JButton();
104     button.setText(label);
105     // TODO: get the base font metrics for the Jalview gui from somewhere
106     button.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
107     button.setForeground(Color.black);
108     button.setHorizontalAlignment(SwingConstants.CENTER);
109     button.setToolTipText(tooltip);
110     button.addActionListener(action);
111     return button;
112   }
113
114   /**
115    * find or add a submenu with the given title in the given menu
116    * 
117    * @param menu
118    * @param submenu
119    * @return the new or existing submenu
120    */
121   public static JMenu findOrCreateMenu(JMenu menu, String submenu)
122   {
123     JMenu submenuinstance = null;
124     for (int i = 0, iSize = menu.getMenuComponentCount(); i < iSize; i++)
125     {
126       if (menu.getMenuComponent(i) instanceof JMenu
127               && ((JMenu) menu.getMenuComponent(i)).getText()
128                       .equals(submenu))
129       {
130         submenuinstance = (JMenu) menu.getMenuComponent(i);
131       }
132     }
133     if (submenuinstance == null)
134     {
135       submenuinstance = new JMenu(submenu);
136       menu.add(submenuinstance);
137     }
138     return submenuinstance;
139
140   }
141
142   /**
143    * A convenience method that that adds a component with label to a container,
144    * sets a tooltip on both component and label, and optionally specifies layout
145    * constraints for the added component (but not the label)
146    * 
147    * @param container
148    * @param tooltip
149    * @param label
150    * @param comp
151    * @param constraints
152    */
153   public static void addtoLayout(Container container, String tooltip,
154           JComponent label, JComponent comp, String constraints)
155   {
156     container.add(label);
157     container.add(comp, constraints);
158     comp.setToolTipText(tooltip); // this doesn't seem to show?
159     label.setToolTipText(tooltip);
160   }
161
162   /**
163    * standard font for labels and check boxes in dialog boxes
164    * 
165    * @return
166    */
167
168   public static Font getLabelFont()
169   {
170     return getLabelFont(false, false);
171   }
172
173   public static Font getLabelFont(boolean bold, boolean italic)
174   {
175     return new java.awt.Font("Verdana",
176             (!bold && !italic) ? Font.PLAIN
177                     : (bold ? Font.BOLD : 0) + (italic ? Font.ITALIC : 0),
178             11);
179   }
180
181   /**
182    * standard font for editable text areas
183    * 
184    * @return
185    */
186   public static Font getTextAreaFont()
187   {
188     return getLabelFont(false, false);
189   }
190
191   /**
192    * clean up a swing menu. Removes any empty submenus without selection
193    * listeners.
194    * 
195    * @param webService
196    */
197   public static void cleanMenu(JMenu webService)
198   {
199     for (int i = 0; i < webService.getItemCount();)
200     {
201       JMenuItem item = webService.getItem(i);
202       if (item instanceof JMenu && ((JMenu) item).getItemCount() == 0)
203       {
204         webService.remove(i);
205       }
206       else
207       {
208         i++;
209       }
210     }
211   }
212
213   /**
214    * Returns the proportion of its range that a scrollbar's position represents,
215    * as a value between 0 and 1. For example if the whole range is from 0 to
216    * 200, then a position of 40 gives proportion = 0.2.
217    * 
218    * @see http://www.javalobby.org/java/forums/t33050.html#91885334
219    * 
220    * @param scroll
221    * @return
222    */
223   public static float getScrollBarProportion(JScrollBar scroll)
224   {
225     /*
226      * The extent (scroll handle width) deduction gives the true operating range
227      * of possible positions.
228      */
229     int possibleRange = scroll.getMaximum() - scroll.getMinimum()
230             - scroll.getModel().getExtent();
231     float valueInRange = scroll.getValue()
232             - (scroll.getModel().getExtent() / 2f);
233     float proportion = valueInRange / possibleRange;
234     return proportion;
235   }
236
237   /**
238    * Returns the scroll bar position in its range that would match the given
239    * proportion (between 0 and 1) of the whole. For example if the whole range
240    * is from 0 to 200, then a proportion of 0.25 gives position 50.
241    * 
242    * @param scrollbar
243    * @param proportion
244    * @return
245    */
246   public static int getScrollValueForProportion(JScrollBar scrollbar,
247           float proportion)
248   {
249     /*
250      * The extent (scroll handle width) deduction gives the true operating range
251      * of possible positions.
252      */
253     float fraction = proportion
254             * (scrollbar.getMaximum() - scrollbar.getMinimum()
255                     - scrollbar.getModel().getExtent())
256             + (scrollbar.getModel().getExtent() / 2f);
257     return Math.min(Math.round(fraction), scrollbar.getMaximum());
258   }
259
260   public static void jvInitComponent(AbstractButton comp, String i18nString)
261   {
262     setColorAndFont(comp);
263     if (i18nString != null && !i18nString.isEmpty())
264     {
265       comp.setText(MessageManager.getString(i18nString));
266     }
267   }
268
269   public static void jvInitComponent(JComponent comp)
270   {
271     setColorAndFont(comp);
272   }
273
274   private static void setColorAndFont(JComponent comp)
275   {
276     comp.setBackground(Color.white);
277     comp.setFont(JvSwingUtils.getLabelFont());
278   }
279
280   /**
281    * A helper method to build a drop-down choice of values, with tooltips for
282    * the entries
283    * 
284    * @param entries
285    * @param tooltips
286    */
287   public static JComboBox<Object> buildComboWithTooltips(
288           List<Object> entries, List<String> tooltips)
289   {
290     JComboBox<Object> combo = new JComboBox<>();
291     final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
292     combo.setRenderer(renderer);
293     for (Object attName : entries)
294     {
295       combo.addItem(attName);
296     }
297     renderer.setTooltips(tooltips);
298     final MouseAdapter mouseListener = new MouseAdapter()
299     {
300       @Override
301       public void mouseEntered(MouseEvent e)
302       {
303         int j = combo.getSelectedIndex();
304         if (j > -1)
305         {
306           combo.setToolTipText(tooltips.get(j));
307         }
308       }
309       @Override
310       public void mouseExited(MouseEvent e)
311       {
312         combo.setToolTipText(null);
313       }
314     };
315     for (Component c : combo.getComponents())
316     {
317       c.addMouseListener(mouseListener);
318     }
319     return combo;
320   }
321
322   /**
323    * Adds a titled border to the component in the default font and position (top
324    * left), optionally with italic text
325    * 
326    * @param comp
327    * @param title
328    * @param italic
329    */
330   public static TitledBorder createTitledBorder(JComponent comp,
331           String title, boolean italic)
332   {
333     Font font = comp.getFont();
334     if (italic)
335     {
336       font = new Font(font.getName(), Font.ITALIC, font.getSize());
337     }
338     Border border = BorderFactory.createTitledBorder("");
339     TitledBorder titledBorder = BorderFactory.createTitledBorder(border,
340             title, TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION,
341             font);
342     comp.setBorder(titledBorder);
343
344     return titledBorder;
345   }
346
347 }