Swing Components Java

/*
 *  JSpinField.java  - A spin field using a JSpinner (JDK 1.4)
 *  Copyright (C) 2004 Kai Toedter
 *  kai@toedter.com
 *  www.toedter.com
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
//package com.toedter.components;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
 * JSpinField is a numeric field with 2 spin buttons to increase or decrease the
 * value. It has the same interface as the "old" JSpinField but uses a JSpinner
 * internally (since J2SE SDK 1.4) rather than a scrollbar for emulating the
 * spin buttons.
 * 
 * @author Kai Toedter
 * @version $LastChangedRevision: 85 $
 * @version $LastChangedDate: 2006-04-28 13:50:52 +0200 (Fr, 28 Apr 2006) $
 */
public class JSpinField extends JPanel implements ChangeListener, CaretListener, ActionListener,
    FocusListener {
  private static final long serialVersionUID = 1694904792717740650L;
  protected JSpinner spinner;
  /** the text (number) field */
  protected JTextField textField;
  protected int min;
  protected int max;
  protected int value;
  protected Color darkGreen;
  /**
   * Default JSpinField constructor. The valid value range is between
   * Integer.MIN_VALUE and Integer.MAX_VALUE. The initial value is 0.
   */
  public JSpinField() {
    this(Integer.MIN_VALUE, Integer.MAX_VALUE);
  }
  /**
   * JSpinField constructor with given minimum and maximum vaues and initial
   * value 0.
   */
  public JSpinField(int min, int max) {
    super();
    setName("JSpinField");
    this.min = min;
    if (max < min)
      max = min;
    this.max = max;
    value = 0;
    if (value < min)
      value = min;
    if (value > max)
      value = max;
    darkGreen = new Color(0, 150, 0);
    setLayout(new BorderLayout());
    textField = new JTextField();
    textField.addCaretListener(this);
    textField.addActionListener(this);
    textField.setHorizontalAlignment(SwingConstants.RIGHT);
    textField.setBorder(BorderFactory.createEmptyBorder());
    textField.setText(Integer.toString(value));
    textField.addFocusListener(this);
    spinner = new JSpinner() {
      private static final long serialVersionUID = -6287709243342021172L;
      private JTextField textField = new JTextField();
      public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        return new Dimension(size.width, textField.getPreferredSize().height);
      }
    };
    spinner.setEditor(textField);
    spinner.addChangeListener(this);
    // spinner.setSize(spinner.getWidth(), textField.getHeight());
    add(spinner, BorderLayout.CENTER);
  }
  public void adjustWidthToMaximumValue() {
    JTextField testTextField = new JTextField(Integer.toString(max));
    int width = testTextField.getPreferredSize().width;
    int height = testTextField.getPreferredSize().height;
    textField.setPreferredSize(new Dimension(width, height));
    textField.revalidate();
  }
  /**
   * Is invoked when the spinner model changes
   * 
   * @param e
   *            the ChangeEvent
   */
  public void stateChanged(ChangeEvent e) {
    SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel();
    int value = model.getNumber().intValue();
    setValue(value);
  }
  /**
   * Sets the value attribute of the JSpinField object.
   * 
   * @param newValue
   *            The new value
   * @param updateTextField
   *            true if text field should be updated
   */
  protected void setValue(int newValue, boolean updateTextField, boolean firePropertyChange) {
    int oldValue = value;
    if (newValue < min) {
      value = min;
    } else if (newValue > max) {
      value = max;
    } else {
      value = newValue;
    }
    if (updateTextField) {
      textField.setText(Integer.toString(value));
      textField.setForeground(Color.black);
    }
    if (firePropertyChange) {
      firePropertyChange("value", oldValue, value);
    }
  }
  /**
   * Sets the value. This is a bound property.
   * 
   * @param newValue
   *            the new value
   * 
   * @see #getValue
   */
  public void setValue(int newValue) {
    setValue(newValue, true, true);
    spinner.setValue(new Integer(value));
  }
  /**
   * Returns the value.
   * 
   * @return the value value
   */
  public int getValue() {
    return value;
  }
  /**
   * Sets the minimum value.
   * 
   * @param newMinimum
   *            the new minimum value
   * 
   * @see #getMinimum
   */
  public void setMinimum(int newMinimum) {
    min = newMinimum;
  }
  /**
   * Returns the minimum value.
   * 
   * @return the minimum value
   */
  public int getMinimum() {
    return min;
  }
  /**
   * Sets the maximum value and adjusts the preferred width.
   * 
   * @param newMaximum
   *            the new maximum value
   * 
   * @see #getMaximum
   */
  public void setMaximum(int newMaximum) {
    max = newMaximum;
  }
  /**
   * Sets the horizontal alignment of the displayed value.
   * 
   * @param alignment
   *            the horizontal alignment
   */
  public void setHorizontalAlignment(int alignment) {
    textField.setHorizontalAlignment(alignment);
  }
  /**
   * Returns the maximum value.
   * 
   * @return the maximum value
   */
  public int getMaximum() {
    return max;
  }
  /**
   * Sets the font property.
   * 
   * @param font
   *            the new font
   */
  public void setFont(Font font) {
    if (textField != null) {
      textField.setFont(font);
    }
  }
  /**
   * Sets the foreground
   * 
   * @param fg
   *            the foreground
   */
  public void setForeground(Color fg) {
    if (textField != null) {
      textField.setForeground(fg);
    }
  }
  /**
   * After any user input, the value of the textfield is proofed. Depending on
   * being an integer, the value is colored green or red.
   * 
   * @param e
   *            the caret event
   */
  public void caretUpdate(CaretEvent e) {
    try {
      int testValue = Integer.valueOf(textField.getText()).intValue();
      if ((testValue >= min) && (testValue <= max)) {
        textField.setForeground(darkGreen);
        setValue(testValue, false, true);
      } else {
        textField.setForeground(Color.red);
      }
    } catch (Exception ex) {
      if (ex instanceof NumberFormatException) {
        textField.setForeground(Color.red);
      }
      // Ignore all other exceptions, e.g. illegal state exception
    }
    textField.repaint();
  }
  /**
   * After any user input, the value of the textfield is proofed. Depending on
   * being an integer, the value is colored green or red. If the textfield is
   * green, the enter key is accepted and the new value is set.
   * 
   * @param e
   *            Description of the Parameter
   */
  public void actionPerformed(ActionEvent e) {
    if (textField.getForeground().equals(darkGreen)) {
      setValue(Integer.valueOf(textField.getText()).intValue());
    }
  }
  /**
   * Enable or disable the JSpinField.
   * 
   * @param enabled
   *            The new enabled value
   */
  public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    spinner.setEnabled(enabled);
    textField.setEnabled(enabled);
    /*
     * Fixes the background bug
     * 4991597 and sets the background explicitely to a
     * TextField.inactiveBackground.
     */
    if (!enabled) {
      textField.setBackground(UIManager.getColor("TextField.inactiveBackground"));
    }
  }
  /**
   * Returns the year chooser's spinner (which allow the focus to be set to
   * it).
   * 
   * @return Component the spinner or null, if the month chooser has no
   *         spinner
   */
  public Component getSpinner() {
    return spinner;
  }
  /**
   * Creates a JFrame with a JSpinField inside and can be used for testing.
   * 
   * @param s
   *            The command line arguments
   */
  public static void main(String[] s) {
    JFrame frame = new JFrame("JSpinField");
    frame.getContentPane().add(new JSpinField());
    frame.pack();
    frame.setVisible(true);
  }
  /*
   * (non-Javadoc)
   * 
   * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
   */
  public void focusGained(FocusEvent e) {
  }
  /**
   * The value of the text field is checked against a valid (green) value. If
   * valid, the value is set and a property change is fired.
   */
  public void focusLost(FocusEvent e) {
    actionPerformed(null);
  }
}