net.mariottini.swing.JFontChooser.java Source code

Java tutorial

Introduction

Here is the source code for net.mariottini.swing.JFontChooser.java

Source

package net.mariottini.swing;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.EventListener;
import java.util.Vector;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.apache.commons.lang.ArrayUtils;

/**
 * A dialog box that lets the user choose a font.
 * <p>
 * An average choose font dialog is like this:
 * </p>
 * <img src="doc-files/font_pre.png">
 * <p>
 * If you want you can hide the sample text area:
 * </p>
 * <img src="doc-files/font.png">
 * <p>
 * It can also show a custom accessory component, with wich you can control the functioning of the
 * dialog box:
 * </p>
 * <img src="doc-files/font_access.png">
 * <p>
 * In this case the accessory component is a list that changes the sample text.
 * </p>
 * <P>
 * <DL>
 * <DT><B>License:</B></DT>
 * <DD>
 * 
 * <pre>
 *  Copyright  2006, 2007 Roberto Mariottini. All rights reserved.
 * 
 *  Permission is granted to anyone to use this software in source and binary forms
 *  for any purpose, with or without modification, including commercial applications,
 *  and to alter it and redistribute it freely, provided that the following conditions
 *  are met:
 * 
 *  o  Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  o  The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  o  Altered source versions must be plainly marked as such, and must not
 *     be misrepresented as being the original software.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * <pre></DD>
 * </DL>
 * 
 * @version 1.0
 * @author Roberto Mariottini
 */
public final class JFontChooser extends JComponent {
    private static final long serialVersionUID = -3334992435394752913L;
    /**
     * <code>showDialog</code> returns this value if the user has approved (chosen) a font.
     * 
     * @see #showDialog
     */
    public static final int APPROVE_OPTION = 0;
    /**
     * <code>showDialog</code> returns this value if the user has not chosen a font.
     * 
     * @see #showDialog
     */
    public static final int CANCEL_OPTION = 1;

    /**
     * Action command used to notify that the user has approved (chosen) a font.
     * 
     * @see #addActionListener
     */
    public static final String APPROVE_SELECTION = "ApproveSelection";
    /**
     * Action command used to notify that the user has not chosen a font.
     * 
     * @see #addActionListener
     */
    public static final String CANCEL_SELECTION = "CancelSelection";

    /**
     * Name of the property used to notify that the user has changed the selected font name in the
     * name list. This property is of type <code>String</code>.
     * 
     * @see #addPropertyChangeListener
     */
    public static final String FONT_NAME_CHANGED_PROPERTY = "FontNameChangedProperty";
    /**
     * Name of the property used to notify that the user has changed the selected font style in the
     * style list. This property is of type <code>Integer</code>, and can assume the following values:
     * <code>Font.PLAIN</code>, <code>Font.BOLD</code>, <code>Font.ITALIC</code>,
     * <code>Font.BOLD | Font.ITALIC</code>.
     * 
     * @see #addPropertyChangeListener
     */
    public static final String FONT_STYLE_CHANGED_PROPERTY = "FontStyleChangedProperty";
    /**
     * Name of the property used to notify that the user has changed the selected font size in the
     * size list or in the size text field. This property is of type <code>Integer</code>.
     * 
     * @see #addPropertyChangeListener
     */
    public static final String FONT_SIZE_CHANGED_PROPERTY = "FontSizeChangedProperty";

    /**
     * Construct a font chooser with the specified fonts, optionally showing a sample to the user. The
     * sample text is a default text, you can change it by calling <code>setSampleText</code>.
     * 
     * @param fontNames
     *          the font family names to show to the user.
     * @param showSample
     *          true to show a sample of the selected font to the user.
     * @see #setSampleText
     */
    public JFontChooser(String[] fontNames, boolean showSample) {
        setLayout(new BorderLayout());

        JPanel centerPanel = new JPanel(new BorderLayout());

        // Uncomment one of the two lines below to use a standard layout manager
        // instead of my MeshLayout manager. The result is ugly.
        // JPanel listsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));/*
        // JPanel listsPanel = new JPanel(new GridLayout(0, 3));/*
        net.mariottini.layout.MeshLayout mesh = new net.mariottini.layout.MeshLayout(0, 3, 0);
        mesh.setExpandColumn(0);
        JPanel listsPanel = new JPanel(mesh);// */

        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
        panel.add(new JLabel("Family name:"), BorderLayout.NORTH);
        fontList = new JList<>(fontNames);
        fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        fontList.setVisibleRowCount(7);
        fontList.setSelectedIndex(0);
        panel.add(new JScrollPane(fontList), BorderLayout.CENTER);
        listsPanel.add(panel);

        panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 8));
        panel.add(new JLabel("Style:"), BorderLayout.NORTH);
        styleList = new JList<>(STYLE_NAMES);
        styleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        styleList.setVisibleRowCount(7);
        styleList.setSelectedIndex(0);
        panel.add(new JScrollPane(styleList), BorderLayout.CENTER);
        listsPanel.add(panel);

        panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 8));
        panel.add(new JLabel("Size:"), BorderLayout.NORTH);
        JPanel sizePanel = new JPanel(new BorderLayout());
        sizeText = new JTextField(String.valueOf(SIZES[0]), 4);
        sizePanel.add(sizeText, BorderLayout.NORTH);
        sizeList = new JList<>(ArrayUtils.toObject(SIZES));
        sizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        sizeList.setVisibleRowCount(6);
        sizePanel.add(new JScrollPane(sizeList), BorderLayout.CENTER);
        panel.add(sizePanel, BorderLayout.CENTER);
        listsPanel.add(panel);

        centerPanel.add(listsPanel, BorderLayout.NORTH);

        samplePanel = new JPanel(new BorderLayout());
        samplePanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 4, 8));
        samplePanel.add(new JLabel("Sample:"), BorderLayout.NORTH);
        sampleLabel = new JLabel(DEFAULT_SAMPLE_TEXT, JLabel.CENTER);
        sampleLabel.setMinimumSize(new Dimension(64, 48));
        sampleLabel.setOpaque(true);
        sampleLabel.setBackground(sizeList.getBackground());
        sampleLabel.setBorder(sizeText.getBorder());
        samplePanel.add(sampleLabel, BorderLayout.CENTER);
        samplePanel.setVisible(showSample);
        centerPanel.add(samplePanel, BorderLayout.CENTER);

        add(centerPanel, BorderLayout.CENTER);

        accessoryPanel = new JPanel(new BorderLayout());
        accessoryPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 4, 8));
        accessoryComponent = new JLabel("Accessory");
        accessoryComponent.setOpaque(true);
        accessoryComponent.setBackground(sizeList.getBackground());
        accessoryPanel.add(accessoryComponent, BorderLayout.CENTER);
        accessoryPanel.setVisible(false);
        add(accessoryPanel, BorderLayout.EAST);

        JPanel southPanel = new JPanel(new BorderLayout());
        southPanel.add(new JLabel(), BorderLayout.CENTER);
        JPanel buttonsPanel = new JPanel();
        ActionListener actionListener = new ButtonActionListener();
        JButton button = new JButton("OK");
        button.addActionListener(actionListener);
        button.setActionCommand(APPROVE_SELECTION);
        buttonsPanel.add(button);
        button = new JButton("Cancel");
        button.addActionListener(actionListener);
        button.setActionCommand(CANCEL_SELECTION);
        buttonsPanel.add(button);
        southPanel.add(buttonsPanel, BorderLayout.EAST);
        add(southPanel, BorderLayout.SOUTH);

        // * Fix list size (optional)
        Dimension d = fontList.getPreferredSize();
        d.width += 6;
        fontList.setPreferredSize(d);
        d = styleList.getPreferredSize();
        d.width += 6;
        styleList.setPreferredSize(d);
        d = sizeList.getPreferredSize();
        d.width += 6;
        sizeList.setPreferredSize(d);
        // */

        // Fix sample size
        Dimension pref = sampleLabel.getPreferredSize();
        Dimension min = sampleLabel.getMinimumSize();
        pref.width += 16;
        pref.height += 12;
        if (pref.width < min.width) {
            pref.width = min.width;
        }
        if (pref.height < min.height) {
            pref.height = min.height;
        }
        sampleLabel.setPreferredSize(pref);

        // set listener
        SampleListener pl = new SampleListener();
        fontList.addListSelectionListener(pl);
        styleList.addListSelectionListener(pl);
        sizeList.addListSelectionListener(pl);
        sizeList.addListSelectionListener(new SizeListListener());
        sizeText.getDocument().addDocumentListener(new SizeTextListener());
        sizeText.addFocusListener(new SizeTextFocusListener());
        sizeList.setSelectedIndex(5);
    }

    /**
     * Construct a font chooser with the specified fonts, showing a sample to the user. The sample
     * text is a default text, you can change it by calling <code>setSampleText</code>.
     * 
     * @param fontNames
     *          the font family names to show to the user.
     * @see #setSampleText
     */
    public JFontChooser(String[] fontNames) {
        this(fontNames, true);
    }

    /**
     * Construct a font chooser with all the available fonts, optionally showing a sample to the user.
     * The sample text is a default text, you can change it by calling <code>setSampleText</code>. The
     * font list is acquired by calling GraphicsEnvironment.getAvailableFontFamilyNames().
     * 
     * @param showSample
     *          true to show a sample of the selected font to the user.
     * @see #setSampleText
     */
    public JFontChooser(boolean showSample) {
        this(genv.getAvailableFontFamilyNames(), showSample);
    }

    /**
     * Construct a font chooser with all the available fonts, showing a sample to the user. The sample
     * text is a default text, you can change it by calling <code>setSampleText</code>. The font list
     * is acquired by calling GraphicsEnvironment.getAvailableFontFamilyNames().
     * 
     * @see #setSampleText
     */
    public JFontChooser() {
        this(genv.getAvailableFontFamilyNames(), true);
    }

    /**
     * change the font names to show in the font list.
     * 
     * @param fontNames
     *          the font family names to show to the user.
     */
    public void setFontNames(String[] fontNames) {
        fontList.setListData(fontNames);
        fontList.setSelectedIndex(0);
        fontList.ensureIndexIsVisible(0);
    }

    /**
     * Set the string to use as a sample for the fonts. Pass <code>null</code> to use the currently
     * selected font family name as sample.
     * 
     * @param text
     *          the sample string, or <code>null</code> to use the font family name as sample.
     */
    public void setSampleText(String text) {
        sampleText = text;
        if (sampleText == null) {
            sampleLabel.setText(fontList.getSelectedValue().toString());
            return;
        }
        sampleLabel.setText(sampleText);

        // Fix sample size
        sampleLabel.setPreferredSize(null);
        Dimension pref = sampleLabel.getPreferredSize();
        pref.width += 16;
        pref.height += 12;
        Dimension min = sampleLabel.getMinimumSize();
        if (pref.width < min.width) {
            pref.width = min.width;
        }
        if (pref.height < min.height) {
            pref.height = min.height;
        }
        sampleLabel.setPreferredSize(pref);
    }

    /**
     * Shows/hides the sample panel.
     * 
     * @param visible
     *          true to show it, false to hide it.
     */
    public void setSampleVisible(boolean visible) {
        samplePanel.setVisible(visible);
    }

    /**
     * Returns the sample panel visibility status.
     * 
     * @return true if it's visible, false if it's hidden.
     */
    public boolean isSampleVisible() {
        return samplePanel.isVisible();
    }

    /**
     * Returns the component used to show the font sample. This function is provided to customize the
     * appearance of the component, in particular foreground and background colors. There's no way to
     * change this component, to customize deeper the sample panel hide it and use the accessory
     * component instead.
     * 
     * @return the sample component.
     * @see #setAccessory
     */
    public JComponent getSampleComponent() {
        return sampleLabel;
    }

    /**
     * Returns the accessory component.
     * 
     * @return the accessory component.
     * @see #setAccessory
     */
    public JComponent getAccessory() {
        return accessoryComponent;
    }

    /**
     * Sets the accessory component. The accessory component is used to customize the dialog. You can
     * use a single component or a container with many components of your choice to extend the
     * functionality of the dialog. Register the appropriate listeners to react to user input. The
     * accessory component will become part of the dialog, and will be disposed along with it: remove
     * it before disposal if you want to preserve it.
     * 
     * @param newAccessory
     *          the accessory component to use, or <code>null</code> to hide and remove any accessory
     *          component previously set.
     * @see #addActionListener
     * @see #addPropertyChangeListener
     * @see #FONT_NAME_CHANGED_PROPERTY
     * @see #FONT_STYLE_CHANGED_PROPERTY
     * @see #FONT_SIZE_CHANGED_PROPERTY
     */
    public void setAccessory(JComponent newAccessory) {
        accessoryPanel.removeAll();
        accessoryComponent = newAccessory;
        if (accessoryComponent == null) {
            accessoryPanel.setVisible(false);
            return;
        }
        accessoryPanel.add(accessoryComponent, BorderLayout.CENTER);
        accessoryPanel.setVisible(true);
    }

    /**
     * Adds an action listener to this component. The <code>actionPerformed</code> method will be
     * called when the user press one of the two buttons "OK" and "Cancel". The two different action
     * command used are <code>APPROVE_SELECTION</code> and <code>CANCEL_SELECTION</code>.
     * 
     * @param listener
     *          the listener to add to the listeners list.
     * @see #APPROVE_SELECTION
     * @see #CANCEL_SELECTION
     */
    public void addActionListener(ActionListener listener) {
        actionListeners.add(listener);
    }

    /**
     * Remove an action listener from this component.
     * 
     * @param listener
     *          the listener to remove from the list.
     * @see #addActionListener
     */
    public void removeActionListener(ActionListener listener) {
        actionListeners.remove(listener);
    }

    /**
     * Called when the user approves a font. Call this method to close the dialog and approve the
     * currently selected font.
     */
    public void approveSelection() {
        ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, APPROVE_SELECTION);
        dispatchActionEvent(event);
    }

    /**
     * Called when the user cancels the operation. Call this method to close the dialog without
     * selecting a font.
     */
    public void cancelSelection() {
        ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, CANCEL_SELECTION);
        dispatchActionEvent(event);
    }

    /**
     * Returns the currently selected font. The Font object returned comprehends the style and size
     * chosen by the user.
     * 
     * @return the currently selected font.
     */
    public Font getSelectedFont() {
        return sampleLabel.getFont();
    }

    /**
     * Sets the currently selected font. The dialog will try to change the listboxes selections
     * according to the font set.
     * 
     * @param font
     *          the font to select.
     */
    public void setSelectedFont(Font font) {
        fontList.setSelectedValue(font.getFamily(), true);
        styleList.setSelectedIndex(font.getStyle());
        int size = font.getSize();
        int index = Arrays.binarySearch(SIZES, new Integer(size));
        if (index >= 0) {
            sizeList.setSelectedIndex(index);
            sizeList.ensureIndexIsVisible(index);
        } else {
            sizeText.setText(String.valueOf(size));
        }
    }

    /**
     * Show a modal "Choose Font" dialog.
     * 
     * @param parent
     *          the parent component, or null to use a default root frame as parent.
     * @return <code>APPROVE_OPTION</code> if the user chose a font, <code>CANCEL_OPTION</code> if the
     *         user canceled the operation.
     * @see #APPROVE_OPTION
     * @see #CANCEL_OPTION
     */
    public int showDialog(Component parent) {
        return showDialog(parent, "Choose Font");
    }

    /**
     * Show a modal "Choose Font" dialog with the specified title.
     * 
     * @param parent
     *          the parent component, or null to use a default root frame as parent.
     * @param title
     *          the title for the dialog.
     * @return <code>APPROVE_OPTION</code> if the user chose a font, <code>CANCEL_OPTION</code> if the
     *         user canceled the operation.
     * @see #APPROVE_OPTION
     * @see #CANCEL_OPTION
     */
    public int showDialog(Component parent, String title) {
        return showDialog(parent, title, true);
    }

    /**
     * Show a "Choose Font" dialog with the specified title and modality.
     * 
     * @param parent
     *          the parent component, or null to use a default root frame as parent.
     * @param title
     *          the title for the dialog.
     * @param modal
     *          true to show a modal dialog, false to show a non-modal dialog (in this case the
     *          function will return immediately after making visible the dialog).
     * @return <code>APPROVE_OPTION</code> if the user chose a font, <code>CANCEL_OPTION</code> if the
     *         user canceled the operation. <code>CANCEL_OPTION</code> is always returned for a
     *         non-modal dialog, use an ActionListener to be notified when the user approves/cancels
     *         the dialog.
     * @see #APPROVE_OPTION
     * @see #CANCEL_OPTION
     * @see #addActionListener
     */
    public int showDialog(Component parent, String title, boolean modal) {
        final int[] result = new int[] { CANCEL_OPTION };
        while (parent != null && !(parent instanceof Window)) {
            parent = parent.getParent();
        }
        final JDialog d;
        if (parent instanceof Frame) {
            d = new JDialog((Frame) parent, title, modal);
        } else if (parent instanceof Dialog) {
            d = new JDialog((Dialog) parent, title, modal);
        } else {
            d = new JDialog();
            d.setTitle(title);
            d.setModal(modal);
        }
        final ActionListener[] listener = new ActionListener[1];
        listener[0] = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals(APPROVE_SELECTION)) {
                    result[0] = APPROVE_OPTION;
                }
                removeActionListener(listener[0]);
                d.setContentPane(new JPanel());
                d.setVisible(false);
                d.dispose();
            }
        };
        addActionListener(listener[0]);
        d.setComponentOrientation(getComponentOrientation());
        d.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
        d.getContentPane().add(this, BorderLayout.CENTER);
        d.pack();
        d.setLocationRelativeTo(parent);
        d.setVisible(true);
        return result[0];
    }

    private static final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
    private static final String DEFAULT_SAMPLE_TEXT = "AaBbCcDdEe123456";
    private static final String[] STYLE_NAMES = { "Plain", "Bold", "Italic", "Bold italic" };
    private static final int[] STYLE_VALUES = { Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC };
    private static final int[] SIZES = { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 };

    private Vector<EventListener> actionListeners = new Vector<>();

    private JList<String> fontList;
    private JList<String> styleList;
    private JList<Integer> sizeList;
    private JTextField sizeText;
    private JPanel samplePanel;
    private JLabel sampleLabel;
    private String sampleText = DEFAULT_SAMPLE_TEXT;
    private JPanel accessoryPanel;
    private JComponent accessoryComponent;

    private void dispatchActionEvent(ActionEvent event) {
        for (int i = 0; i < actionListeners.size(); ++i) {
            ((ActionListener) actionListeners.get(i)).actionPerformed(event);
        }
    }

    private final class ButtonActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (e.getActionCommand().equals(APPROVE_SELECTION)) {
                approveSelection();
            } else {
                cancelSelection();
            }
        }
    }

    private final class SizeListListener implements ListSelectionListener {
        public void valueChanged(ListSelectionEvent e) {
            if (!e.getValueIsAdjusting()) {
                String val = sizeList.getSelectedValue().toString();
                if (!sizeText.getText().equals(val)) {
                    sizeText.setText(val);
                }
            }
        }
    }

    private final class SizeTextFocusListener extends FocusAdapter {
        public void focusLost(FocusEvent e) {
            try {
                Integer.valueOf(sizeText.getText());
            } catch (NumberFormatException nfe) {
                sizeText.setText(sizeList.getSelectedValue().toString());
            }
        }
    }

    private final class SizeTextListener implements DocumentListener {
        public void changedUpdate(DocumentEvent e) {
            updateList();
        }

        public void insertUpdate(DocumentEvent e) {
            updateList();
        }

        public void removeUpdate(DocumentEvent e) {
            updateList();
        }

        private void updateList() {
            try {
                Integer val = new Integer(sizeText.getText());
                sizeList.setSelectedValue(val, true);
                updateSample();
            } catch (NumberFormatException nfe) {
            }
        }
    }

    private final class SampleListener implements ListSelectionListener {
        public void valueChanged(ListSelectionEvent e) {
            if (!e.getValueIsAdjusting()) {
                updateSample();
            }
        }
    }

    private String oldFamily = null;
    private Integer oldSize = null;
    private int oldStyle = -1;

    private void updateSample() {
        String familyName = (String) fontList.getSelectedValue();
        if (familyName == null) {
            return;
        }

        Integer size = null;
        try {
            size = new Integer(sizeText.getText());
        } catch (Exception nfe) {
            size = (Integer) sizeList.getSelectedValue();
        }

        int style = STYLE_VALUES[styleList.getSelectedIndex()];

        if (!familyName.equals(oldFamily) || !size.equals(oldSize) || style != oldStyle) {
            if (sampleText == null && !familyName.equals(oldFamily)) {
                sampleLabel.setText(familyName);
            }

            if (!familyName.equals(oldFamily)) {
                notifyPropertyChange(FONT_NAME_CHANGED_PROPERTY, oldFamily, familyName);
            }
            if (!size.equals(oldSize)) {
                notifyPropertyChange(FONT_SIZE_CHANGED_PROPERTY, oldSize, size);
            }
            if (style != oldStyle) {
                notifyPropertyChange(FONT_STYLE_CHANGED_PROPERTY, new Integer(oldStyle), new Integer(style));
            }

            sampleLabel.setFont(new Font(familyName, style, size.intValue()));

            oldFamily = familyName;
            oldSize = size;
            oldStyle = style;
        }
    }

    private void notifyPropertyChange(String property, Object oldValue, Object newValue) {
        PropertyChangeEvent evt = new PropertyChangeEvent(this, property, oldValue, newValue);
        PropertyChangeListener[] listeners = getPropertyChangeListeners();
        for (int i = 0; i < listeners.length; ++i) {
            listeners[i].propertyChange(evt);
        }
    }
}