/*
 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing;

import java.awt.*;
import java.awt.event.*;

import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.plaf.SpinnerUI;

import java.util.*;
import java.beans.*;
import java.text.*;
import java.io.*;
import java.text.spi.DateFormatProvider;
import java.text.spi.NumberFormatProvider;

import javax.accessibility.*;
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.locale.provider.LocaleResources;

A single line input field that lets the user select a number or an object value from an ordered sequence. Spinners typically provide a pair of tiny arrow buttons for stepping through the elements of the sequence. The keyboard up/down arrow keys also cycle through the elements. The user may also be allowed to type a (legal) value directly into the spinner. Although combo boxes provide similar functionality, spinners are sometimes preferred because they don't require a drop down list that can obscure important data.

A JSpinner's sequence value is defined by its SpinnerModel. The model can be specified as a constructor argument and changed with the model property. SpinnerModel classes for some common types are provided: SpinnerListModel, SpinnerNumberModel, and SpinnerDateModel.

A JSpinner has a single child component that's responsible for displaying and potentially changing the current element or value of the model, which is called the editor. The editor is created by the JSpinner's constructor and can be changed with the editor property. The JSpinner's editor stays in sync with the model by listening for ChangeEvents. If the user has changed the value displayed by the editor it is possible for the model's value to differ from that of the editor. To make sure the model has the same value as the editor use the commitEdit method, eg:

  try {
      spinner.commitEdit();
  }
  catch (ParseException pe) {
      // Edited value is invalid, spinner.getValue() will return
      // the last valid value, you could revert the spinner to show that:
      JComponent editor = spinner.getEditor();
      if (editor instanceof DefaultEditor) {
          ((DefaultEditor)editor).getTextField().setValue(spinner.getValue());
      }
      // reset the value to some known value:
      spinner.setValue(fallbackValue);
      // or treat the last valid value as the current, in which
      // case you don't need to do anything.
  }
  return spinner.getValue();

For information and examples of using spinner see How to Use Spinners, a section in The Java Tutorial.

Warning: Swing is not thread safe. For more information see Swing's Threading Policy.

Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. As of 1.4, support for long term storage of all JavaBeans™ has been added to the java.beans package. Please see XMLEncoder.

Author:Hans Muller, Lynn Monsanto (accessibility)
See Also:
@beaninfo attribute: isContainer false description: A single line input field that lets the user select a number or an object value from an ordered set.
Since:1.4
/** * A single line input field that lets the user select a * number or an object value from an ordered sequence. Spinners typically * provide a pair of tiny arrow buttons for stepping through the elements * of the sequence. The keyboard up/down arrow keys also cycle through the * elements. The user may also be allowed to type a (legal) value directly * into the spinner. Although combo boxes provide similar functionality, * spinners are sometimes preferred because they don't require a drop down list * that can obscure important data. * <p> * A <code>JSpinner</code>'s sequence value is defined by its * <code>SpinnerModel</code>. * The <code>model</code> can be specified as a constructor argument and * changed with the <code>model</code> property. <code>SpinnerModel</code> * classes for some common types are provided: <code>SpinnerListModel</code>, * <code>SpinnerNumberModel</code>, and <code>SpinnerDateModel</code>. * <p> * A <code>JSpinner</code> has a single child component that's * responsible for displaying * and potentially changing the current element or <i>value</i> of * the model, which is called the <code>editor</code>. The editor is created * by the <code>JSpinner</code>'s constructor and can be changed with the * <code>editor</code> property. The <code>JSpinner</code>'s editor stays * in sync with the model by listening for <code>ChangeEvent</code>s. If the * user has changed the value displayed by the <code>editor</code> it is * possible for the <code>model</code>'s value to differ from that of * the <code>editor</code>. To make sure the <code>model</code> has the same * value as the editor use the <code>commitEdit</code> method, eg: * <pre> * try { * spinner.commitEdit(); * } * catch (ParseException pe) { * // Edited value is invalid, spinner.getValue() will return * // the last valid value, you could revert the spinner to show that: * JComponent editor = spinner.getEditor(); * if (editor instanceof DefaultEditor) { * ((DefaultEditor)editor).getTextField().setValue(spinner.getValue()); * } * // reset the value to some known value: * spinner.setValue(fallbackValue); * // or treat the last valid value as the current, in which * // case you don't need to do anything. * } * return spinner.getValue(); * </pre> * <p> * For information and examples of using spinner see * <a href="https://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html">How to Use Spinners</a>, * a section in <em>The Java Tutorial.</em> * <p> * <strong>Warning:</strong> Swing is not thread safe. For more * information see <a * href="package-summary.html#threading">Swing's Threading * Policy</a>. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans&trade; * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @beaninfo * attribute: isContainer false * description: A single line input field that lets the user select a * number or an object value from an ordered set. * * @see SpinnerModel * @see AbstractSpinnerModel * @see SpinnerListModel * @see SpinnerNumberModel * @see SpinnerDateModel * @see JFormattedTextField * * @author Hans Muller * @author Lynn Monsanto (accessibility) * @since 1.4 */
public class JSpinner extends JComponent implements Accessible {
See Also:
/** * @see #getUIClassID * @see #readObject */
private static final String uiClassID = "SpinnerUI"; private static final Action DISABLED_ACTION = new DisabledAction(); private SpinnerModel model; private JComponent editor; private ChangeListener modelListener; private transient ChangeEvent changeEvent; private boolean editorExplicitlySet = false;
Constructs a spinner for the given model. The spinner has a set of previous/next buttons, and an editor appropriate for the model.
Throws:
  • NullPointerException – if the model is null
/** * Constructs a spinner for the given model. The spinner has * a set of previous/next buttons, and an editor appropriate * for the model. * * @throws NullPointerException if the model is {@code null} */
public JSpinner(SpinnerModel model) { if (model == null) { throw new NullPointerException("model cannot be null"); } this.model = model; this.editor = createEditor(model); setUIProperty("opaque",true); updateUI(); }
Constructs a spinner with an Integer SpinnerNumberModel with initial value 0 and no minimum or maximum limits.
/** * Constructs a spinner with an <code>Integer SpinnerNumberModel</code> * with initial value 0 and no minimum or maximum limits. */
public JSpinner() { this(new SpinnerNumberModel()); }
Returns the look and feel (L&F) object that renders this component.
Returns:the SpinnerUI object that renders this component
/** * Returns the look and feel (L&amp;F) object that renders this component. * * @return the <code>SpinnerUI</code> object that renders this component */
public SpinnerUI getUI() { return (SpinnerUI)ui; }
Sets the look and feel (L&F) object that renders this component.
Params:
  • ui – the SpinnerUI L&F object
See Also:
/** * Sets the look and feel (L&amp;F) object that renders this component. * * @param ui the <code>SpinnerUI</code> L&amp;F object * @see UIDefaults#getUI */
public void setUI(SpinnerUI ui) { super.setUI(ui); }
Returns the suffix used to construct the name of the look and feel (L&F) class used to render this component.
See Also:
Returns:the string "SpinnerUI"
/** * Returns the suffix used to construct the name of the look and feel * (L&amp;F) class used to render this component. * * @return the string "SpinnerUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */
public String getUIClassID() { return uiClassID; }
Resets the UI property with the value from the current look and feel.
See Also:
  • getUI.getUI
/** * Resets the UI property with the value from the current look and feel. * * @see UIManager#getUI */
public void updateUI() { setUI((SpinnerUI)UIManager.getUI(this)); invalidate(); }
This method is called by the constructors to create the JComponent that displays the current value of the sequence. The editor may also allow the user to enter an element of the sequence directly. An editor must listen for ChangeEvents on the model and keep the value it displays in sync with the value of the model.

Subclasses may override this method to add support for new SpinnerModel classes. Alternatively one can just replace the editor created here with the setEditor method. The default mapping from model type to editor is:

  • SpinnerNumberModel => JSpinner.NumberEditor
  • SpinnerDateModel => JSpinner.DateEditor
  • SpinnerListModel => JSpinner.ListEditor
  • all others => JSpinner.DefaultEditor
Params:
  • model – the value of getModel
See Also:
Returns:a component that displays the current value of the sequence
/** * This method is called by the constructors to create the * <code>JComponent</code> * that displays the current value of the sequence. The editor may * also allow the user to enter an element of the sequence directly. * An editor must listen for <code>ChangeEvents</code> on the * <code>model</code> and keep the value it displays * in sync with the value of the model. * <p> * Subclasses may override this method to add support for new * <code>SpinnerModel</code> classes. Alternatively one can just * replace the editor created here with the <code>setEditor</code> * method. The default mapping from model type to editor is: * <ul> * <li> <code>SpinnerNumberModel =&gt; JSpinner.NumberEditor</code> * <li> <code>SpinnerDateModel =&gt; JSpinner.DateEditor</code> * <li> <code>SpinnerListModel =&gt; JSpinner.ListEditor</code> * <li> <i>all others</i> =&gt; <code>JSpinner.DefaultEditor</code> * </ul> * * @return a component that displays the current value of the sequence * @param model the value of getModel * @see #getModel * @see #setEditor */
protected JComponent createEditor(SpinnerModel model) { if (model instanceof SpinnerDateModel) { return new DateEditor(this); } else if (model instanceof SpinnerListModel) { return new ListEditor(this); } else if (model instanceof SpinnerNumberModel) { return new NumberEditor(this); } else { return new DefaultEditor(this); } }
Changes the model that represents the value of this spinner. If the editor property has not been explicitly set, the editor property is (implicitly) set after the "model" PropertyChangeEvent has been fired. The editor property is set to the value returned by createEditor, as in:
setEditor(createEditor(model));
Params:
  • model – the new SpinnerModel
Throws:
See Also:
@beaninfo bound: true attribute: visualUpdate true description: Model that represents the value of this spinner.
/** * Changes the model that represents the value of this spinner. * If the editor property has not been explicitly set, * the editor property is (implicitly) set after the <code>"model"</code> * <code>PropertyChangeEvent</code> has been fired. The editor * property is set to the value returned by <code>createEditor</code>, * as in: * <pre> * setEditor(createEditor(model)); * </pre> * * @param model the new <code>SpinnerModel</code> * @see #getModel * @see #getEditor * @see #setEditor * @throws IllegalArgumentException if model is <code>null</code> * * @beaninfo * bound: true * attribute: visualUpdate true * description: Model that represents the value of this spinner. */
public void setModel(SpinnerModel model) { if (model == null) { throw new IllegalArgumentException("null model"); } if (!model.equals(this.model)) { SpinnerModel oldModel = this.model; this.model = model; if (modelListener != null) { oldModel.removeChangeListener(modelListener); this.model.addChangeListener(modelListener); } firePropertyChange("model", oldModel, model); if (!editorExplicitlySet) { setEditor(createEditor(model)); // sets editorExplicitlySet true editorExplicitlySet = false; } repaint(); revalidate(); } }
Returns the SpinnerModel that defines this spinners sequence of values.
See Also:
Returns:the value of the model property
/** * Returns the <code>SpinnerModel</code> that defines * this spinners sequence of values. * * @return the value of the model property * @see #setModel */
public SpinnerModel getModel() { return model; }
Returns the current value of the model, typically this value is displayed by the editor. If the user has changed the value displayed by the editor it is possible for the model's value to differ from that of the editor, refer to the class level javadoc for examples of how to deal with this.

This method simply delegates to the model. It is equivalent to:

getModel().getValue()
See Also:
/** * Returns the current value of the model, typically * this value is displayed by the <code>editor</code>. If the * user has changed the value displayed by the <code>editor</code> it is * possible for the <code>model</code>'s value to differ from that of * the <code>editor</code>, refer to the class level javadoc for examples * of how to deal with this. * <p> * This method simply delegates to the <code>model</code>. * It is equivalent to: * <pre> * getModel().getValue() * </pre> * * @see #setValue * @see SpinnerModel#getValue */
public Object getValue() { return getModel().getValue(); }
Changes current value of the model, typically this value is displayed by the editor. If the SpinnerModel implementation doesn't support the specified value then an IllegalArgumentException is thrown.

This method simply delegates to the model. It is equivalent to:

getModel().setValue(value)
Throws:
  • IllegalArgumentException – if value isn't allowed
See Also:
/** * Changes current value of the model, typically * this value is displayed by the <code>editor</code>. * If the <code>SpinnerModel</code> implementation * doesn't support the specified value then an * <code>IllegalArgumentException</code> is thrown. * <p> * This method simply delegates to the <code>model</code>. * It is equivalent to: * <pre> * getModel().setValue(value) * </pre> * * @throws IllegalArgumentException if <code>value</code> isn't allowed * @see #getValue * @see SpinnerModel#setValue */
public void setValue(Object value) { getModel().setValue(value); }
Returns the object in the sequence that comes after the object returned by getValue(). If the end of the sequence has been reached then return null. Calling this method does not effect value.

This method simply delegates to the model. It is equivalent to:

getModel().getNextValue()
See Also:
Returns:the next legal value or null if one doesn't exist
/** * Returns the object in the sequence that comes after the object returned * by <code>getValue()</code>. If the end of the sequence has been reached * then return <code>null</code>. * Calling this method does not effect <code>value</code>. * <p> * This method simply delegates to the <code>model</code>. * It is equivalent to: * <pre> * getModel().getNextValue() * </pre> * * @return the next legal value or <code>null</code> if one doesn't exist * @see #getValue * @see #getPreviousValue * @see SpinnerModel#getNextValue */
public Object getNextValue() { return getModel().getNextValue(); }
We pass Change events along to the listeners with the the slider (instead of the model itself) as the event source.
/** * We pass <code>Change</code> events along to the listeners with the * the slider (instead of the model itself) as the event source. */
private class ModelListener implements ChangeListener, Serializable { public void stateChanged(ChangeEvent e) { fireStateChanged(); } }
Adds a listener to the list that is notified each time a change to the model occurs. The source of ChangeEvents delivered to ChangeListeners will be this JSpinner. Note also that replacing the model will not affect listeners added directly to JSpinner. Applications can add listeners to the model directly. In that case is that the source of the event would be the SpinnerModel.
Params:
  • listener – the ChangeListener to add
See Also:
/** * Adds a listener to the list that is notified each time a change * to the model occurs. The source of <code>ChangeEvents</code> * delivered to <code>ChangeListeners</code> will be this * <code>JSpinner</code>. Note also that replacing the model * will not affect listeners added directly to JSpinner. * Applications can add listeners to the model directly. In that * case is that the source of the event would be the * <code>SpinnerModel</code>. * * @param listener the <code>ChangeListener</code> to add * @see #removeChangeListener * @see #getModel */
public void addChangeListener(ChangeListener listener) { if (modelListener == null) { modelListener = new ModelListener(); getModel().addChangeListener(modelListener); } listenerList.add(ChangeListener.class, listener); }
Removes a ChangeListener from this spinner.
Params:
  • listener – the ChangeListener to remove
See Also:
/** * Removes a <code>ChangeListener</code> from this spinner. * * @param listener the <code>ChangeListener</code> to remove * @see #fireStateChanged * @see #addChangeListener */
public void removeChangeListener(ChangeListener listener) { listenerList.remove(ChangeListener.class, listener); }
Returns an array of all the ChangeListeners added to this JSpinner with addChangeListener().
Returns:all of the ChangeListeners added or an empty array if no listeners have been added
Since:1.4
/** * Returns an array of all the <code>ChangeListener</code>s added * to this JSpinner with addChangeListener(). * * @return all of the <code>ChangeListener</code>s added or an empty * array if no listeners have been added * @since 1.4 */
public ChangeListener[] getChangeListeners() { return listenerList.getListeners(ChangeListener.class); }
Sends a ChangeEvent, whose source is this JSpinner, to each ChangeListener. When a ChangeListener has been added to the spinner, this method method is called each time a ChangeEvent is received from the model.
See Also:
/** * Sends a <code>ChangeEvent</code>, whose source is this * <code>JSpinner</code>, to each <code>ChangeListener</code>. * When a <code>ChangeListener</code> has been added * to the spinner, this method method is called each time * a <code>ChangeEvent</code> is received from the model. * * @see #addChangeListener * @see #removeChangeListener * @see EventListenerList */
protected void fireStateChanged() { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { if (changeEvent == null) { changeEvent = new ChangeEvent(this); } ((ChangeListener)listeners[i+1]).stateChanged(changeEvent); } } }
Returns the object in the sequence that comes before the object returned by getValue(). If the end of the sequence has been reached then return null. Calling this method does not effect value.

This method simply delegates to the model. It is equivalent to:

getModel().getPreviousValue()
See Also:
Returns:the previous legal value or null if one doesn't exist
/** * Returns the object in the sequence that comes * before the object returned by <code>getValue()</code>. * If the end of the sequence has been reached then * return <code>null</code>. Calling this method does * not effect <code>value</code>. * <p> * This method simply delegates to the <code>model</code>. * It is equivalent to: * <pre> * getModel().getPreviousValue() * </pre> * * @return the previous legal value or <code>null</code> * if one doesn't exist * @see #getValue * @see #getNextValue * @see SpinnerModel#getPreviousValue */
public Object getPreviousValue() { return getModel().getPreviousValue(); }
Changes the JComponent that displays the current value of the SpinnerModel. It is the responsibility of this method to disconnect the old editor from the model and to connect the new editor. This may mean removing the old editors ChangeListener from the model or the spinner itself and adding one for the new editor.
Params:
  • editor – the new editor
Throws:
See Also:
@beaninfo bound: true attribute: visualUpdate true description: JComponent that displays the current value of the model
/** * Changes the <code>JComponent</code> that displays the current value * of the <code>SpinnerModel</code>. It is the responsibility of this * method to <i>disconnect</i> the old editor from the model and to * connect the new editor. This may mean removing the * old editors <code>ChangeListener</code> from the model or the * spinner itself and adding one for the new editor. * * @param editor the new editor * @see #getEditor * @see #createEditor * @see #getModel * @throws IllegalArgumentException if editor is <code>null</code> * * @beaninfo * bound: true * attribute: visualUpdate true * description: JComponent that displays the current value of the model */
public void setEditor(JComponent editor) { if (editor == null) { throw new IllegalArgumentException("null editor"); } if (!editor.equals(this.editor)) { JComponent oldEditor = this.editor; this.editor = editor; if (oldEditor instanceof DefaultEditor) { ((DefaultEditor)oldEditor).dismiss(this); } editorExplicitlySet = true; firePropertyChange("editor", oldEditor, editor); revalidate(); repaint(); } }
Returns the component that displays and potentially changes the model's value.
See Also:
Returns:the component that displays and potentially changes the model's value
/** * Returns the component that displays and potentially * changes the model's value. * * @return the component that displays and potentially * changes the model's value * @see #setEditor * @see #createEditor */
public JComponent getEditor() { return editor; }
Commits the currently edited value to the SpinnerModel.

If the editor is an instance of DefaultEditor, the call if forwarded to the editor, otherwise this does nothing.

Throws:
  • ParseException – if the currently edited value couldn't be committed.
/** * Commits the currently edited value to the <code>SpinnerModel</code>. * <p> * If the editor is an instance of <code>DefaultEditor</code>, the * call if forwarded to the editor, otherwise this does nothing. * * @throws ParseException if the currently edited value couldn't * be committed. */
public void commitEdit() throws ParseException { JComponent editor = getEditor(); if (editor instanceof DefaultEditor) { ((DefaultEditor)editor).commitEdit(); } } /* * See readObject and writeObject in JComponent for more * information about serialization in Swing. * * @param s Stream to write to */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); if (getUIClassID().equals(uiClassID)) { byte count = JComponent.getWriteObjCounter(this); JComponent.setWriteObjCounter(this, --count); if (count == 0 && ui != null) { ui.installUI(this); } } }
A simple base class for more specialized editors that displays a read-only view of the model's current value with a JFormattedTextField. Subclasses can configure the JFormattedTextField to create an editor that's appropriate for the type of model they support and they may want to override the stateChanged and propertyChanged methods, which keep the model and the text field in sync.

This class defines a dismiss method that removes the editors ChangeListener from the JSpinner that it's part of. The setEditor method knows about DefaultEditor.dismiss, so if the developer replaces an editor that's derived from JSpinner.DefaultEditor its ChangeListener connection back to the JSpinner will be removed. However after that, it's up to the developer to manage their editor listeners. Similarly, if a subclass overrides createEditor, it's up to the subclasser to deal with their editor subsequently being replaced (with setEditor). We expect that in most cases, and in editor installed with setEditor or created by a createEditor override, will not be replaced anyway.

This class is the LayoutManager for it's single JFormattedTextField child. By default the child is just centered with the parents insets.

Since:1.4
/** * A simple base class for more specialized editors * that displays a read-only view of the model's current * value with a <code>JFormattedTextField</code>. Subclasses * can configure the <code>JFormattedTextField</code> to create * an editor that's appropriate for the type of model they * support and they may want to override * the <code>stateChanged</code> and <code>propertyChanged</code> * methods, which keep the model and the text field in sync. * <p> * This class defines a <code>dismiss</code> method that removes the * editors <code>ChangeListener</code> from the <code>JSpinner</code> * that it's part of. The <code>setEditor</code> method knows about * <code>DefaultEditor.dismiss</code>, so if the developer * replaces an editor that's derived from <code>JSpinner.DefaultEditor</code> * its <code>ChangeListener</code> connection back to the * <code>JSpinner</code> will be removed. However after that, * it's up to the developer to manage their editor listeners. * Similarly, if a subclass overrides <code>createEditor</code>, * it's up to the subclasser to deal with their editor * subsequently being replaced (with <code>setEditor</code>). * We expect that in most cases, and in editor installed * with <code>setEditor</code> or created by a <code>createEditor</code> * override, will not be replaced anyway. * <p> * This class is the <code>LayoutManager</code> for it's single * <code>JFormattedTextField</code> child. By default the * child is just centered with the parents insets. * @since 1.4 */
public static class DefaultEditor extends JPanel implements ChangeListener, PropertyChangeListener, LayoutManager {
Constructs an editor component for the specified JSpinner. This DefaultEditor is it's own layout manager and it is added to the spinner's ChangeListener list. The constructor creates a single JFormattedTextField child, initializes it's value to be the spinner model's current value and adds it to this DefaultEditor.
Params:
  • spinner – the spinner whose model this editor will monitor
See Also:
/** * Constructs an editor component for the specified <code>JSpinner</code>. * This <code>DefaultEditor</code> is it's own layout manager and * it is added to the spinner's <code>ChangeListener</code> list. * The constructor creates a single <code>JFormattedTextField</code> child, * initializes it's value to be the spinner model's current value * and adds it to <code>this</code> <code>DefaultEditor</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @see #getTextField * @see JSpinner#addChangeListener */
public DefaultEditor(JSpinner spinner) { super(null); JFormattedTextField ftf = new JFormattedTextField(); ftf.setName("Spinner.formattedTextField"); ftf.setValue(spinner.getValue()); ftf.addPropertyChangeListener(this); ftf.setEditable(false); ftf.setInheritsPopupMenu(true); String toolTipText = spinner.getToolTipText(); if (toolTipText != null) { ftf.setToolTipText(toolTipText); } add(ftf); setLayout(this); spinner.addChangeListener(this); // We want the spinner's increment/decrement actions to be // active vs those of the JFormattedTextField. As such we // put disabled actions in the JFormattedTextField's actionmap. // A binding to a disabled action is treated as a nonexistant // binding. ActionMap ftfMap = ftf.getActionMap(); if (ftfMap != null) { ftfMap.put("increment", DISABLED_ACTION); ftfMap.put("decrement", DISABLED_ACTION); } }
Disconnect this editor from the specified JSpinner. By default, this method removes itself from the spinners ChangeListener list.
Params:
  • spinner – the JSpinner to disconnect this editor from; the same spinner as was passed to the constructor.
/** * Disconnect <code>this</code> editor from the specified * <code>JSpinner</code>. By default, this method removes * itself from the spinners <code>ChangeListener</code> list. * * @param spinner the <code>JSpinner</code> to disconnect this * editor from; the same spinner as was passed to the constructor. */
public void dismiss(JSpinner spinner) { spinner.removeChangeListener(this); }
Returns the JSpinner ancestor of this editor or null if none of the ancestors are a JSpinner. Typically the editor's parent is a JSpinner however subclasses of JSpinner may override the the createEditor method and insert one or more containers between the JSpinner and it's editor.
See Also:
Returns:JSpinner ancestor; null if none of the ancestors are a JSpinner
/** * Returns the <code>JSpinner</code> ancestor of this editor or * <code>null</code> if none of the ancestors are a * <code>JSpinner</code>. * Typically the editor's parent is a <code>JSpinner</code> however * subclasses of <code>JSpinner</code> may override the * the <code>createEditor</code> method and insert one or more containers * between the <code>JSpinner</code> and it's editor. * * @return <code>JSpinner</code> ancestor; <code>null</code> * if none of the ancestors are a <code>JSpinner</code> * * @see JSpinner#createEditor */
public JSpinner getSpinner() { for (Component c = this; c != null; c = c.getParent()) { if (c instanceof JSpinner) { return (JSpinner)c; } } return null; }
Returns the JFormattedTextField child of this editor. By default the text field is the first and only child of editor.
See Also:
Returns:the JFormattedTextField that gives the user access to the SpinnerDateModel's value.
/** * Returns the <code>JFormattedTextField</code> child of this * editor. By default the text field is the first and only * child of editor. * * @return the <code>JFormattedTextField</code> that gives the user * access to the <code>SpinnerDateModel's</code> value. * @see #getSpinner * @see #getModel */
public JFormattedTextField getTextField() { return (JFormattedTextField)getComponent(0); }
This method is called when the spinner's model's state changes. It sets the value of the text field to the current value of the spinners model.
Params:
  • e – the ChangeEvent whose source is the JSpinner whose model has changed.
See Also:
/** * This method is called when the spinner's model's state changes. * It sets the <code>value</code> of the text field to the current * value of the spinners model. * * @param e the <code>ChangeEvent</code> whose source is the * <code>JSpinner</code> whose model has changed. * @see #getTextField * @see JSpinner#getValue */
public void stateChanged(ChangeEvent e) { JSpinner spinner = (JSpinner)(e.getSource()); getTextField().setValue(spinner.getValue()); }
Called by the JFormattedTextField PropertyChangeListener. When the "value" property changes, which implies that the user has typed a new number, we set the value of the spinners model.

This class ignores PropertyChangeEvents whose source is not the JFormattedTextField, so subclasses may safely make this DefaultEditor a PropertyChangeListener on other objects.

Params:
  • e – the PropertyChangeEvent whose source is the JFormattedTextField created by this class.
See Also:
/** * Called by the <code>JFormattedTextField</code> * <code>PropertyChangeListener</code>. When the <code>"value"</code> * property changes, which implies that the user has typed a new * number, we set the value of the spinners model. * <p> * This class ignores <code>PropertyChangeEvents</code> whose * source is not the <code>JFormattedTextField</code>, so subclasses * may safely make <code>this</code> <code>DefaultEditor</code> a * <code>PropertyChangeListener</code> on other objects. * * @param e the <code>PropertyChangeEvent</code> whose source is * the <code>JFormattedTextField</code> created by this class. * @see #getTextField */
public void propertyChange(PropertyChangeEvent e) { JSpinner spinner = getSpinner(); if (spinner == null) { // Indicates we aren't installed anywhere. return; } Object source = e.getSource(); String name = e.getPropertyName(); if ((source instanceof JFormattedTextField) && "value".equals(name)) { Object lastValue = spinner.getValue(); // Try to set the new value try { spinner.setValue(getTextField().getValue()); } catch (IllegalArgumentException iae) { // SpinnerModel didn't like new value, reset try { ((JFormattedTextField)source).setValue(lastValue); } catch (IllegalArgumentException iae2) { // Still bogus, nothing else we can do, the // SpinnerModel and JFormattedTextField are now out // of sync. } } } }
This LayoutManager method does nothing. We're only managing a single child and there's no support for layout constraints.
Params:
  • name – ignored
  • child – ignored
/** * This <code>LayoutManager</code> method does nothing. We're * only managing a single child and there's no support * for layout constraints. * * @param name ignored * @param child ignored */
public void addLayoutComponent(String name, Component child) { }
This LayoutManager method does nothing. There isn't any per-child state.
Params:
  • child – ignored
/** * This <code>LayoutManager</code> method does nothing. There * isn't any per-child state. * * @param child ignored */
public void removeLayoutComponent(Component child) { }
Returns the size of the parents insets.
/** * Returns the size of the parents insets. */
private Dimension insetSize(Container parent) { Insets insets = parent.getInsets(); int w = insets.left + insets.right; int h = insets.top + insets.bottom; return new Dimension(w, h); }
Returns the preferred size of first (and only) child plus the size of the parents insets.
Params:
  • parent – the Container that's managing the layout
Returns:the preferred dimensions to lay out the subcomponents of the specified container.
/** * Returns the preferred size of first (and only) child plus the * size of the parents insets. * * @param parent the Container that's managing the layout * @return the preferred dimensions to lay out the subcomponents * of the specified container. */
public Dimension preferredLayoutSize(Container parent) { Dimension preferredSize = insetSize(parent); if (parent.getComponentCount() > 0) { Dimension childSize = getComponent(0).getPreferredSize(); preferredSize.width += childSize.width; preferredSize.height += childSize.height; } return preferredSize; }
Returns the minimum size of first (and only) child plus the size of the parents insets.
Params:
  • parent – the Container that's managing the layout
Returns: the minimum dimensions needed to lay out the subcomponents of the specified container.
/** * Returns the minimum size of first (and only) child plus the * size of the parents insets. * * @param parent the Container that's managing the layout * @return the minimum dimensions needed to lay out the subcomponents * of the specified container. */
public Dimension minimumLayoutSize(Container parent) { Dimension minimumSize = insetSize(parent); if (parent.getComponentCount() > 0) { Dimension childSize = getComponent(0).getMinimumSize(); minimumSize.width += childSize.width; minimumSize.height += childSize.height; } return minimumSize; }
Resize the one (and only) child to completely fill the area within the parents insets.
/** * Resize the one (and only) child to completely fill the area * within the parents insets. */
public void layoutContainer(Container parent) { if (parent.getComponentCount() > 0) { Insets insets = parent.getInsets(); int w = parent.getWidth() - (insets.left + insets.right); int h = parent.getHeight() - (insets.top + insets.bottom); getComponent(0).setBounds(insets.left, insets.top, w, h); } }
Pushes the currently edited value to the SpinnerModel.

The default implementation invokes commitEdit on the JFormattedTextField.

Throws:
  • ParseException – if the edited value is not legal
/** * Pushes the currently edited value to the <code>SpinnerModel</code>. * <p> * The default implementation invokes <code>commitEdit</code> on the * <code>JFormattedTextField</code>. * * @throws ParseException if the edited value is not legal */
public void commitEdit() throws ParseException { // If the value in the JFormattedTextField is legal, this will have // the result of pushing the value to the SpinnerModel // by way of the <code>propertyChange</code> method. JFormattedTextField ftf = getTextField(); ftf.commitEdit(); }
Returns the baseline.
Throws:
  • IllegalArgumentException – {@inheritDoc}
See Also:
Since:1.6
/** * Returns the baseline. * * @throws IllegalArgumentException {@inheritDoc} * @see javax.swing.JComponent#getBaseline(int,int) * @see javax.swing.JComponent#getBaselineResizeBehavior() * @since 1.6 */
public int getBaseline(int width, int height) { // check size. super.getBaseline(width, height); Insets insets = getInsets(); width = width - insets.left - insets.right; height = height - insets.top - insets.bottom; int baseline = getComponent(0).getBaseline(width, height); if (baseline >= 0) { return baseline + insets.top; } return -1; }
Returns an enum indicating how the baseline of the component changes as the size changes.
Throws:
  • NullPointerException – {@inheritDoc}
See Also:
Since:1.6
/** * Returns an enum indicating how the baseline of the component * changes as the size changes. * * @throws NullPointerException {@inheritDoc} * @see javax.swing.JComponent#getBaseline(int, int) * @since 1.6 */
public BaselineResizeBehavior getBaselineResizeBehavior() { return getComponent(0).getBaselineResizeBehavior(); } }
This subclass of javax.swing.DateFormatter maps the minimum/maximum properties to te start/end properties of a SpinnerDateModel.
/** * This subclass of javax.swing.DateFormatter maps the minimum/maximum * properties to te start/end properties of a SpinnerDateModel. */
private static class DateEditorFormatter extends DateFormatter { private final SpinnerDateModel model; DateEditorFormatter(SpinnerDateModel model, DateFormat format) { super(format); this.model = model; } public void setMinimum(Comparable min) { model.setStart(min); } public Comparable getMinimum() { return model.getStart(); } public void setMaximum(Comparable max) { model.setEnd(max); } public Comparable getMaximum() { return model.getEnd(); } }
An editor for a JSpinner whose model is a SpinnerDateModel. The value of the editor is displayed with a JFormattedTextField whose format is defined by a DateFormatter instance whose minimum and maximum properties are mapped to the SpinnerDateModel.
Since:1.4
/** * An editor for a <code>JSpinner</code> whose model is a * <code>SpinnerDateModel</code>. The value of the editor is * displayed with a <code>JFormattedTextField</code> whose format * is defined by a <code>DateFormatter</code> instance whose * <code>minimum</code> and <code>maximum</code> properties * are mapped to the <code>SpinnerDateModel</code>. * @since 1.4 */
// PENDING(hmuller): more example javadoc public static class DateEditor extends DefaultEditor { // This is here until SimpleDateFormat gets a constructor that // takes a Locale: 4923525 private static String getDefaultPattern(Locale loc) { LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DateFormatProvider.class, loc); LocaleResources lr = adapter.getLocaleResources(loc); if (lr == null) { lr = LocaleProviderAdapter.forJRE().getLocaleResources(loc); } return lr.getDateTimePattern(DateFormat.SHORT, DateFormat.SHORT, null); }
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerDateModel with a JFormattedTextField. This DateEditor becomes both a ChangeListener on the spinners model and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
Throws:
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerDateModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>DateEditor</code> becomes both a <code>ChangeListener</code> * on the spinners model and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerDateModel</code> * * @see #getModel * @see #getFormat * @see SpinnerDateModel */
public DateEditor(JSpinner spinner) { this(spinner, getDefaultPattern(spinner.getLocale())); }
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerDateModel with a JFormattedTextField. This DateEditor becomes both a ChangeListener on the spinner and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
  • dateFormatPattern – the initial pattern for the SimpleDateFormat object that's used to display and parse the value of the text field.
Throws:
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerDateModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>DateEditor</code> becomes both a <code>ChangeListener</code> * on the spinner and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @param dateFormatPattern the initial pattern for the * <code>SimpleDateFormat</code> object that's used to display * and parse the value of the text field. * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerDateModel</code> * * @see #getModel * @see #getFormat * @see SpinnerDateModel * @see java.text.SimpleDateFormat */
public DateEditor(JSpinner spinner, String dateFormatPattern) { this(spinner, new SimpleDateFormat(dateFormatPattern, spinner.getLocale())); }
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerDateModel with a JFormattedTextField. This DateEditor becomes both a ChangeListener on the spinner and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
  • format – DateFormat object that's used to display and parse the value of the text field.
Throws:
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerDateModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>DateEditor</code> becomes both a <code>ChangeListener</code> * on the spinner and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor * will monitor * @param format <code>DateFormat</code> object that's used to display * and parse the value of the text field. * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerDateModel</code> * * @see #getModel * @see #getFormat * @see SpinnerDateModel * @see java.text.SimpleDateFormat */
private DateEditor(JSpinner spinner, DateFormat format) { super(spinner); if (!(spinner.getModel() instanceof SpinnerDateModel)) { throw new IllegalArgumentException( "model not a SpinnerDateModel"); } SpinnerDateModel model = (SpinnerDateModel)spinner.getModel(); DateFormatter formatter = new DateEditorFormatter(model, format); DefaultFormatterFactory factory = new DefaultFormatterFactory( formatter); JFormattedTextField ftf = getTextField(); ftf.setEditable(true); ftf.setFormatterFactory(factory); /* TBD - initializing the column width of the text field * is imprecise and doing it here is tricky because * the developer may configure the formatter later. */ try { String maxString = formatter.valueToString(model.getStart()); String minString = formatter.valueToString(model.getEnd()); ftf.setColumns(Math.max(maxString.length(), minString.length())); } catch (ParseException e) { // PENDING: hmuller } }
Returns the java.text.SimpleDateFormat object the JFormattedTextField uses to parse and format numbers.
See Also:
Returns:the value of getTextField().getFormatter().getFormat().
/** * Returns the <code>java.text.SimpleDateFormat</code> object the * <code>JFormattedTextField</code> uses to parse and format * numbers. * * @return the value of <code>getTextField().getFormatter().getFormat()</code>. * @see #getTextField * @see java.text.SimpleDateFormat */
public SimpleDateFormat getFormat() { return (SimpleDateFormat)((DateFormatter)(getTextField().getFormatter())).getFormat(); }
Return our spinner ancestor's SpinnerDateModel.
See Also:
Returns:getSpinner().getModel()
/** * Return our spinner ancestor's <code>SpinnerDateModel</code>. * * @return <code>getSpinner().getModel()</code> * @see #getSpinner * @see #getTextField */
public SpinnerDateModel getModel() { return (SpinnerDateModel)(getSpinner().getModel()); } }
This subclass of javax.swing.NumberFormatter maps the minimum/maximum properties to a SpinnerNumberModel and initializes the valueClass of the NumberFormatter to match the type of the initial models value.
/** * This subclass of javax.swing.NumberFormatter maps the minimum/maximum * properties to a SpinnerNumberModel and initializes the valueClass * of the NumberFormatter to match the type of the initial models value. */
private static class NumberEditorFormatter extends NumberFormatter { private final SpinnerNumberModel model; NumberEditorFormatter(SpinnerNumberModel model, NumberFormat format) { super(format); this.model = model; setValueClass(model.getValue().getClass()); } public void setMinimum(Comparable min) { model.setMinimum(min); } public Comparable getMinimum() { return model.getMinimum(); } public void setMaximum(Comparable max) { model.setMaximum(max); } public Comparable getMaximum() { return model.getMaximum(); } }
An editor for a JSpinner whose model is a SpinnerNumberModel. The value of the editor is displayed with a JFormattedTextField whose format is defined by a NumberFormatter instance whose minimum and maximum properties are mapped to the SpinnerNumberModel.
Since:1.4
/** * An editor for a <code>JSpinner</code> whose model is a * <code>SpinnerNumberModel</code>. The value of the editor is * displayed with a <code>JFormattedTextField</code> whose format * is defined by a <code>NumberFormatter</code> instance whose * <code>minimum</code> and <code>maximum</code> properties * are mapped to the <code>SpinnerNumberModel</code>. * @since 1.4 */
// PENDING(hmuller): more example javadoc public static class NumberEditor extends DefaultEditor { // This is here until DecimalFormat gets a constructor that // takes a Locale: 4923525 private static String getDefaultPattern(Locale locale) { // Get the pattern for the default locale. LocaleProviderAdapter adapter; adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, locale); LocaleResources lr = adapter.getLocaleResources(locale); if (lr == null) { lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale); } String[] all = lr.getNumberPatterns(); return all[0]; }
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerNumberModel with a JFormattedTextField. This NumberEditor becomes both a ChangeListener on the spinner and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
Throws:
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerNumberModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>NumberEditor</code> becomes both a <code>ChangeListener</code> * on the spinner and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerNumberModel</code> * * @see #getModel * @see #getFormat * @see SpinnerNumberModel */
public NumberEditor(JSpinner spinner) { this(spinner, getDefaultPattern(spinner.getLocale())); }
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerNumberModel with a JFormattedTextField. This NumberEditor becomes both a ChangeListener on the spinner and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
  • decimalFormatPattern – the initial pattern for the DecimalFormat object that's used to display and parse the value of the text field.
Throws:
  • IllegalArgumentException – if the spinners model is not an instance of SpinnerNumberModel or if decimalFormatPattern is not a legal argument to DecimalFormat
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerNumberModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>NumberEditor</code> becomes both a <code>ChangeListener</code> * on the spinner and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @param decimalFormatPattern the initial pattern for the * <code>DecimalFormat</code> object that's used to display * and parse the value of the text field. * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerNumberModel</code> or if * <code>decimalFormatPattern</code> is not a legal * argument to <code>DecimalFormat</code> * * @see #getTextField * @see SpinnerNumberModel * @see java.text.DecimalFormat */
public NumberEditor(JSpinner spinner, String decimalFormatPattern) { this(spinner, new DecimalFormat(decimalFormatPattern)); }
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerNumberModel with a JFormattedTextField. This NumberEditor becomes both a ChangeListener on the spinner and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
  • decimalFormatPattern – the initial pattern for the DecimalFormat object that's used to display and parse the value of the text field.
Throws:
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerNumberModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>NumberEditor</code> becomes both a <code>ChangeListener</code> * on the spinner and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @param decimalFormatPattern the initial pattern for the * <code>DecimalFormat</code> object that's used to display * and parse the value of the text field. * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerNumberModel</code> * * @see #getTextField * @see SpinnerNumberModel * @see java.text.DecimalFormat */
private NumberEditor(JSpinner spinner, DecimalFormat format) { super(spinner); if (!(spinner.getModel() instanceof SpinnerNumberModel)) { throw new IllegalArgumentException( "model not a SpinnerNumberModel"); } SpinnerNumberModel model = (SpinnerNumberModel)spinner.getModel(); NumberFormatter formatter = new NumberEditorFormatter(model, format); DefaultFormatterFactory factory = new DefaultFormatterFactory( formatter); JFormattedTextField ftf = getTextField(); ftf.setEditable(true); ftf.setFormatterFactory(factory); ftf.setHorizontalAlignment(JTextField.RIGHT); /* TBD - initializing the column width of the text field * is imprecise and doing it here is tricky because * the developer may configure the formatter later. */ try { String maxString = formatter.valueToString(model.getMinimum()); String minString = formatter.valueToString(model.getMaximum()); ftf.setColumns(Math.max(maxString.length(), minString.length())); } catch (ParseException e) { // TBD should throw a chained error here } }
Returns the java.text.DecimalFormat object the JFormattedTextField uses to parse and format numbers.
See Also:
Returns:the value of getTextField().getFormatter().getFormat().
/** * Returns the <code>java.text.DecimalFormat</code> object the * <code>JFormattedTextField</code> uses to parse and format * numbers. * * @return the value of <code>getTextField().getFormatter().getFormat()</code>. * @see #getTextField * @see java.text.DecimalFormat */
public DecimalFormat getFormat() { return (DecimalFormat)((NumberFormatter)(getTextField().getFormatter())).getFormat(); }
Return our spinner ancestor's SpinnerNumberModel.
See Also:
Returns:getSpinner().getModel()
/** * Return our spinner ancestor's <code>SpinnerNumberModel</code>. * * @return <code>getSpinner().getModel()</code> * @see #getSpinner * @see #getTextField */
public SpinnerNumberModel getModel() { return (SpinnerNumberModel)(getSpinner().getModel()); } }
An editor for a JSpinner whose model is a SpinnerListModel.
Since:1.4
/** * An editor for a <code>JSpinner</code> whose model is a * <code>SpinnerListModel</code>. * @since 1.4 */
public static class ListEditor extends DefaultEditor {
Construct a JSpinner editor that supports displaying and editing the value of a SpinnerListModel with a JFormattedTextField. This ListEditor becomes both a ChangeListener on the spinner and a PropertyChangeListener on the new JFormattedTextField.
Params:
  • spinner – the spinner whose model this editor will monitor
Throws:
See Also:
/** * Construct a <code>JSpinner</code> editor that supports displaying * and editing the value of a <code>SpinnerListModel</code> * with a <code>JFormattedTextField</code>. <code>This</code> * <code>ListEditor</code> becomes both a <code>ChangeListener</code> * on the spinner and a <code>PropertyChangeListener</code> * on the new <code>JFormattedTextField</code>. * * @param spinner the spinner whose model <code>this</code> editor will monitor * @exception IllegalArgumentException if the spinners model is not * an instance of <code>SpinnerListModel</code> * * @see #getModel * @see SpinnerListModel */
public ListEditor(JSpinner spinner) { super(spinner); if (!(spinner.getModel() instanceof SpinnerListModel)) { throw new IllegalArgumentException("model not a SpinnerListModel"); } getTextField().setEditable(true); getTextField().setFormatterFactory(new DefaultFormatterFactory(new ListFormatter())); }
Return our spinner ancestor's SpinnerNumberModel.
See Also:
Returns:getSpinner().getModel()
/** * Return our spinner ancestor's <code>SpinnerNumberModel</code>. * * @return <code>getSpinner().getModel()</code> * @see #getSpinner * @see #getTextField */
public SpinnerListModel getModel() { return (SpinnerListModel)(getSpinner().getModel()); }
ListFormatter provides completion while text is being input into the JFormattedTextField. Completion is only done if the user is inserting text at the end of the document. Completion is done by way of the SpinnerListModel method findNextMatch.
/** * ListFormatter provides completion while text is being input * into the JFormattedTextField. Completion is only done if the * user is inserting text at the end of the document. Completion * is done by way of the SpinnerListModel method findNextMatch. */
private class ListFormatter extends JFormattedTextField.AbstractFormatter { private DocumentFilter filter; public String valueToString(Object value) throws ParseException { if (value == null) { return ""; } return value.toString(); } public Object stringToValue(String string) throws ParseException { return string; } protected DocumentFilter getDocumentFilter() { if (filter == null) { filter = new Filter(); } return filter; } private class Filter extends DocumentFilter { public void replace(FilterBypass fb, int offset, int length, String string, AttributeSet attrs) throws BadLocationException { if (string != null && (offset + length) == fb.getDocument().getLength()) { Object next = getModel().findNextMatch( fb.getDocument().getText(0, offset) + string); String value = (next != null) ? next.toString() : null; if (value != null) { fb.remove(0, offset + length); fb.insertString(0, value, null); getFormattedTextField().select(offset + string.length(), value.length()); return; } } super.replace(fb, offset, length, string, attrs); } public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { replace(fb, offset, 0, string, attr); } } } }
An Action implementation that is always disabled.
/** * An Action implementation that is always disabled. */
private static class DisabledAction implements Action { public Object getValue(String key) { return null; } public void putValue(String key, Object value) { } public void setEnabled(boolean b) { } public boolean isEnabled() { return false; } public void addPropertyChangeListener(PropertyChangeListener l) { } public void removePropertyChangeListener(PropertyChangeListener l) { } public void actionPerformed(ActionEvent ae) { } } ///////////////// // Accessibility support ////////////////
Gets the AccessibleContext for the JSpinner
Returns:the AccessibleContext for the JSpinner
Since:1.5
/** * Gets the <code>AccessibleContext</code> for the <code>JSpinner</code> * * @return the <code>AccessibleContext</code> for the <code>JSpinner</code> * @since 1.5 */
public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessibleJSpinner(); } return accessibleContext; }
AccessibleJSpinner implements accessibility support for the JSpinner class.
Since:1.5
/** * <code>AccessibleJSpinner</code> implements accessibility * support for the <code>JSpinner</code> class. * @since 1.5 */
protected class AccessibleJSpinner extends AccessibleJComponent implements AccessibleValue, AccessibleAction, AccessibleText, AccessibleEditableText, ChangeListener { private Object oldModelValue = null;
AccessibleJSpinner constructor
/** * AccessibleJSpinner constructor */
protected AccessibleJSpinner() { // model is guaranteed to be non-null oldModelValue = model.getValue(); JSpinner.this.addChangeListener(this); }
Invoked when the target of the listener has changed its state.
Params:
  • e – a ChangeEvent object. Must not be null.
Throws:
/** * Invoked when the target of the listener has changed its state. * * @param e a <code>ChangeEvent</code> object. Must not be null. * @throws NullPointerException if the parameter is null. */
public void stateChanged(ChangeEvent e) { if (e == null) { throw new NullPointerException(); } Object newModelValue = model.getValue(); firePropertyChange(ACCESSIBLE_VALUE_PROPERTY, oldModelValue, newModelValue); firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, 0); // entire text may have changed oldModelValue = newModelValue; } /* ===== Begin AccessibleContext methods ===== */
Gets the role of this object. The role of the object is the generic purpose or use of the class of this object. For example, the role of a push button is AccessibleRole.PUSH_BUTTON. The roles in AccessibleRole are provided so component developers can pick from a set of predefined roles. This enables assistive technologies to provide a consistent interface to various tweaked subclasses of components (e.g., use AccessibleRole.PUSH_BUTTON for all components that act like a push button) as well as distinguish between subclasses that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes and AccessibleRole.RADIO_BUTTON for radio buttons).

Note that the AccessibleRole class is also extensible, so custom component developers can define their own AccessibleRole's if the set of predefined roles is inadequate.

See Also:
Returns:an instance of AccessibleRole describing the role of the object
/** * Gets the role of this object. The role of the object is the generic * purpose or use of the class of this object. For example, the role * of a push button is AccessibleRole.PUSH_BUTTON. The roles in * AccessibleRole are provided so component developers can pick from * a set of predefined roles. This enables assistive technologies to * provide a consistent interface to various tweaked subclasses of * components (e.g., use AccessibleRole.PUSH_BUTTON for all components * that act like a push button) as well as distinguish between subclasses * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes * and AccessibleRole.RADIO_BUTTON for radio buttons). * <p>Note that the AccessibleRole class is also extensible, so * custom component developers can define their own AccessibleRole's * if the set of predefined roles is inadequate. * * @return an instance of AccessibleRole describing the role of the object * @see AccessibleRole */
public AccessibleRole getAccessibleRole() { return AccessibleRole.SPIN_BOX; }
Returns the number of accessible children of the object.
Returns:the number of accessible children of the object.
/** * Returns the number of accessible children of the object. * * @return the number of accessible children of the object. */
public int getAccessibleChildrenCount() { // the JSpinner has one child, the editor if (editor.getAccessibleContext() != null) { return 1; } return 0; }
Returns the specified Accessible child of the object. The Accessible children of an Accessible object are zero-based, so the first child of an Accessible child is at index 0, the second child is at index 1, and so on.
Params:
  • i – zero-based index of child
See Also:
Returns:the Accessible child of the object
/** * Returns the specified Accessible child of the object. The Accessible * children of an Accessible object are zero-based, so the first child * of an Accessible child is at index 0, the second child is at index 1, * and so on. * * @param i zero-based index of child * @return the Accessible child of the object * @see #getAccessibleChildrenCount */
public Accessible getAccessibleChild(int i) { // the JSpinner has one child, the editor if (i != 0) { return null; } if (editor.getAccessibleContext() != null) { return (Accessible)editor; } return null; } /* ===== End AccessibleContext methods ===== */
Gets the AccessibleAction associated with this object that supports one or more actions.
See Also:
Returns:AccessibleAction if supported by object; else return null
/** * Gets the AccessibleAction associated with this object that supports * one or more actions. * * @return AccessibleAction if supported by object; else return null * @see AccessibleAction */
public AccessibleAction getAccessibleAction() { return this; }
Gets the AccessibleText associated with this object presenting text on the display.
See Also:
Returns:AccessibleText if supported by object; else return null
/** * Gets the AccessibleText associated with this object presenting * text on the display. * * @return AccessibleText if supported by object; else return null * @see AccessibleText */
public AccessibleText getAccessibleText() { return this; } /* * Returns the AccessibleContext for the JSpinner editor */ private AccessibleContext getEditorAccessibleContext() { if (editor instanceof DefaultEditor) { JTextField textField = ((DefaultEditor)editor).getTextField(); if (textField != null) { return textField.getAccessibleContext(); } } else if (editor instanceof Accessible) { return editor.getAccessibleContext(); } return null; } /* * Returns the AccessibleText for the JSpinner editor */ private AccessibleText getEditorAccessibleText() { AccessibleContext ac = getEditorAccessibleContext(); if (ac != null) { return ac.getAccessibleText(); } return null; } /* * Returns the AccessibleEditableText for the JSpinner editor */ private AccessibleEditableText getEditorAccessibleEditableText() { AccessibleText at = getEditorAccessibleText(); if (at instanceof AccessibleEditableText) { return (AccessibleEditableText)at; } return null; }
Gets the AccessibleValue associated with this object.
See Also:
Returns:AccessibleValue if supported by object; else return null
/** * Gets the AccessibleValue associated with this object. * * @return AccessibleValue if supported by object; else return null * @see AccessibleValue * */
public AccessibleValue getAccessibleValue() { return this; } /* ===== Begin AccessibleValue impl ===== */
Get the value of this object as a Number. If the value has not been set, the return value will be null.
See Also:
Returns:value of the object
/** * Get the value of this object as a Number. If the value has not been * set, the return value will be null. * * @return value of the object * @see #setCurrentAccessibleValue */
public Number getCurrentAccessibleValue() { Object o = model.getValue(); if (o instanceof Number) { return (Number)o; } return null; }
Set the value of this object as a Number.
Params:
  • n – the value to set for this object
See Also:
Returns:true if the value was set; else False
/** * Set the value of this object as a Number. * * @param n the value to set for this object * @return true if the value was set; else False * @see #getCurrentAccessibleValue */
public boolean setCurrentAccessibleValue(Number n) { // try to set the new value try { model.setValue(n); return true; } catch (IllegalArgumentException iae) { // SpinnerModel didn't like new value } return false; }
Get the minimum value of this object as a Number.
See Also:
Returns:Minimum value of the object; null if this object does not have a minimum value
/** * Get the minimum value of this object as a Number. * * @return Minimum value of the object; null if this object does not * have a minimum value * @see #getMaximumAccessibleValue */
public Number getMinimumAccessibleValue() { if (model instanceof SpinnerNumberModel) { SpinnerNumberModel numberModel = (SpinnerNumberModel)model; Object o = numberModel.getMinimum(); if (o instanceof Number) { return (Number)o; } } return null; }
Get the maximum value of this object as a Number.
See Also:
Returns:Maximum value of the object; null if this object does not have a maximum value
/** * Get the maximum value of this object as a Number. * * @return Maximum value of the object; null if this object does not * have a maximum value * @see #getMinimumAccessibleValue */
public Number getMaximumAccessibleValue() { if (model instanceof SpinnerNumberModel) { SpinnerNumberModel numberModel = (SpinnerNumberModel)model; Object o = numberModel.getMaximum(); if (o instanceof Number) { return (Number)o; } } return null; } /* ===== End AccessibleValue impl ===== */ /* ===== Begin AccessibleAction impl ===== */
Returns the number of accessible actions available in this object If there are more than one, the first one is considered the "default" action of the object. Two actions are supported: AccessibleAction.INCREMENT which increments the spinner value and AccessibleAction.DECREMENT which decrements the spinner value
Returns:the zero-based number of Actions in this object
/** * Returns the number of accessible actions available in this object * If there are more than one, the first one is considered the "default" * action of the object. * * Two actions are supported: AccessibleAction.INCREMENT which * increments the spinner value and AccessibleAction.DECREMENT * which decrements the spinner value * * @return the zero-based number of Actions in this object */
public int getAccessibleActionCount() { return 2; }
Returns a description of the specified action of the object.
Params:
  • i – zero-based index of the actions
See Also:
Returns:a String description of the action
/** * Returns a description of the specified action of the object. * * @param i zero-based index of the actions * @return a String description of the action * @see #getAccessibleActionCount */
public String getAccessibleActionDescription(int i) { if (i == 0) { return AccessibleAction.INCREMENT; } else if (i == 1) { return AccessibleAction.DECREMENT; } return null; }
Performs the specified Action on the object
Params:
  • i – zero-based index of actions. The first action (index 0) is AccessibleAction.INCREMENT and the second action (index 1) is AccessibleAction.DECREMENT.
See Also:
Returns:true if the action was performed; otherwise false.
/** * Performs the specified Action on the object * * @param i zero-based index of actions. The first action * (index 0) is AccessibleAction.INCREMENT and the second * action (index 1) is AccessibleAction.DECREMENT. * @return true if the action was performed; otherwise false. * @see #getAccessibleActionCount */
public boolean doAccessibleAction(int i) { if (i < 0 || i > 1) { return false; } Object o; if (i == 0) { o = getNextValue(); // AccessibleAction.INCREMENT } else { o = getPreviousValue(); // AccessibleAction.DECREMENT } // try to set the new value try { model.setValue(o); return true; } catch (IllegalArgumentException iae) { // SpinnerModel didn't like new value } return false; } /* ===== End AccessibleAction impl ===== */ /* ===== Begin AccessibleText impl ===== */ /* * Returns whether source and destination components have the * same window ancestor */ private boolean sameWindowAncestor(Component src, Component dest) { if (src == null || dest == null) { return false; } return SwingUtilities.getWindowAncestor(src) == SwingUtilities.getWindowAncestor(dest); }
Given a point in local coordinates, return the zero-based index of the character under that Point. If the point is invalid, this method returns -1.
Params:
  • p – the Point in local coordinates
Returns:the zero-based index of the character under Point p; if Point is invalid return -1.
/** * Given a point in local coordinates, return the zero-based index * of the character under that Point. If the point is invalid, * this method returns -1. * * @param p the Point in local coordinates * @return the zero-based index of the character under Point p; if * Point is invalid return -1. */
public int getIndexAtPoint(Point p) { AccessibleText at = getEditorAccessibleText(); if (at != null && sameWindowAncestor(JSpinner.this, editor)) { // convert point from the JSpinner bounds (source) to // editor bounds (destination) Point editorPoint = SwingUtilities.convertPoint(JSpinner.this, p, editor); if (editorPoint != null) { return at.getIndexAtPoint(editorPoint); } } return -1; }
Determines the bounding box of the character at the given index into the string. The bounds are returned in local coordinates. If the index is invalid an empty rectangle is returned.
Params:
  • i – the index into the String
Returns:the screen coordinates of the character's bounding box, if index is invalid return an empty rectangle.
/** * Determines the bounding box of the character at the given * index into the string. The bounds are returned in local * coordinates. If the index is invalid an empty rectangle is * returned. * * @param i the index into the String * @return the screen coordinates of the character's bounding box, * if index is invalid return an empty rectangle. */
public Rectangle getCharacterBounds(int i) { AccessibleText at = getEditorAccessibleText(); if (at != null ) { Rectangle editorRect = at.getCharacterBounds(i); if (editorRect != null && sameWindowAncestor(JSpinner.this, editor)) { // return rectangle in the the JSpinner bounds return SwingUtilities.convertRectangle(editor, editorRect, JSpinner.this); } } return null; }
Returns the number of characters (valid indicies)
Returns:the number of characters
/** * Returns the number of characters (valid indicies) * * @return the number of characters */
public int getCharCount() { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getCharCount(); } return -1; }
Returns the zero-based offset of the caret. Note: That to the right of the caret will have the same index value as the offset (the caret is between two characters).
Returns:the zero-based offset of the caret.
/** * Returns the zero-based offset of the caret. * * Note: That to the right of the caret will have the same index * value as the offset (the caret is between two characters). * @return the zero-based offset of the caret. */
public int getCaretPosition() { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getCaretPosition(); } return -1; }
Returns the String at a given index.
Params:
  • part – the CHARACTER, WORD, or SENTENCE to retrieve
  • index – an index within the text
Returns:the letter, word, or sentence
/** * Returns the String at a given index. * * @param part the CHARACTER, WORD, or SENTENCE to retrieve * @param index an index within the text * @return the letter, word, or sentence */
public String getAtIndex(int part, int index) { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getAtIndex(part, index); } return null; }
Returns the String after a given index.
Params:
  • part – the CHARACTER, WORD, or SENTENCE to retrieve
  • index – an index within the text
Returns:the letter, word, or sentence
/** * Returns the String after a given index. * * @param part the CHARACTER, WORD, or SENTENCE to retrieve * @param index an index within the text * @return the letter, word, or sentence */
public String getAfterIndex(int part, int index) { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getAfterIndex(part, index); } return null; }
Returns the String before a given index.
Params:
  • part – the CHARACTER, WORD, or SENTENCE to retrieve
  • index – an index within the text
Returns:the letter, word, or sentence
/** * Returns the String before a given index. * * @param part the CHARACTER, WORD, or SENTENCE to retrieve * @param index an index within the text * @return the letter, word, or sentence */
public String getBeforeIndex(int part, int index) { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getBeforeIndex(part, index); } return null; }
Returns the AttributeSet for a given character at a given index
Params:
  • i – the zero-based index into the text
Returns:the AttributeSet of the character
/** * Returns the AttributeSet for a given character at a given index * * @param i the zero-based index into the text * @return the AttributeSet of the character */
public AttributeSet getCharacterAttribute(int i) { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getCharacterAttribute(i); } return null; }
Returns the start offset within the selected text. If there is no selection, but there is a caret, the start and end offsets will be the same.
Returns:the index into the text of the start of the selection
/** * Returns the start offset within the selected text. * If there is no selection, but there is * a caret, the start and end offsets will be the same. * * @return the index into the text of the start of the selection */
public int getSelectionStart() { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getSelectionStart(); } return -1; }
Returns the end offset within the selected text. If there is no selection, but there is a caret, the start and end offsets will be the same.
Returns:the index into the text of the end of the selection
/** * Returns the end offset within the selected text. * If there is no selection, but there is * a caret, the start and end offsets will be the same. * * @return the index into the text of the end of the selection */
public int getSelectionEnd() { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getSelectionEnd(); } return -1; }
Returns the portion of the text that is selected.
Returns:the String portion of the text that is selected
/** * Returns the portion of the text that is selected. * * @return the String portion of the text that is selected */
public String getSelectedText() { AccessibleText at = getEditorAccessibleText(); if (at != null) { return at.getSelectedText(); } return null; } /* ===== End AccessibleText impl ===== */ /* ===== Begin AccessibleEditableText impl ===== */
Sets the text contents to the specified string.
Params:
  • s – the string to set the text contents
/** * Sets the text contents to the specified string. * * @param s the string to set the text contents */
public void setTextContents(String s) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.setTextContents(s); } }
Inserts the specified string at the given index/
Params:
  • index – the index in the text where the string will be inserted
  • s – the string to insert in the text
/** * Inserts the specified string at the given index/ * * @param index the index in the text where the string will * be inserted * @param s the string to insert in the text */
public void insertTextAtIndex(int index, String s) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.insertTextAtIndex(index, s); } }
Returns the text string between two indices.
Params:
  • startIndex – the starting index in the text
  • endIndex – the ending index in the text
Returns:the text string between the indices
/** * Returns the text string between two indices. * * @param startIndex the starting index in the text * @param endIndex the ending index in the text * @return the text string between the indices */
public String getTextRange(int startIndex, int endIndex) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { return at.getTextRange(startIndex, endIndex); } return null; }
Deletes the text between two indices
Params:
  • startIndex – the starting index in the text
  • endIndex – the ending index in the text
/** * Deletes the text between two indices * * @param startIndex the starting index in the text * @param endIndex the ending index in the text */
public void delete(int startIndex, int endIndex) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.delete(startIndex, endIndex); } }
Cuts the text between two indices into the system clipboard.
Params:
  • startIndex – the starting index in the text
  • endIndex – the ending index in the text
/** * Cuts the text between two indices into the system clipboard. * * @param startIndex the starting index in the text * @param endIndex the ending index in the text */
public void cut(int startIndex, int endIndex) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.cut(startIndex, endIndex); } }
Pastes the text from the system clipboard into the text starting at the specified index.
Params:
  • startIndex – the starting index in the text
/** * Pastes the text from the system clipboard into the text * starting at the specified index. * * @param startIndex the starting index in the text */
public void paste(int startIndex) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.paste(startIndex); } }
Replaces the text between two indices with the specified string.
Params:
  • startIndex – the starting index in the text
  • endIndex – the ending index in the text
  • s – the string to replace the text between two indices
/** * Replaces the text between two indices with the specified * string. * * @param startIndex the starting index in the text * @param endIndex the ending index in the text * @param s the string to replace the text between two indices */
public void replaceText(int startIndex, int endIndex, String s) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.replaceText(startIndex, endIndex, s); } }
Selects the text between two indices.
Params:
  • startIndex – the starting index in the text
  • endIndex – the ending index in the text
/** * Selects the text between two indices. * * @param startIndex the starting index in the text * @param endIndex the ending index in the text */
public void selectText(int startIndex, int endIndex) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.selectText(startIndex, endIndex); } }
Sets attributes for the text between two indices.
Params:
  • startIndex – the starting index in the text
  • endIndex – the ending index in the text
  • as – the attribute set
See Also:
/** * Sets attributes for the text between two indices. * * @param startIndex the starting index in the text * @param endIndex the ending index in the text * @param as the attribute set * @see AttributeSet */
public void setAttributes(int startIndex, int endIndex, AttributeSet as) { AccessibleEditableText at = getEditorAccessibleEditableText(); if (at != null) { at.setAttributes(startIndex, endIndex, as); } } } /* End AccessibleJSpinner */ }