1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: FontChooser.java 2223 2012-01-26 21:28:02Z dmsmith $
21   */
22  package org.crosswire.common.swing;
23  
24  import java.awt.BorderLayout;
25  import java.awt.Component;
26  import java.awt.FlowLayout;
27  import java.awt.Font;
28  import java.awt.GraphicsEnvironment;
29  import java.awt.GridLayout;
30  import java.awt.event.ActionEvent;
31  import java.awt.event.ActionListener;
32  import java.awt.event.ItemEvent;
33  import java.awt.event.ItemListener;
34  
35  import javax.swing.AbstractListModel;
36  import javax.swing.BorderFactory;
37  import javax.swing.ComboBoxModel;
38  import javax.swing.DefaultListCellRenderer;
39  import javax.swing.JButton;
40  import javax.swing.JCheckBox;
41  import javax.swing.JComboBox;
42  import javax.swing.JDialog;
43  import javax.swing.JFrame;
44  import javax.swing.JLabel;
45  import javax.swing.JList;
46  import javax.swing.JPanel;
47  import javax.swing.SwingUtilities;
48  
49  /**
50   * FontChooserBean allows the user to select a font in a similar way to a
51   * FileSelectionDialog.
52   * 
53   * @see gnu.lgpl.License for license details.<br>
54   *      The copyright to this program is held by it's authors.
55   * @author Joe Walker [joe at eireneh dot com]
56   */
57  public class FontChooser extends JPanel {
58      /**
59       * Create a FontChooser.
60       */
61      public FontChooser() {
62          ItemListener changer = new ItemListener() {
63              public void itemStateChanged(ItemEvent ev) {
64                  fireStateChange();
65              }
66          };
67  
68          font = DEFAULT_FONT.getFont();
69          name = new FontNameComboBox();
70          name.setModel(new CustomComboBoxModel());
71          name.setRenderer(new CustomListCellRenderer());
72          name.setSelectedItem(font.deriveFont(Font.PLAIN, RENDERED_FONT_SIZE));
73          name.addItemListener(changer);
74  
75          size = new JComboBox();
76          size.setRenderer(new NumberCellRenderer());
77          for (int i = MIN_FONT_SIZE; i <= MAX_FONT_SIZE; i++) {
78              size.addItem(Integer.valueOf(i));
79          }
80  
81          size.setSelectedItem(Integer.valueOf(RENDERED_FONT_SIZE));
82          size.addItemListener(changer);
83  
84          // TRANSLATOR: This is an option for a bold font.
85          bold = new JCheckBox(CWMsg.gettext("Bold"));
86          bold.setSelected(font.isBold());
87          bold.addItemListener(changer);
88  
89          // TRANSLATOR: This is an option for an italic font.
90          italic = new JCheckBox(CWMsg.gettext("Italic"));
91          italic.setSelected(font.isItalic());
92          italic.addItemListener(changer);
93  
94          setLayout(new GridLayout(2, 2, 5, 5));
95  
96          add(name);
97          add(size);
98          add(bold);
99          add(italic);
100         GuiUtil.applyDefaultOrientation(this);
101     }
102 
103     /**
104      * Display a FontChooser in a dialog
105      */
106     public static Font showDialog(Component parent, String title, Font initial) {
107         final FontChooser fontc = new FontChooser();
108 
109         Component root = SwingUtilities.getRoot(parent);
110 
111         fontc.dialog = (root instanceof JFrame) ? new JDialog((JFrame) root, title, true) : new JDialog((JDialog) root, title, true);
112 
113         Font font = (initial != null) ? initial : DEFAULT_FONT.getFont();
114         fontc.name.setSelectedItem(font);
115         fontc.bold.setSelected(font.isBold());
116         fontc.italic.setSelected(font.isItalic());
117         fontc.size.setSelectedItem(Integer.valueOf(font.getSize()));
118 
119         final ActionFactory actions = new ActionFactory(fontc);
120 
121         // TRANSLATOR: This is the text on an "OK" button.
122         JButton ok = actions.createJButton(actions.addAction("OK", CWMsg.gettext("OK")), new ActionListener() {
123             public void actionPerformed(ActionEvent e) {
124                 fontc.dialog.dispose();
125             }
126         });
127 
128         // TRANSLATOR: This is the text on a "Cancel" button.
129         JButton cancel = actions.createJButton(actions.addAction("Cancel", CWMsg.gettext("Cancel")), new ActionListener() {
130             public void actionPerformed(ActionEvent ex) {
131                 fontc.dialog.setVisible(false);
132                 fontc.font = null;
133             }
134         });
135 
136         JPanel buttons = new JPanel();
137         buttons.setLayout(new FlowLayout());
138         buttons.add(ok);
139         buttons.add(cancel);
140 
141         // TRANSLATOR: Label indicating that the user should select a font.
142         fontc.setBorder(BorderFactory.createTitledBorder(CWMsg.gettext("Select Font")));
143 
144         fontc.dialog.getRootPane().setDefaultButton(ok);
145         fontc.dialog.getContentPane().setLayout(new BorderLayout());
146         fontc.dialog.getContentPane().add(fontc, BorderLayout.NORTH);
147         fontc.dialog.getContentPane().add(buttons, BorderLayout.SOUTH);
148         fontc.dialog.setSize(800, 500);
149         fontc.dialog.pack();
150         GuiUtil.centerOnScreen(fontc.dialog);
151         GuiUtil.applyDefaultOrientation(fontc.dialog);
152         fontc.dialog.setVisible(true);
153 
154         fontc.dialog.dispose();
155 
156         return fontc.font;
157     }
158 
159     /**
160      * Set the Font displayed
161      * 
162      * @param newFont
163      *            The current Font
164      */
165     public void setStyle(Font newFont) {
166         suppressEvents = true;
167 
168         if (newFont == null) {
169             return;
170         }
171 
172         CustomComboBoxModel model = (CustomComboBoxModel) name.getModel();
173         model.setSelectedItem(newFont.deriveFont(Font.PLAIN, RENDERED_FONT_SIZE));
174 
175         bold.setSelected(newFont.isBold());
176         italic.setSelected(newFont.isItalic());
177         size.setSelectedItem(Integer.valueOf(newFont.getSize()));
178 
179         suppressEvents = false;
180         fireStateChange();
181     }
182 
183     /**
184      * @return The currently selected font
185      */
186     public Font getStyle() {
187         Font selected = (Font) name.getSelectedItem();
188 
189         if (selected == null) {
190             return DEFAULT_FONT.getFont();
191         }
192 
193         int font_style = (bold.isSelected() ? Font.BOLD : Font.PLAIN) | (italic.isSelected() ? Font.ITALIC : Font.PLAIN);
194         int font_size = ((Integer) size.getSelectedItem()).intValue();
195 
196         return selected.deriveFont(font_style, font_size);
197     }
198 
199     /**
200      * When something changes we must inform out listeners.
201      */
202     protected void fireStateChange() {
203         Font old = font;
204         font = getStyle();
205 
206         if (!suppressEvents) {
207             firePropertyChange(PROPERTY_STYLE, old, font);
208         }
209     }
210 
211     /**
212      * Model for the font style drop down
213      */
214     static class CustomComboBoxModel extends AbstractListModel implements ComboBoxModel {
215         /**
216          * Create a custom data model for a JComboBox
217          */
218         protected CustomComboBoxModel() {
219             String[] names = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
220 
221             fonts = new Font[names.length];
222 
223             for (int i = 0; i < fonts.length; i++) {
224                 // We need to exclude certain fonts that cause the JVM to crash.
225                 // BUG_PARADE(DMS): 6376296
226                 // It will be fixed in Java 1.6 (Mustang)
227                 if ("padmaa".equals(names[i]) || "Rekha".equals(names[i]) || names[i].indexOf("Lohit") > -1 || names[i].indexOf("aakar") > -1)
228                 {
229                     continue;
230                 }
231 
232                 // Add good fonts to total font listing
233                 fonts[fontCount++] = new Font(names[i], Font.PLAIN, RENDERED_FONT_SIZE);
234             }
235         }
236 
237         /*
238          * (non-Javadoc)
239          * 
240          * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
241          */
242         public void setSelectedItem(Object selection) {
243             this.selection = selection;
244             fireContentsChanged(this, -1, -1);
245         }
246 
247         /*
248          * (non-Javadoc)
249          * 
250          * @see javax.swing.ComboBoxModel#getSelectedItem()
251          */
252         public Object getSelectedItem() {
253             return selection;
254         }
255 
256         /*
257          * (non-Javadoc)
258          * 
259          * @see javax.swing.ListModel#getSize()
260          */
261         public int getSize() {
262             return fontCount;
263         }
264 
265         /*
266          * (non-Javadoc)
267          * 
268          * @see javax.swing.ListModel#getElementAt(int)
269          */
270         public Object getElementAt(int index) {
271             return fonts[index];
272         }
273 
274         /**
275          * The total number of fonts. Note, this may be less than or equal to
276          * fonts.length.
277          */
278         private int fontCount;
279 
280         /**
281          * An array of the fonts themselves. Note the array is as big as the
282          * total number of fonts in the system.
283          */
284         private Font[] fonts;
285 
286         /**
287          * The currently selected item
288          */
289         private Object selection;
290 
291         /**
292          * Serialization ID
293          */
294         private static final long serialVersionUID = 3258129150505071664L;
295     }
296 
297     /**
298      * An extension of JLabel that resets it's font so that it can be used to
299      * render the items in a JComboBox
300      */
301     static class CustomListCellRenderer extends DefaultListCellRenderer {
302         public CustomListCellRenderer() {
303             GuiUtil.applyDefaultOrientation(this);
304         }
305 
306         /*
307          * (non-Javadoc)
308          * 
309          * @see
310          * javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing
311          * .JList, java.lang.Object, int, boolean, boolean)
312          */
313         @Override
314         public Component getListCellRendererComponent(JList listbox, Object value, int index, boolean selected, boolean focus) {
315             Font defaultFont = DEFAULT_FONT.getFont();
316             if (value == null) {
317                 setText("<null>");
318                 setFont(defaultFont);
319             } else {
320                 Font afont = (Font) value;
321                 setText(afont.getFamily());
322                 setFont(defaultFont); // afont); // Some fonts cannot display
323                 // their own name.
324             }
325 
326             return this;
327         }
328 
329         /**
330          * Serialization ID
331          */
332         private static final long serialVersionUID = 3256726195025358905L;
333     }
334 
335     /**
336      * An extension of JComboBox that selects a font in the combo based on it's
337      * name, not object equivalence.
338      */
339     static class FontNameComboBox extends JComboBox {
340         @Override
341         public void setSelectedItem(Object anObject) {
342             if ((selectedItemReminder == null || !selectedItemReminder.equals(anObject)) && (anObject instanceof Font)) {
343                 String fontName = ((Font) anObject).getName();
344                 for (int i = 0; i < dataModel.getSize(); i++) {
345                     Object element = dataModel.getElementAt(i);
346                     if (element instanceof Font && (((Font) element).getName().equals(fontName))) {
347                         super.setSelectedItem(element);
348                         return;
349                     }
350                 }
351             }
352         }
353 
354         /**
355          * Serialization ID
356          */
357         private static final long serialVersionUID = -7394816349446551753L;
358     }
359 
360     public static final String PROPERTY_STYLE = "style";
361 
362     /**
363      * A label that we can use to get defaults
364      */
365     protected static final JLabel DEFAULT_FONT = new JLabel();
366 
367     /**
368      * The dialog box
369      */
370     protected JDialog dialog;
371 
372     /**
373      * The current font
374      */
375     protected Font font;
376 
377     /**
378      * The minimum size of the font.
379      */
380     private static final int MIN_FONT_SIZE = 5;
381 
382     /**
383      * The minimum size of the font.
384      */
385     private static final int MAX_FONT_SIZE = 72;
386 
387     /**
388      * The default size of the rendered font
389      */
390     private static final int RENDERED_FONT_SIZE = 16;
391 
392     /**
393      * The choice of font name
394      */
395     protected JComboBox name;
396 
397     /**
398      * Bold font?
399      */
400     protected JCheckBox bold;
401 
402     /**
403      * Italic font?
404      */
405     protected JCheckBox italic;
406 
407     /**
408      * The font size
409      */
410     protected JComboBox size;
411 
412     /**
413      * Are we doing some processing, that makes us not want to send events?
414      */
415     protected boolean suppressEvents;
416 
417     /**
418      * Serialization ID
419      */
420     private static final long serialVersionUID = 3978992071925250097L;
421 }
422