2033 lines · 72.8 KB · Java
RawDownload
/* * Copyright (c) 2000, 2008, 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.util.HashMap;import sun.util.resources.LocaleData; import javax.accessibility.*;  /** * 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="http://java.sun.com/doc/books/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<sup><font size="-2">TM</font></sup> * 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 #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 {@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 <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.     *     * @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.     *     * @param ui  the <code>SpinnerUI</code> L&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.     *     * @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 UIManager#getUI     */    public void updateUI() {        setUI((SpinnerUI)UIManager.getUI(this));        invalidate();    }      /**     * 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 <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 <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 <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 <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 <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 <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 <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 <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 <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 <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 <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 <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>     *

Lines 1–500 of 2033.