ca.sqlpower.swingui.FontSelector.java Source code

Java tutorial

Introduction

Here is the source code for ca.sqlpower.swingui.FontSelector.java

Source

/*
 * Copyright (c) 2008, SQL Power Group Inc.
 *
 * This file is part of SQL Power Library.
 *
 * SQL Power Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SQL Power Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */

package ca.sqlpower.swingui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Arrays;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.apache.log4j.Logger;

import ca.sqlpower.dao.session.SPFontLoader;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * This class is a {@link DataEntryPanel} that asks the user to pick a font.
 */
public class FontSelector implements DataEntryPanel {

    private static final Logger logger = Logger.getLogger(FontSelector.class);

    private static enum FontStyle {
        PLAIN("Plain", Font.PLAIN), BOLD("Bold", Font.BOLD), ITALIC("Italic",
                Font.ITALIC), BOLD_ITALIC("Bold Italic", Font.BOLD | Font.ITALIC);

        private final String humanName;
        private final int styleCode;

        FontStyle(String name, int styleCode) {
            humanName = name;
            this.styleCode = styleCode;
        }

        /**
         * Returns a font derived from the given font which has the style
         * specified by this enum constant.
         * 
         * @param f
         *            The original font
         * @return A new font with the same size and family as f, but with this
         *         enum constant's style.
         */
        public Font apply(Font f) {
            return f.deriveFont(styleCode);
        }

        @Override
        public String toString() {
            return humanName;
        }

        public static FontStyle forCode(int styleCode) {
            for (FontStyle sty : values()) {
                if (styleCode == sty.styleCode) {
                    return sty;
                }
            }
            throw new IllegalArgumentException("Unknown font style code: " + styleCode);
        }

        public int getStyleCode() {
            return styleCode;
        }
    }

    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

    /**
     * The font the user has chosen. This is updated whenever any of the
     * font choosing components fires a change event.
     */
    private Font selectedFont;

    /**
     * A list populated with all font names available on the local system.
     */
    private final JList fontNameList;

    /**
     * A list populated with a number of common font sizes the user might want
     * to pick. Choosing an item on this list fills in the value in the font
     * size text field.
     */
    private final JList fontSizeList;

    /**
     * The field that actually specifies the font size. Can be updated directly
     * to any positive integer value, or by clicking a preset value in the
     * {@link #fontSizeList}.
     */
    private final JSpinner fontSizeSpinner;

    /**
     * The font style chooser (bold, italic, bold italic, plain).
     */
    private final JList styleChoice;

    /** The list of font sizes */
    private static final Integer[] FONT_SIZES = { 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 30, 36, 40,
            48, 60, 72, 96, 144 };

    /**
     * The display area.
     */
    private final JTextArea previewArea = new JTextArea("The Quick Wabit Architected the Loaded Matchmaker");

    /**
     * The panel we return as this data entry panel's GUI.
     */
    private final JPanel panel;

    /**
     * The original font we started with. If this data entry panel is canceled,
     * the user's current choice will revert to this value.
     */
    private final Font originalFont;

    private final SPFontLoader fontLoader;

    private class SelectionHandler implements ChangeListener, ListSelectionListener {

        boolean updatingListSelection = false;

        public void stateChanged(ChangeEvent e) {
            if (updatingListSelection)
                return;
            try {
                updatingListSelection = true;
                Integer newSize = (Integer) fontSizeSpinner.getValue();
                int newSizeIndexInList = Arrays.binarySearch(FONT_SIZES, newSize);
                if (newSizeIndexInList >= 0) {
                    fontSizeList.setSelectedIndex(newSizeIndexInList);
                    fontSizeList.ensureIndexIsVisible(newSizeIndexInList);
                } else {
                    fontSizeList.clearSelection();
                }
            } finally {
                updatingListSelection = false;
            }
            previewFont();
        }

        public void valueChanged(ListSelectionEvent e) {
            previewFont();
        }

    }

    /**
     * This constructor will create the Font Selector given a parent frame and a
     * previous font.
     * 
     * @param font The font to start with in the preview dialog. If null, the system
     * default font will be used.
     */
    public FontSelector(Font font) {
        this(font == null ? Font.decode(null) : font,
                GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(), null);
    }

    public FontSelector(Font font, String[] fontList, SPFontLoader fontLoader) {

        this.fontLoader = fontLoader;
        if (font == null) {
            if (fontList == null || fontList.length == 0) {
                throw new IllegalArgumentException("The fontList parameter requires at least one valid font.");
            }
            font = Font.decode(fontList[0]);
            if (font == null) {
                throw new IllegalArgumentException("The fontList[0] element cannot be loaded.");
            }
        }

        logger.debug("Creating new font selector with given font: " + font);

        this.originalFont = font;

        SelectionHandler selectionHandler = new SelectionHandler();
        fontNameList = new JList(fontList);
        fontNameList.addListSelectionListener(selectionHandler);

        fontSizeSpinner = new JSpinner(new SpinnerNumberModel(font.getSize(), 1, 200, 1));
        fontSizeSpinner.addChangeListener(selectionHandler);

        fontSizeList = new JList(FONT_SIZES);
        fontSizeList.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (fontSizeList.getSelectedValue() != null) {
                    fontSizeSpinner.setValue((Integer) fontSizeList.getSelectedValue());
                }
            }
        });

        styleChoice = new JList(FontStyle.values());
        styleChoice.setSelectedValue(FontStyle.forCode(font.getStyle()), true);
        styleChoice.addListSelectionListener(selectionHandler);

        FormLayout layout = new FormLayout("pref:grow, 4dlu, pref, 4dlu, pref",
                "pref, 4dlu, pref, 4dlu, fill:pref:grow");
        layout.setHonorsVisibility(true);
        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        CellConstraints cc = new CellConstraints();

        builder.add(new JScrollPane(fontNameList), cc.xywh(1, 1, 1, 3));
        builder.add(fontSizeSpinner, cc.xywh(3, 1, 1, 1));
        builder.add(new JScrollPane(fontSizeList), cc.xywh(3, 3, 1, 1));
        builder.add(new JScrollPane(styleChoice), cc.xywh(5, 1, 1, 3));

        previewArea.setBackground(Color.WHITE);
        previewArea.setPreferredSize(new Dimension(300, 100));
        builder.add(previewArea, cc.xywh(1, 5, 5, 1));

        // Set defaults after creating layout so the "scroll to visible" works
        fontSizeList.setSelectedValue(Integer.valueOf(font.getSize()), true);
        fontNameList.setSelectedValue(font.getFamily(), true);
        logger.debug(
                "Set family list to \"" + font.getFamily() + "\" and size to " + Integer.valueOf(font.getSize()));

        panel = builder.getPanel();

        previewFont(); // ensure view is up to date!
    }

    /**
     * Updates the {@link #selectedFont} and the font in the preview area. This
     * method gets called from the change handlers installed on all the chooser
     * components.
     */
    private void previewFont() {
        String name = (String) fontNameList.getSelectedValue();
        FontStyle style = (FontStyle) styleChoice.getSelectedValue();
        int size = ((Integer) fontSizeSpinner.getValue()).intValue();

        if (fontLoader == null) {
            setSelectedFont(new Font(name, style.getStyleCode(), size));
        } else {
            Font font = fontLoader.loadFontFromName(name);
            font = font.deriveFont(style.getStyleCode());
            font = font.deriveFont(Float.valueOf(String.valueOf(size)));
            setSelectedFont(font);
        }
    }

    /**
     * Returns the font the user has constructed through the choices in this
     * font selector.
     */
    public Font getSelectedFont() {
        return selectedFont;
    }

    public void setSelectedFont(Font selectedFont) {
        Font oldFont = this.selectedFont;
        this.selectedFont = selectedFont;
        pcs.firePropertyChange("selectedFont", oldFont, selectedFont);
        previewArea.setFont(selectedFont);
    }

    public JPanel getPanel() {
        return panel;
    }

    public boolean applyChanges() {
        return true;
    }

    /**
     * Reverts to the original font.
     */
    public void discardChanges() {
        setSelectedFont(originalFont);
    }

    public boolean hasUnsavedChanges() {
        return false;
    }

    /**
     * Sets the text that will appear in the preview area. If you do not
     * call this method, the font selector will show its catchy default
     * phrase.
     */
    public void setPreviewText(String text) {
        previewArea.setText(text);
    }

    /**
     * Returns the current preview text. Keep in mind the text is in an
     * editable text area, so the user could have modified the text by the
     * time you call this method, and the entered text could be multi-line.
     */
    public String getPreviewText() {
        return previewArea.getText();
    }

    public void setShowingPreview(boolean show) {
        previewArea.setVisible(show);
    }

    public boolean isShowingPreview() {
        return previewArea.isVisible();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                final FontSelector fs = new FontSelector(Font.decode("Courier bold 20"));
                fs.addPropertyChangeListener(new PropertyChangeListener() {
                    public void propertyChange(PropertyChangeEvent evt) {
                        System.out.println("New font selection: " + fs.getSelectedFont());
                    }
                });
                JFrame dummyFrame = new JFrame();
                JDialog d = DataEntryPanelBuilder.createDataEntryPanelDialog(fs, dummyFrame, "Font Selector Demo!",
                        "Yeehaw");
                d.setModal(true);
                d.setVisible(true);
                System.out.println("Selected font: " + fs.getSelectedFont());
                d.dispose();
                dummyFrame.dispose();
            }
        });
    }

    /**
     * Adds a property change listener that will be notified every time
     * the selected font has changed. The property name is "selectedFont".
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public PropertyChangeListener[] getPropertyChangeListeners() {
        return pcs.getPropertyChangeListeners();
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

}