package com.vistatec.ocelot.ui; import java.awt.Dimension; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.Locale; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import javax.swing.text.PlainDocument; /** * This tool lets user to choose specific font family and size for a text to * display. The list of available font families depends on the specific * language. */ public class OcelotFontTool extends JPanel implements ItemListener { /** Serial version UID. */ private static final long serialVersionUID = 1L; /** * Min value allowed for size combo. It is the min value used by MS Office * Word. */ private static final int MIN_SIZE_VALUE = 1; /** * Max value allowed for size combo. It is the max value used by MS Office * Word. */ private static final int MAX_SIZE_VALUE = 1638; /** Font family combo width. */ private static final int FONT_FAMILY_WIDTH = 150; /** Font family combo max width. */ private static final int FONT_FAMILY_MAX_WIDTH = 180; /** Font family combo height. */ private static final int FONT_FAMILY_HEIGHT = 20; /** Font size combo width. */ private static final int FONT_SIZE_WIDTH = 50; /** Font size combo max width. */ private static final int FONT_SIZE_MAX_WIDTH = 80; /** Font size combo height. */ private static final int FONT_SIZE_HEIGHT = 20; /** Model for the size combo box. */ private final Integer[] sizeModel = { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 }; /** The font family combo. */ private JComboBox<String> cmbFontFamily; /** The font size combo. */ private JComboBox<Integer> cmbFontSize; /** The external listener to this tool events. */ private ItemListener externalListener; /** * Constructor. * * @param toolName * this is the tool name. It will be displayed as the text of the * label describing the tool. * @param listener * the listener to this tool events. */ public OcelotFontTool(final String toolName, final ItemListener listener) { this(toolName, null, listener); } /** * Constructor. * * @param toolName * this is the tool name. It will be displayed as the text of the * label describing the tool. * @param languageTag * the IETF BCP 47 language tag string used to detect the locale * for retrieving the appropriate fonts list. It is used with * this method {@link java.util.Locale#forLanguageTag(String)} * @param listener * the listener to this tool events. */ public OcelotFontTool(final String toolName, final String languageTag, final ItemListener listener) { this.externalListener = listener; makeTool(toolName, languageTag); } /** * Builds the tool. * * @param toolName * the tool name * @param languageTag * the language tag */ private void makeTool(String toolName, String languageTag) { // makes the tool transparent to make it appear perfectly embedded into // the tool bar setOpaque(false); // builds the tool components buildComponents(toolName); // sets the panel layout and adds the components into the tool panel. setLayout(new GridBagLayout()); GridBagConstraints gridBag = new GridBagConstraints(); // insert the label displaying the tool name JLabel lblSourceFont = new JLabel(toolName); gridBag.gridx = 0; gridBag.gridy = 0; gridBag.anchor = GridBagConstraints.WEST; gridBag.insets = new Insets(5, 5, 5, 5); add(lblSourceFont, gridBag); // Insert Source Font Family Combo gridBag.gridx = 1; gridBag.insets = new Insets(5, 0, 5, 0); add(cmbFontFamily, gridBag); // Insert Source Font Size Combo gridBag.gridx = 2; gridBag.insets = new Insets(5, 0, 5, 5); add(cmbFontSize, gridBag); // loads font and size lists. loadFontsAndSizes(languageTag); } /** * Loads the available fonts and sizes lists depending on the specific * language. * * @param languageTag * the IETF BCP 47 language tag. */ public void loadFontsAndSizes(String languageTag) { if (languageTag != null) { // retrieves the available fonts for the language String[] sourceFontsModel = GraphicsEnvironment .getLocalGraphicsEnvironment().getAvailableFontFamilyNames( Locale.forLanguageTag(languageTag)); cmbFontFamily.setModel(new DefaultComboBoxModel<String>( sourceFontsModel)); cmbFontFamily.setEnabled(true); cmbFontSize.setModel(new DefaultComboBoxModel<Integer>(sizeModel)); cmbFontSize.setEnabled(true); } else { // Disables the combo boxes if there is no language tag. cmbFontFamily.setEnabled(false); cmbFontSize.setEnabled(false); } } /** * Builds the tool components. * * @param toolName * the tool name */ private void buildComponents(final String toolName) { // builds the font family combo cmbFontFamily = new JComboBox<String>(); cmbFontFamily.setName(toolName); cmbFontFamily.addItemListener(this); cmbFontFamily.setPreferredSize(new Dimension(FONT_FAMILY_WIDTH, FONT_FAMILY_HEIGHT)); cmbFontFamily.setMinimumSize(new Dimension(FONT_FAMILY_WIDTH, FONT_FAMILY_HEIGHT)); cmbFontFamily.setMaximumSize(new Dimension(FONT_FAMILY_MAX_WIDTH, FONT_FAMILY_HEIGHT)); // builds the font size combo. cmbFontSize = new JComboBox<Integer>(); cmbFontSize.setName(toolName); cmbFontSize.addItemListener(this); cmbFontSize.setPreferredSize(new Dimension(FONT_SIZE_WIDTH, FONT_SIZE_HEIGHT)); cmbFontSize.setMaximumSize(new Dimension(FONT_SIZE_MAX_WIDTH, FONT_SIZE_HEIGHT)); // the font size is editable. Users can type the desired value into the // size combo. It will accept even those values not listed in the size // model. cmbFontSize.setEditable(true); // Sets a document to the size combo, in order to validate inserted text ((JTextComponent) cmbFontSize.getEditor().getEditorComponent()) .setDocument(new IntegerDocument()); } /** * Selects the appropriate font family and size according to the font passed * as parameter. * * @param font * a <code>java.awt.Font</code> object. */ public void setSelectedFont(final Font font) { if (font != null) { cmbFontFamily.setSelectedItem(font.getFamily()); cmbFontSize.setSelectedItem(font.getSize()); } } /** * Gets the selected font. * * @return a <code>java.awt.Font</code> object, built by using selected font * family and size. The Font.PLAIN style is used. */ public Font getSelectedFont() { Font font = null; if (cmbFontFamily != null && cmbFontFamily.getSelectedItem() != null && cmbFontSize != null && cmbFontSize.getSelectedItem() != null) { font = new Font((String) cmbFontFamily.getSelectedItem(), Font.PLAIN, (Integer) cmbFontSize.getSelectedItem()); } return font; } /** * Handles the event one of the combo has changed the selected item. If the * font family combo item changes, no checks take place; if the font size * selected item changes, it checks the new value falls in the allowed * interval. If all the checks are successful, then the external listener is * notified. (non-Javadoc) * * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) */ @Override public void itemStateChanged(ItemEvent e) { boolean canNotifyOutside = true; // This method is called twice: one for DESELECTED item and one for // SELECTED item. // Handles only the SELECTED item case. if (e.getStateChange() == ItemEvent.SELECTED) { // Make checks only if the size combo has changed if (e.getSource().equals(cmbFontSize)) { int selectedItem = (Integer) cmbFontSize.getSelectedItem(); if (selectedItem < MIN_SIZE_VALUE || selectedItem > MAX_SIZE_VALUE) { canNotifyOutside = false; // Display a message to the user JOptionPane.showMessageDialog(this, "Please, insert a value between " + MIN_SIZE_VALUE + " and " + MAX_SIZE_VALUE + ".", "Ocelot", JOptionPane.WARNING_MESSAGE); } } // If the checks are successful, notify the external listener if (canNotifyOutside) { externalListener.itemStateChanged(e); } } } /** * This document let you validate text inserted in any Swing text field. It * let user insert only integer value into the text field. */ private class IntegerDocument extends PlainDocument { /** serial version uid. */ private static final long serialVersionUID = -412079755824907144L; /** * Checks if the string resulting from the insertion of the parameter * string at the given offset is an integer. If it is the case, the * string is inserted at the right offset; otherwise the current string * does not change. * * @see javax.swing.text.PlainDocument#insertString(int, * java.lang.String, javax.swing.text.AttributeSet) */ @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str != null) { boolean ok = true; final String currText = getText(0, getLength()); final String newText = currText.substring(0, offs) + str + currText.substring(offs, getLength()); try { Integer.parseInt(newText); } catch (NumberFormatException e) { ok = false; } if (ok) { super.insertString(offs, str, a); } } } } }