0N/A/*
2362N/A * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.swing;
0N/A
0N/Aimport java.awt.*;
0N/Aimport java.awt.event.*;
0N/A
0N/Aimport javax.swing.event.*;
0N/Aimport javax.swing.text.*;
0N/Aimport javax.swing.plaf.SpinnerUI;
0N/A
0N/Aimport java.util.*;
0N/Aimport java.beans.*;
0N/Aimport java.text.*;
0N/Aimport java.io.*;
0N/Aimport java.util.HashMap;
0N/Aimport sun.util.resources.LocaleData;
0N/A
0N/Aimport javax.accessibility.*;
0N/A
0N/A
0N/A/**
0N/A * A single line input field that lets the user select a
0N/A * number or an object value from an ordered sequence. Spinners typically
0N/A * provide a pair of tiny arrow buttons for stepping through the elements
0N/A * of the sequence. The keyboard up/down arrow keys also cycle through the
0N/A * elements. The user may also be allowed to type a (legal) value directly
0N/A * into the spinner. Although combo boxes provide similar functionality,
0N/A * spinners are sometimes preferred because they don't require a drop down list
0N/A * that can obscure important data.
0N/A * <p>
0N/A * A <code>JSpinner</code>'s sequence value is defined by its
0N/A * <code>SpinnerModel</code>.
0N/A * The <code>model</code> can be specified as a constructor argument and
0N/A * changed with the <code>model</code> property. <code>SpinnerModel</code>
0N/A * classes for some common types are provided: <code>SpinnerListModel</code>,
0N/A * <code>SpinnerNumberModel</code>, and <code>SpinnerDateModel</code>.
0N/A * <p>
0N/A * A <code>JSpinner</code> has a single child component that's
0N/A * responsible for displaying
0N/A * and potentially changing the current element or <i>value</i> of
0N/A * the model, which is called the <code>editor</code>. The editor is created
0N/A * by the <code>JSpinner</code>'s constructor and can be changed with the
0N/A * <code>editor</code> property. The <code>JSpinner</code>'s editor stays
0N/A * in sync with the model by listening for <code>ChangeEvent</code>s. If the
0N/A * user has changed the value displayed by the <code>editor</code> it is
0N/A * possible for the <code>model</code>'s value to differ from that of
0N/A * the <code>editor</code>. To make sure the <code>model</code> has the same
0N/A * value as the editor use the <code>commitEdit</code> method, eg:
0N/A * <pre>
0N/A * try {
0N/A * spinner.commitEdit();
0N/A * }
0N/A * catch (ParseException pe) {{
0N/A * // Edited value is invalid, spinner.getValue() will return
0N/A * // the last valid value, you could revert the spinner to show that:
0N/A * JComponent editor = spinner.getEditor()
0N/A * if (editor instanceof DefaultEditor) {
0N/A * ((DefaultEditor)editor).getTextField().setValue(spinner.getValue();
0N/A * }
0N/A * // reset the value to some known value:
0N/A * spinner.setValue(fallbackValue);
0N/A * // or treat the last valid value as the current, in which
0N/A * // case you don't need to do anything.
0N/A * }
0N/A * return spinner.getValue();
0N/A * </pre>
0N/A * <p>
0N/A * For information and examples of using spinner see
0N/A * <a href="http://java.sun.com/doc/books/tutorial/uiswing/components/spinner.html">How to Use Spinners</a>,
0N/A * a section in <em>The Java Tutorial.</em>
0N/A * <p>
0N/A * <strong>Warning:</strong> Swing is not thread safe. For more
0N/A * information see <a
0N/A * href="package-summary.html#threading">Swing's Threading
0N/A * Policy</a>.
0N/A * <p>
0N/A * <strong>Warning:</strong>
0N/A * Serialized objects of this class will not be compatible with
0N/A * future Swing releases. The current serialization support is
0N/A * appropriate for short term storage or RMI between applications running
0N/A * the same version of Swing. As of 1.4, support for long term storage
0N/A * of all JavaBeans<sup><font size="-2">TM</font></sup>
0N/A * has been added to the <code>java.beans</code> package.
0N/A * Please see {@link java.beans.XMLEncoder}.
0N/A *
0N/A * @beaninfo
0N/A * attribute: isContainer false
0N/A * description: A single line input field that lets the user select a
0N/A * number or an object value from an ordered set.
0N/A *
0N/A * @see SpinnerModel
0N/A * @see AbstractSpinnerModel
0N/A * @see SpinnerListModel
0N/A * @see SpinnerNumberModel
0N/A * @see SpinnerDateModel
0N/A * @see JFormattedTextField
0N/A *
0N/A * @author Hans Muller
0N/A * @author Lynn Monsanto (accessibility)
0N/A * @since 1.4
0N/A */
0N/Apublic class JSpinner extends JComponent implements Accessible
0N/A{
0N/A /**
0N/A * @see #getUIClassID
0N/A * @see #readObject
0N/A */
0N/A private static final String uiClassID = "SpinnerUI";
0N/A
0N/A private static final Action DISABLED_ACTION = new DisabledAction();
0N/A
0N/A private SpinnerModel model;
0N/A private JComponent editor;
0N/A private ChangeListener modelListener;
0N/A private transient ChangeEvent changeEvent;
0N/A private boolean editorExplicitlySet = false;
0N/A
0N/A
0N/A /**
0N/A * Constructs a spinner for the given model. The spinner has
0N/A * a set of previous/next buttons, and an editor appropriate
0N/A * for the model.
0N/A *
0N/A * @throws NullPointerException if the model is {@code null}
0N/A */
0N/A public JSpinner(SpinnerModel model) {
0N/A if (model == null) {
0N/A throw new NullPointerException("model cannot be null");
0N/A }
0N/A this.model = model;
0N/A this.editor = createEditor(model);
1173N/A setUIProperty("opaque",true);
0N/A updateUI();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Constructs a spinner with an <code>Integer SpinnerNumberModel</code>
0N/A * with initial value 0 and no minimum or maximum limits.
0N/A */
0N/A public JSpinner() {
0N/A this(new SpinnerNumberModel());
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the look and feel (L&F) object that renders this component.
0N/A *
0N/A * @return the <code>SpinnerUI</code> object that renders this component
0N/A */
0N/A public SpinnerUI getUI() {
0N/A return (SpinnerUI)ui;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Sets the look and feel (L&F) object that renders this component.
0N/A *
0N/A * @param ui the <code>SpinnerUI</code> L&F object
0N/A * @see UIDefaults#getUI
0N/A */
0N/A public void setUI(SpinnerUI ui) {
0N/A super.setUI(ui);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the suffix used to construct the name of the look and feel
0N/A * (L&F) class used to render this component.
0N/A *
0N/A * @return the string "SpinnerUI"
0N/A * @see JComponent#getUIClassID
0N/A * @see UIDefaults#getUI
0N/A */
0N/A public String getUIClassID() {
0N/A return uiClassID;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Resets the UI property with the value from the current look and feel.
0N/A *
0N/A * @see UIManager#getUI
0N/A */
0N/A public void updateUI() {
0N/A setUI((SpinnerUI)UIManager.getUI(this));
0N/A invalidate();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * This method is called by the constructors to create the
0N/A * <code>JComponent</code>
0N/A * that displays the current value of the sequence. The editor may
0N/A * also allow the user to enter an element of the sequence directly.
0N/A * An editor must listen for <code>ChangeEvents</code> on the
0N/A * <code>model</code> and keep the value it displays
0N/A * in sync with the value of the model.
0N/A * <p>
0N/A * Subclasses may override this method to add support for new
0N/A * <code>SpinnerModel</code> classes. Alternatively one can just
0N/A * replace the editor created here with the <code>setEditor</code>
0N/A * method. The default mapping from model type to editor is:
0N/A * <ul>
0N/A * <li> <code>SpinnerNumberModel =&gt; JSpinner.NumberEditor</code>
0N/A * <li> <code>SpinnerDateModel =&gt; JSpinner.DateEditor</code>
0N/A * <li> <code>SpinnerListModel =&gt; JSpinner.ListEditor</code>
0N/A * <li> <i>all others</i> =&gt; <code>JSpinner.DefaultEditor</code>
0N/A * </ul>
0N/A *
0N/A * @return a component that displays the current value of the sequence
0N/A * @param model the value of getModel
0N/A * @see #getModel
0N/A * @see #setEditor
0N/A */
0N/A protected JComponent createEditor(SpinnerModel model) {
0N/A if (model instanceof SpinnerDateModel) {
0N/A return new DateEditor(this);
0N/A }
0N/A else if (model instanceof SpinnerListModel) {
0N/A return new ListEditor(this);
0N/A }
0N/A else if (model instanceof SpinnerNumberModel) {
0N/A return new NumberEditor(this);
0N/A }
0N/A else {
0N/A return new DefaultEditor(this);
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Changes the model that represents the value of this spinner.
0N/A * If the editor property has not been explicitly set,
0N/A * the editor property is (implicitly) set after the <code>"model"</code>
0N/A * <code>PropertyChangeEvent</code> has been fired. The editor
0N/A * property is set to the value returned by <code>createEditor</code>,
0N/A * as in:
0N/A * <pre>
0N/A * setEditor(createEditor(model));
0N/A * </pre>
0N/A *
0N/A * @param model the new <code>SpinnerModel</code>
0N/A * @see #getModel
0N/A * @see #getEditor
0N/A * @see #setEditor
0N/A * @throws IllegalArgumentException if model is <code>null</code>
0N/A *
0N/A * @beaninfo
0N/A * bound: true
0N/A * attribute: visualUpdate true
0N/A * description: Model that represents the value of this spinner.
0N/A */
0N/A public void setModel(SpinnerModel model) {
0N/A if (model == null) {
0N/A throw new IllegalArgumentException("null model");
0N/A }
0N/A if (!model.equals(this.model)) {
0N/A SpinnerModel oldModel = this.model;
0N/A this.model = model;
0N/A if (modelListener != null) {
0N/A oldModel.removeChangeListener(modelListener);
0N/A this.model.addChangeListener(modelListener);
0N/A }
0N/A firePropertyChange("model", oldModel, model);
0N/A if (!editorExplicitlySet) {
0N/A setEditor(createEditor(model)); // sets editorExplicitlySet true
0N/A editorExplicitlySet = false;
0N/A }
0N/A repaint();
0N/A revalidate();
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the <code>SpinnerModel</code> that defines
0N/A * this spinners sequence of values.
0N/A *
0N/A * @return the value of the model property
0N/A * @see #setModel
0N/A */
0N/A public SpinnerModel getModel() {
0N/A return model;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the current value of the model, typically
0N/A * this value is displayed by the <code>editor</code>. If the
0N/A * user has changed the value displayed by the <code>editor</code> it is
0N/A * possible for the <code>model</code>'s value to differ from that of
0N/A * the <code>editor</code>, refer to the class level javadoc for examples
0N/A * of how to deal with this.
0N/A * <p>
0N/A * This method simply delegates to the <code>model</code>.
0N/A * It is equivalent to:
0N/A * <pre>
0N/A * getModel().getValue()
0N/A * </pre>
0N/A *
0N/A * @see #setValue
0N/A * @see SpinnerModel#getValue
0N/A */
0N/A public Object getValue() {
0N/A return getModel().getValue();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Changes current value of the model, typically
0N/A * this value is displayed by the <code>editor</code>.
0N/A * If the <code>SpinnerModel</code> implementation
0N/A * doesn't support the specified value then an
0N/A * <code>IllegalArgumentException</code> is thrown.
0N/A * <p>
0N/A * This method simply delegates to the <code>model</code>.
0N/A * It is equivalent to:
0N/A * <pre>
0N/A * getModel().setValue(value)
0N/A * </pre>
0N/A *
0N/A * @throws IllegalArgumentException if <code>value</code> isn't allowed
0N/A * @see #getValue
0N/A * @see SpinnerModel#setValue
0N/A */
0N/A public void setValue(Object value) {
0N/A getModel().setValue(value);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the object in the sequence that comes after the object returned
0N/A * by <code>getValue()</code>. If the end of the sequence has been reached
0N/A * then return <code>null</code>.
0N/A * Calling this method does not effect <code>value</code>.
0N/A * <p>
0N/A * This method simply delegates to the <code>model</code>.
0N/A * It is equivalent to:
0N/A * <pre>
0N/A * getModel().getNextValue()
0N/A * </pre>
0N/A *
0N/A * @return the next legal value or <code>null</code> if one doesn't exist
0N/A * @see #getValue
0N/A * @see #getPreviousValue
0N/A * @see SpinnerModel#getNextValue
0N/A */
0N/A public Object getNextValue() {
0N/A return getModel().getNextValue();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * We pass <code>Change</code> events along to the listeners with the
0N/A * the slider (instead of the model itself) as the event source.
0N/A */
0N/A private class ModelListener implements ChangeListener, Serializable {
0N/A public void stateChanged(ChangeEvent e) {
0N/A fireStateChanged();
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Adds a listener to the list that is notified each time a change
0N/A * to the model occurs. The source of <code>ChangeEvents</code>
0N/A * delivered to <code>ChangeListeners</code> will be this
0N/A * <code>JSpinner</code>. Note also that replacing the model
0N/A * will not affect listeners added directly to JSpinner.
0N/A * Applications can add listeners to the model directly. In that
0N/A * case is that the source of the event would be the
0N/A * <code>SpinnerModel</code>.
0N/A *
0N/A * @param listener the <code>ChangeListener</code> to add
0N/A * @see #removeChangeListener
0N/A * @see #getModel
0N/A */
0N/A public void addChangeListener(ChangeListener listener) {
0N/A if (modelListener == null) {
0N/A modelListener = new ModelListener();
0N/A getModel().addChangeListener(modelListener);
0N/A }
0N/A listenerList.add(ChangeListener.class, listener);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Removes a <code>ChangeListener</code> from this spinner.
0N/A *
0N/A * @param listener the <code>ChangeListener</code> to remove
0N/A * @see #fireStateChanged
0N/A * @see #addChangeListener
0N/A */
0N/A public void removeChangeListener(ChangeListener listener) {
0N/A listenerList.remove(ChangeListener.class, listener);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns an array of all the <code>ChangeListener</code>s added
0N/A * to this JSpinner with addChangeListener().
0N/A *
0N/A * @return all of the <code>ChangeListener</code>s added or an empty
0N/A * array if no listeners have been added
0N/A * @since 1.4
0N/A */
0N/A public ChangeListener[] getChangeListeners() {
625N/A return listenerList.getListeners(ChangeListener.class);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Sends a <code>ChangeEvent</code>, whose source is this
0N/A * <code>JSpinner</code>, to each <code>ChangeListener</code>.
0N/A * When a <code>ChangeListener</code> has been added
0N/A * to the spinner, this method method is called each time
0N/A * a <code>ChangeEvent</code> is received from the model.
0N/A *
0N/A * @see #addChangeListener
0N/A * @see #removeChangeListener
0N/A * @see EventListenerList
0N/A */
0N/A protected void fireStateChanged() {
0N/A Object[] listeners = listenerList.getListenerList();
0N/A for (int i = listeners.length - 2; i >= 0; i -= 2) {
0N/A if (listeners[i] == ChangeListener.class) {
0N/A if (changeEvent == null) {
0N/A changeEvent = new ChangeEvent(this);
0N/A }
0N/A ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the object in the sequence that comes
0N/A * before the object returned by <code>getValue()</code>.
0N/A * If the end of the sequence has been reached then
0N/A * return <code>null</code>. Calling this method does
0N/A * not effect <code>value</code>.
0N/A * <p>
0N/A * This method simply delegates to the <code>model</code>.
0N/A * It is equivalent to:
0N/A * <pre>
0N/A * getModel().getPreviousValue()
0N/A * </pre>
0N/A *
0N/A * @return the previous legal value or <code>null</code>
0N/A * if one doesn't exist
0N/A * @see #getValue
0N/A * @see #getNextValue
0N/A * @see SpinnerModel#getPreviousValue
0N/A */
0N/A public Object getPreviousValue() {
0N/A return getModel().getPreviousValue();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Changes the <code>JComponent</code> that displays the current value
0N/A * of the <code>SpinnerModel</code>. It is the responsibility of this
0N/A * method to <i>disconnect</i> the old editor from the model and to
0N/A * connect the new editor. This may mean removing the
0N/A * old editors <code>ChangeListener</code> from the model or the
0N/A * spinner itself and adding one for the new editor.
0N/A *
0N/A * @param editor the new editor
0N/A * @see #getEditor
0N/A * @see #createEditor
0N/A * @see #getModel
0N/A * @throws IllegalArgumentException if editor is <code>null</code>
0N/A *
0N/A * @beaninfo
0N/A * bound: true
0N/A * attribute: visualUpdate true
0N/A * description: JComponent that displays the current value of the model
0N/A */
0N/A public void setEditor(JComponent editor) {
0N/A if (editor == null) {
0N/A throw new IllegalArgumentException("null editor");
0N/A }
0N/A if (!editor.equals(this.editor)) {
0N/A JComponent oldEditor = this.editor;
0N/A this.editor = editor;
0N/A if (oldEditor instanceof DefaultEditor) {
0N/A ((DefaultEditor)oldEditor).dismiss(this);
0N/A }
0N/A editorExplicitlySet = true;
0N/A firePropertyChange("editor", oldEditor, editor);
0N/A revalidate();
0N/A repaint();
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the component that displays and potentially
0N/A * changes the model's value.
0N/A *
0N/A * @return the component that displays and potentially
0N/A * changes the model's value
0N/A * @see #setEditor
0N/A * @see #createEditor
0N/A */
0N/A public JComponent getEditor() {
0N/A return editor;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Commits the currently edited value to the <code>SpinnerModel</code>.
0N/A * <p>
0N/A * If the editor is an instance of <code>DefaultEditor</code>, the
0N/A * call if forwarded to the editor, otherwise this does nothing.
0N/A *
0N/A * @throws ParseException if the currently edited value couldn't
0N/A * be commited.
0N/A */
0N/A public void commitEdit() throws ParseException {
0N/A JComponent editor = getEditor();
0N/A if (editor instanceof DefaultEditor) {
0N/A ((DefaultEditor)editor).commitEdit();
0N/A }
0N/A }
0N/A
0N/A
0N/A /*
0N/A * See readObject and writeObject in JComponent for more
0N/A * information about serialization in Swing.
0N/A *
0N/A * @param s Stream to write to
0N/A */
0N/A private void writeObject(ObjectOutputStream s) throws IOException {
0N/A s.defaultWriteObject();
0N/A if (getUIClassID().equals(uiClassID)) {
0N/A byte count = JComponent.getWriteObjCounter(this);
0N/A JComponent.setWriteObjCounter(this, --count);
0N/A if (count == 0 && ui != null) {
0N/A ui.installUI(this);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * A simple base class for more specialized editors
0N/A * that displays a read-only view of the model's current
0N/A * value with a <code>JFormattedTextField</code>. Subclasses
0N/A * can configure the <code>JFormattedTextField</code> to create
0N/A * an editor that's appropriate for the type of model they
0N/A * support and they may want to override
0N/A * the <code>stateChanged</code> and <code>propertyChanged</code>
0N/A * methods, which keep the model and the text field in sync.
0N/A * <p>
0N/A * This class defines a <code>dismiss</code> method that removes the
0N/A * editors <code>ChangeListener</code> from the <code>JSpinner</code>
0N/A * that it's part of. The <code>setEditor</code> method knows about
0N/A * <code>DefaultEditor.dismiss</code>, so if the developer
0N/A * replaces an editor that's derived from <code>JSpinner.DefaultEditor</code>
0N/A * its <code>ChangeListener</code> connection back to the
0N/A * <code>JSpinner</code> will be removed. However after that,
0N/A * it's up to the developer to manage their editor listeners.
0N/A * Similarly, if a subclass overrides <code>createEditor</code>,
0N/A * it's up to the subclasser to deal with their editor
0N/A * subsequently being replaced (with <code>setEditor</code>).
0N/A * We expect that in most cases, and in editor installed
0N/A * with <code>setEditor</code> or created by a <code>createEditor</code>
0N/A * override, will not be replaced anyway.
0N/A * <p>
0N/A * This class is the <code>LayoutManager</code> for it's single
0N/A * <code>JFormattedTextField</code> child. By default the
0N/A * child is just centered with the parents insets.
0N/A * @since 1.4
0N/A */
0N/A public static class DefaultEditor extends JPanel
0N/A implements ChangeListener, PropertyChangeListener, LayoutManager
0N/A {
0N/A /**
0N/A * Constructs an editor component for the specified <code>JSpinner</code>.
0N/A * This <code>DefaultEditor</code> is it's own layout manager and
0N/A * it is added to the spinner's <code>ChangeListener</code> list.
0N/A * The constructor creates a single <code>JFormattedTextField</code> child,
0N/A * initializes it's value to be the spinner model's current value
0N/A * and adds it to <code>this</code> <code>DefaultEditor</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @see #getTextField
0N/A * @see JSpinner#addChangeListener
0N/A */
0N/A public DefaultEditor(JSpinner spinner) {
0N/A super(null);
0N/A
0N/A JFormattedTextField ftf = new JFormattedTextField();
0N/A ftf.setName("Spinner.formattedTextField");
0N/A ftf.setValue(spinner.getValue());
0N/A ftf.addPropertyChangeListener(this);
0N/A ftf.setEditable(false);
0N/A ftf.setInheritsPopupMenu(true);
0N/A
0N/A String toolTipText = spinner.getToolTipText();
0N/A if (toolTipText != null) {
0N/A ftf.setToolTipText(toolTipText);
0N/A }
0N/A
0N/A add(ftf);
0N/A
0N/A setLayout(this);
0N/A spinner.addChangeListener(this);
0N/A
0N/A // We want the spinner's increment/decrement actions to be
0N/A // active vs those of the JFormattedTextField. As such we
0N/A // put disabled actions in the JFormattedTextField's actionmap.
0N/A // A binding to a disabled action is treated as a nonexistant
0N/A // binding.
0N/A ActionMap ftfMap = ftf.getActionMap();
0N/A
0N/A if (ftfMap != null) {
0N/A ftfMap.put("increment", DISABLED_ACTION);
0N/A ftfMap.put("decrement", DISABLED_ACTION);
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Disconnect <code>this</code> editor from the specified
0N/A * <code>JSpinner</code>. By default, this method removes
0N/A * itself from the spinners <code>ChangeListener</code> list.
0N/A *
0N/A * @param spinner the <code>JSpinner</code> to disconnect this
0N/A * editor from; the same spinner as was passed to the constructor.
0N/A */
0N/A public void dismiss(JSpinner spinner) {
0N/A spinner.removeChangeListener(this);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the <code>JSpinner</code> ancestor of this editor or
0N/A * <code>null</code> if none of the ancestors are a
0N/A * <code>JSpinner</code>.
0N/A * Typically the editor's parent is a <code>JSpinner</code> however
0N/A * subclasses of <code>JSpinner</code> may override the
0N/A * the <code>createEditor</code> method and insert one or more containers
0N/A * between the <code>JSpinner</code> and it's editor.
0N/A *
0N/A * @return <code>JSpinner</code> ancestor; <code>null</code>
0N/A * if none of the ancestors are a <code>JSpinner</code>
0N/A *
0N/A * @see JSpinner#createEditor
0N/A */
0N/A public JSpinner getSpinner() {
0N/A for (Component c = this; c != null; c = c.getParent()) {
0N/A if (c instanceof JSpinner) {
0N/A return (JSpinner)c;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the <code>JFormattedTextField</code> child of this
0N/A * editor. By default the text field is the first and only
0N/A * child of editor.
0N/A *
0N/A * @return the <code>JFormattedTextField</code> that gives the user
0N/A * access to the <code>SpinnerDateModel's</code> value.
0N/A * @see #getSpinner
0N/A * @see #getModel
0N/A */
0N/A public JFormattedTextField getTextField() {
0N/A return (JFormattedTextField)getComponent(0);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * This method is called when the spinner's model's state changes.
0N/A * It sets the <code>value</code> of the text field to the current
0N/A * value of the spinners model.
0N/A *
0N/A * @param e the <code>ChangeEvent</code> whose source is the
0N/A * <code>JSpinner</code> whose model has changed.
0N/A * @see #getTextField
0N/A * @see JSpinner#getValue
0N/A */
0N/A public void stateChanged(ChangeEvent e) {
0N/A JSpinner spinner = (JSpinner)(e.getSource());
0N/A getTextField().setValue(spinner.getValue());
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Called by the <code>JFormattedTextField</code>
0N/A * <code>PropertyChangeListener</code>. When the <code>"value"</code>
0N/A * property changes, which implies that the user has typed a new
0N/A * number, we set the value of the spinners model.
0N/A * <p>
0N/A * This class ignores <code>PropertyChangeEvents</code> whose
0N/A * source is not the <code>JFormattedTextField</code>, so subclasses
0N/A * may safely make <code>this</code> <code>DefaultEditor</code> a
0N/A * <code>PropertyChangeListener</code> on other objects.
0N/A *
0N/A * @param e the <code>PropertyChangeEvent</code> whose source is
0N/A * the <code>JFormattedTextField</code> created by this class.
0N/A * @see #getTextField
0N/A */
0N/A public void propertyChange(PropertyChangeEvent e)
0N/A {
0N/A JSpinner spinner = getSpinner();
0N/A
0N/A if (spinner == null) {
0N/A // Indicates we aren't installed anywhere.
0N/A return;
0N/A }
0N/A
0N/A Object source = e.getSource();
0N/A String name = e.getPropertyName();
0N/A if ((source instanceof JFormattedTextField) && "value".equals(name)) {
0N/A Object lastValue = spinner.getValue();
0N/A
0N/A // Try to set the new value
0N/A try {
0N/A spinner.setValue(getTextField().getValue());
0N/A } catch (IllegalArgumentException iae) {
0N/A // SpinnerModel didn't like new value, reset
0N/A try {
0N/A ((JFormattedTextField)source).setValue(lastValue);
0N/A } catch (IllegalArgumentException iae2) {
0N/A // Still bogus, nothing else we can do, the
0N/A // SpinnerModel and JFormattedTextField are now out
0N/A // of sync.
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * This <code>LayoutManager</code> method does nothing. We're
0N/A * only managing a single child and there's no support
0N/A * for layout constraints.
0N/A *
0N/A * @param name ignored
0N/A * @param child ignored
0N/A */
0N/A public void addLayoutComponent(String name, Component child) {
0N/A }
0N/A
0N/A
0N/A /**
0N/A * This <code>LayoutManager</code> method does nothing. There
0N/A * isn't any per-child state.
0N/A *
0N/A * @param child ignored
0N/A */
0N/A public void removeLayoutComponent(Component child) {
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the size of the parents insets.
0N/A */
0N/A private Dimension insetSize(Container parent) {
0N/A Insets insets = parent.getInsets();
0N/A int w = insets.left + insets.right;
0N/A int h = insets.top + insets.bottom;
0N/A return new Dimension(w, h);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the preferred size of first (and only) child plus the
0N/A * size of the parents insets.
0N/A *
0N/A * @param parent the Container that's managing the layout
0N/A * @return the preferred dimensions to lay out the subcomponents
0N/A * of the specified container.
0N/A */
0N/A public Dimension preferredLayoutSize(Container parent) {
0N/A Dimension preferredSize = insetSize(parent);
0N/A if (parent.getComponentCount() > 0) {
0N/A Dimension childSize = getComponent(0).getPreferredSize();
0N/A preferredSize.width += childSize.width;
0N/A preferredSize.height += childSize.height;
0N/A }
0N/A return preferredSize;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the minimum size of first (and only) child plus the
0N/A * size of the parents insets.
0N/A *
0N/A * @param parent the Container that's managing the layout
0N/A * @return the minimum dimensions needed to lay out the subcomponents
0N/A * of the specified container.
0N/A */
0N/A public Dimension minimumLayoutSize(Container parent) {
0N/A Dimension minimumSize = insetSize(parent);
0N/A if (parent.getComponentCount() > 0) {
0N/A Dimension childSize = getComponent(0).getMinimumSize();
0N/A minimumSize.width += childSize.width;
0N/A minimumSize.height += childSize.height;
0N/A }
0N/A return minimumSize;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Resize the one (and only) child to completely fill the area
0N/A * within the parents insets.
0N/A */
0N/A public void layoutContainer(Container parent) {
0N/A if (parent.getComponentCount() > 0) {
0N/A Insets insets = parent.getInsets();
0N/A int w = parent.getWidth() - (insets.left + insets.right);
0N/A int h = parent.getHeight() - (insets.top + insets.bottom);
0N/A getComponent(0).setBounds(insets.left, insets.top, w, h);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Pushes the currently edited value to the <code>SpinnerModel</code>.
0N/A * <p>
0N/A * The default implementation invokes <code>commitEdit</code> on the
0N/A * <code>JFormattedTextField</code>.
0N/A *
0N/A * @throws ParseException if the edited value is not legal
0N/A */
0N/A public void commitEdit() throws ParseException {
0N/A // If the value in the JFormattedTextField is legal, this will have
0N/A // the result of pushing the value to the SpinnerModel
0N/A // by way of the <code>propertyChange</code> method.
0N/A JFormattedTextField ftf = getTextField();
0N/A
0N/A ftf.commitEdit();
0N/A }
0N/A
0N/A /**
0N/A * Returns the baseline.
0N/A *
0N/A * @throws IllegalArgumentException {@inheritDoc}
0N/A * @see javax.swing.JComponent#getBaseline(int,int)
0N/A * @see javax.swing.JComponent#getBaselineResizeBehavior()
0N/A * @since 1.6
0N/A */
0N/A public int getBaseline(int width, int height) {
0N/A // check size.
0N/A super.getBaseline(width, height);
0N/A Insets insets = getInsets();
0N/A width = width - insets.left - insets.right;
0N/A height = height - insets.top - insets.bottom;
0N/A int baseline = getComponent(0).getBaseline(width, height);
0N/A if (baseline >= 0) {
0N/A return baseline + insets.top;
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Returns an enum indicating how the baseline of the component
0N/A * changes as the size changes.
0N/A *
0N/A * @throws NullPointerException {@inheritDoc}
0N/A * @see javax.swing.JComponent#getBaseline(int, int)
0N/A * @since 1.6
0N/A */
0N/A public BaselineResizeBehavior getBaselineResizeBehavior() {
0N/A return getComponent(0).getBaselineResizeBehavior();
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A
0N/A /**
0N/A * This subclass of javax.swing.DateFormatter maps the minimum/maximum
0N/A * properties to te start/end properties of a SpinnerDateModel.
0N/A */
0N/A private static class DateEditorFormatter extends DateFormatter {
0N/A private final SpinnerDateModel model;
0N/A
0N/A DateEditorFormatter(SpinnerDateModel model, DateFormat format) {
0N/A super(format);
0N/A this.model = model;
0N/A }
0N/A
0N/A public void setMinimum(Comparable min) {
0N/A model.setStart(min);
0N/A }
0N/A
0N/A public Comparable getMinimum() {
0N/A return model.getStart();
0N/A }
0N/A
0N/A public void setMaximum(Comparable max) {
0N/A model.setEnd(max);
0N/A }
0N/A
0N/A public Comparable getMaximum() {
0N/A return model.getEnd();
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * An editor for a <code>JSpinner</code> whose model is a
0N/A * <code>SpinnerDateModel</code>. The value of the editor is
0N/A * displayed with a <code>JFormattedTextField</code> whose format
0N/A * is defined by a <code>DateFormatter</code> instance whose
0N/A * <code>minimum</code> and <code>maximum</code> properties
0N/A * are mapped to the <code>SpinnerDateModel</code>.
0N/A * @since 1.4
0N/A */
0N/A // PENDING(hmuller): more example javadoc
0N/A public static class DateEditor extends DefaultEditor
0N/A {
0N/A // This is here until SimpleDateFormat gets a constructor that
0N/A // takes a Locale: 4923525
0N/A private static String getDefaultPattern(Locale loc) {
0N/A ResourceBundle r = LocaleData.getDateFormatData(loc);
0N/A String[] dateTimePatterns = r.getStringArray("DateTimePatterns");
0N/A Object[] dateTimeArgs = {dateTimePatterns[DateFormat.SHORT],
0N/A dateTimePatterns[DateFormat.SHORT + 4]};
0N/A return MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
0N/A }
0N/A
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerDateModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinners model and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerDateModel</code>
0N/A *
0N/A * @see #getModel
0N/A * @see #getFormat
0N/A * @see SpinnerDateModel
0N/A */
0N/A public DateEditor(JSpinner spinner) {
0N/A this(spinner, getDefaultPattern(spinner.getLocale()));
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerDateModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinner and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @param dateFormatPattern the initial pattern for the
0N/A * <code>SimpleDateFormat</code> object that's used to display
0N/A * and parse the value of the text field.
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerDateModel</code>
0N/A *
0N/A * @see #getModel
0N/A * @see #getFormat
0N/A * @see SpinnerDateModel
0N/A * @see java.text.SimpleDateFormat
0N/A */
0N/A public DateEditor(JSpinner spinner, String dateFormatPattern) {
0N/A this(spinner, new SimpleDateFormat(dateFormatPattern,
0N/A spinner.getLocale()));
0N/A }
0N/A
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerDateModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinner and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor
0N/A * will monitor
0N/A * @param format <code>DateFormat</code> object that's used to display
0N/A * and parse the value of the text field.
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerDateModel</code>
0N/A *
0N/A * @see #getModel
0N/A * @see #getFormat
0N/A * @see SpinnerDateModel
0N/A * @see java.text.SimpleDateFormat
0N/A */
0N/A private DateEditor(JSpinner spinner, DateFormat format) {
0N/A super(spinner);
0N/A if (!(spinner.getModel() instanceof SpinnerDateModel)) {
0N/A throw new IllegalArgumentException(
0N/A "model not a SpinnerDateModel");
0N/A }
0N/A
0N/A SpinnerDateModel model = (SpinnerDateModel)spinner.getModel();
0N/A DateFormatter formatter = new DateEditorFormatter(model, format);
0N/A DefaultFormatterFactory factory = new DefaultFormatterFactory(
0N/A formatter);
0N/A JFormattedTextField ftf = getTextField();
0N/A ftf.setEditable(true);
0N/A ftf.setFormatterFactory(factory);
0N/A
0N/A /* TBD - initializing the column width of the text field
0N/A * is imprecise and doing it here is tricky because
0N/A * the developer may configure the formatter later.
0N/A */
0N/A try {
0N/A String maxString = formatter.valueToString(model.getStart());
0N/A String minString = formatter.valueToString(model.getEnd());
0N/A ftf.setColumns(Math.max(maxString.length(),
0N/A minString.length()));
0N/A }
0N/A catch (ParseException e) {
0N/A // PENDING: hmuller
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the <code>java.text.SimpleDateFormat</code> object the
0N/A * <code>JFormattedTextField</code> uses to parse and format
0N/A * numbers.
0N/A *
0N/A * @return the value of <code>getTextField().getFormatter().getFormat()</code>.
0N/A * @see #getTextField
0N/A * @see java.text.SimpleDateFormat
0N/A */
0N/A public SimpleDateFormat getFormat() {
0N/A return (SimpleDateFormat)((DateFormatter)(getTextField().getFormatter())).getFormat();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Return our spinner ancestor's <code>SpinnerDateModel</code>.
0N/A *
0N/A * @return <code>getSpinner().getModel()</code>
0N/A * @see #getSpinner
0N/A * @see #getTextField
0N/A */
0N/A public SpinnerDateModel getModel() {
0N/A return (SpinnerDateModel)(getSpinner().getModel());
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * This subclass of javax.swing.NumberFormatter maps the minimum/maximum
0N/A * properties to a SpinnerNumberModel and initializes the valueClass
0N/A * of the NumberFormatter to match the type of the initial models value.
0N/A */
0N/A private static class NumberEditorFormatter extends NumberFormatter {
0N/A private final SpinnerNumberModel model;
0N/A
0N/A NumberEditorFormatter(SpinnerNumberModel model, NumberFormat format) {
0N/A super(format);
0N/A this.model = model;
0N/A setValueClass(model.getValue().getClass());
0N/A }
0N/A
0N/A public void setMinimum(Comparable min) {
0N/A model.setMinimum(min);
0N/A }
0N/A
0N/A public Comparable getMinimum() {
0N/A return model.getMinimum();
0N/A }
0N/A
0N/A public void setMaximum(Comparable max) {
0N/A model.setMaximum(max);
0N/A }
0N/A
0N/A public Comparable getMaximum() {
0N/A return model.getMaximum();
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * An editor for a <code>JSpinner</code> whose model is a
0N/A * <code>SpinnerNumberModel</code>. The value of the editor is
0N/A * displayed with a <code>JFormattedTextField</code> whose format
0N/A * is defined by a <code>NumberFormatter</code> instance whose
0N/A * <code>minimum</code> and <code>maximum</code> properties
0N/A * are mapped to the <code>SpinnerNumberModel</code>.
0N/A * @since 1.4
0N/A */
0N/A // PENDING(hmuller): more example javadoc
0N/A public static class NumberEditor extends DefaultEditor
0N/A {
0N/A // This is here until DecimalFormat gets a constructor that
0N/A // takes a Locale: 4923525
0N/A private static String getDefaultPattern(Locale locale) {
0N/A // Get the pattern for the default locale.
0N/A ResourceBundle rb = LocaleData.getNumberFormatData(locale);
0N/A String[] all = rb.getStringArray("NumberPatterns");
0N/A return all[0];
0N/A }
0N/A
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerNumberModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinner and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerNumberModel</code>
0N/A *
0N/A * @see #getModel
0N/A * @see #getFormat
0N/A * @see SpinnerNumberModel
0N/A */
0N/A public NumberEditor(JSpinner spinner) {
0N/A this(spinner, getDefaultPattern(spinner.getLocale()));
0N/A }
0N/A
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerNumberModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinner and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @param decimalFormatPattern the initial pattern for the
0N/A * <code>DecimalFormat</code> object that's used to display
0N/A * and parse the value of the text field.
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerNumberModel</code> or if
0N/A * <code>decimalFormatPattern</code> is not a legal
0N/A * argument to <code>DecimalFormat</code>
0N/A *
0N/A * @see #getTextField
0N/A * @see SpinnerNumberModel
0N/A * @see java.text.DecimalFormat
0N/A */
0N/A public NumberEditor(JSpinner spinner, String decimalFormatPattern) {
0N/A this(spinner, new DecimalFormat(decimalFormatPattern));
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerNumberModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinner and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @param decimalFormatPattern the initial pattern for the
0N/A * <code>DecimalFormat</code> object that's used to display
0N/A * and parse the value of the text field.
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerNumberModel</code>
0N/A *
0N/A * @see #getTextField
0N/A * @see SpinnerNumberModel
0N/A * @see java.text.DecimalFormat
0N/A */
0N/A private NumberEditor(JSpinner spinner, DecimalFormat format) {
0N/A super(spinner);
0N/A if (!(spinner.getModel() instanceof SpinnerNumberModel)) {
0N/A throw new IllegalArgumentException(
0N/A "model not a SpinnerNumberModel");
0N/A }
0N/A
0N/A SpinnerNumberModel model = (SpinnerNumberModel)spinner.getModel();
0N/A NumberFormatter formatter = new NumberEditorFormatter(model,
0N/A format);
0N/A DefaultFormatterFactory factory = new DefaultFormatterFactory(
0N/A formatter);
0N/A JFormattedTextField ftf = getTextField();
0N/A ftf.setEditable(true);
0N/A ftf.setFormatterFactory(factory);
0N/A ftf.setHorizontalAlignment(JTextField.RIGHT);
0N/A
0N/A /* TBD - initializing the column width of the text field
0N/A * is imprecise and doing it here is tricky because
0N/A * the developer may configure the formatter later.
0N/A */
0N/A try {
0N/A String maxString = formatter.valueToString(model.getMinimum());
0N/A String minString = formatter.valueToString(model.getMaximum());
0N/A ftf.setColumns(Math.max(maxString.length(),
0N/A minString.length()));
0N/A }
0N/A catch (ParseException e) {
0N/A // TBD should throw a chained error here
0N/A }
0N/A
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the <code>java.text.DecimalFormat</code> object the
0N/A * <code>JFormattedTextField</code> uses to parse and format
0N/A * numbers.
0N/A *
0N/A * @return the value of <code>getTextField().getFormatter().getFormat()</code>.
0N/A * @see #getTextField
0N/A * @see java.text.DecimalFormat
0N/A */
0N/A public DecimalFormat getFormat() {
0N/A return (DecimalFormat)((NumberFormatter)(getTextField().getFormatter())).getFormat();
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Return our spinner ancestor's <code>SpinnerNumberModel</code>.
0N/A *
0N/A * @return <code>getSpinner().getModel()</code>
0N/A * @see #getSpinner
0N/A * @see #getTextField
0N/A */
0N/A public SpinnerNumberModel getModel() {
0N/A return (SpinnerNumberModel)(getSpinner().getModel());
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * An editor for a <code>JSpinner</code> whose model is a
0N/A * <code>SpinnerListModel</code>.
0N/A * @since 1.4
0N/A */
0N/A public static class ListEditor extends DefaultEditor
0N/A {
0N/A /**
0N/A * Construct a <code>JSpinner</code> editor that supports displaying
0N/A * and editing the value of a <code>SpinnerListModel</code>
0N/A * with a <code>JFormattedTextField</code>. <code>This</code>
0N/A * <code>ListEditor</code> becomes both a <code>ChangeListener</code>
0N/A * on the spinner and a <code>PropertyChangeListener</code>
0N/A * on the new <code>JFormattedTextField</code>.
0N/A *
0N/A * @param spinner the spinner whose model <code>this</code> editor will monitor
0N/A * @exception IllegalArgumentException if the spinners model is not
0N/A * an instance of <code>SpinnerListModel</code>
0N/A *
0N/A * @see #getModel
0N/A * @see SpinnerListModel
0N/A */
0N/A public ListEditor(JSpinner spinner) {
0N/A super(spinner);
0N/A if (!(spinner.getModel() instanceof SpinnerListModel)) {
0N/A throw new IllegalArgumentException("model not a SpinnerListModel");
0N/A }
0N/A getTextField().setEditable(true);
0N/A getTextField().setFormatterFactory(new
0N/A DefaultFormatterFactory(new ListFormatter()));
0N/A }
0N/A
0N/A /**
0N/A * Return our spinner ancestor's <code>SpinnerNumberModel</code>.
0N/A *
0N/A * @return <code>getSpinner().getModel()</code>
0N/A * @see #getSpinner
0N/A * @see #getTextField
0N/A */
0N/A public SpinnerListModel getModel() {
0N/A return (SpinnerListModel)(getSpinner().getModel());
0N/A }
0N/A
0N/A
0N/A /**
0N/A * ListFormatter provides completion while text is being input
0N/A * into the JFormattedTextField. Completion is only done if the
0N/A * user is inserting text at the end of the document. Completion
0N/A * is done by way of the SpinnerListModel method findNextMatch.
0N/A */
0N/A private class ListFormatter extends
0N/A JFormattedTextField.AbstractFormatter {
0N/A private DocumentFilter filter;
0N/A
0N/A public String valueToString(Object value) throws ParseException {
0N/A if (value == null) {
0N/A return "";
0N/A }
0N/A return value.toString();
0N/A }
0N/A
0N/A public Object stringToValue(String string) throws ParseException {
0N/A return string;
0N/A }
0N/A
0N/A protected DocumentFilter getDocumentFilter() {
0N/A if (filter == null) {
0N/A filter = new Filter();
0N/A }
0N/A return filter;
0N/A }
0N/A
0N/A
0N/A private class Filter extends DocumentFilter {
0N/A public void replace(FilterBypass fb, int offset, int length,
0N/A String string, AttributeSet attrs) throws
0N/A BadLocationException {
0N/A if (string != null && (offset + length) ==
0N/A fb.getDocument().getLength()) {
0N/A Object next = getModel().findNextMatch(
0N/A fb.getDocument().getText(0, offset) +
0N/A string);
0N/A String value = (next != null) ? next.toString() : null;
0N/A
0N/A if (value != null) {
0N/A fb.remove(0, offset + length);
0N/A fb.insertString(0, value, null);
0N/A getFormattedTextField().select(offset +
0N/A string.length(),
0N/A value.length());
0N/A return;
0N/A }
0N/A }
0N/A super.replace(fb, offset, length, string, attrs);
0N/A }
0N/A
0N/A public void insertString(FilterBypass fb, int offset,
0N/A String string, AttributeSet attr)
0N/A throws BadLocationException {
0N/A replace(fb, offset, 0, string, attr);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * An Action implementation that is always disabled.
0N/A */
0N/A private static class DisabledAction implements Action {
0N/A public Object getValue(String key) {
0N/A return null;
0N/A }
0N/A public void putValue(String key, Object value) {
0N/A }
0N/A public void setEnabled(boolean b) {
0N/A }
0N/A public boolean isEnabled() {
0N/A return false;
0N/A }
0N/A public void addPropertyChangeListener(PropertyChangeListener l) {
0N/A }
0N/A public void removePropertyChangeListener(PropertyChangeListener l) {
0N/A }
0N/A public void actionPerformed(ActionEvent ae) {
0N/A }
0N/A }
0N/A
0N/A /////////////////
0N/A // Accessibility support
0N/A ////////////////
0N/A
0N/A /**
0N/A * Gets the <code>AccessibleContext</code> for the <code>JSpinner</code>
0N/A *
0N/A * @return the <code>AccessibleContext</code> for the <code>JSpinner</code>
0N/A * @since 1.5
0N/A */
0N/A public AccessibleContext getAccessibleContext() {
0N/A if (accessibleContext == null) {
0N/A accessibleContext = new AccessibleJSpinner();
0N/A }
0N/A return accessibleContext;
0N/A }
0N/A
0N/A /**
0N/A * <code>AccessibleJSpinner</code> implements accessibility
0N/A * support for the <code>JSpinner</code> class.
0N/A * @since 1.5
0N/A */
0N/A protected class AccessibleJSpinner extends AccessibleJComponent
0N/A implements AccessibleValue, AccessibleAction, AccessibleText,
0N/A AccessibleEditableText, ChangeListener {
0N/A
0N/A private Object oldModelValue = null;
0N/A
0N/A /**
0N/A * AccessibleJSpinner constructor
0N/A */
0N/A protected AccessibleJSpinner() {
0N/A // model is guaranteed to be non-null
0N/A oldModelValue = model.getValue();
0N/A JSpinner.this.addChangeListener(this);
0N/A }
0N/A
0N/A /**
0N/A * Invoked when the target of the listener has changed its state.
0N/A *
0N/A * @param e a <code>ChangeEvent</code> object. Must not be null.
0N/A * @throws NullPointerException if the parameter is null.
0N/A */
0N/A public void stateChanged(ChangeEvent e) {
0N/A if (e == null) {
0N/A throw new NullPointerException();
0N/A }
0N/A Object newModelValue = model.getValue();
0N/A firePropertyChange(ACCESSIBLE_VALUE_PROPERTY,
0N/A oldModelValue,
0N/A newModelValue);
0N/A firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
0N/A null,
0N/A 0); // entire text may have changed
0N/A
0N/A oldModelValue = newModelValue;
0N/A }
0N/A
0N/A /* ===== Begin AccessibleContext methods ===== */
0N/A
0N/A /**
0N/A * Gets the role of this object. The role of the object is the generic
0N/A * purpose or use of the class of this object. For example, the role
0N/A * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
0N/A * AccessibleRole are provided so component developers can pick from
0N/A * a set of predefined roles. This enables assistive technologies to
0N/A * provide a consistent interface to various tweaked subclasses of
0N/A * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
0N/A * that act like a push button) as well as distinguish between sublasses
0N/A * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
0N/A * and AccessibleRole.RADIO_BUTTON for radio buttons).
0N/A * <p>Note that the AccessibleRole class is also extensible, so
0N/A * custom component developers can define their own AccessibleRole's
0N/A * if the set of predefined roles is inadequate.
0N/A *
0N/A * @return an instance of AccessibleRole describing the role of the object
0N/A * @see AccessibleRole
0N/A */
0N/A public AccessibleRole getAccessibleRole() {
0N/A return AccessibleRole.SPIN_BOX;
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of accessible children of the object.
0N/A *
0N/A * @return the number of accessible children of the object.
0N/A */
0N/A public int getAccessibleChildrenCount() {
0N/A // the JSpinner has one child, the editor
0N/A if (editor.getAccessibleContext() != null) {
0N/A return 1;
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * Returns the specified Accessible child of the object. The Accessible
0N/A * children of an Accessible object are zero-based, so the first child
0N/A * of an Accessible child is at index 0, the second child is at index 1,
0N/A * and so on.
0N/A *
0N/A * @param i zero-based index of child
0N/A * @return the Accessible child of the object
0N/A * @see #getAccessibleChildrenCount
0N/A */
0N/A public Accessible getAccessibleChild(int i) {
0N/A // the JSpinner has one child, the editor
0N/A if (i != 0) {
0N/A return null;
0N/A }
0N/A if (editor.getAccessibleContext() != null) {
0N/A return (Accessible)editor;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /* ===== End AccessibleContext methods ===== */
0N/A
0N/A /**
0N/A * Gets the AccessibleAction associated with this object that supports
0N/A * one or more actions.
0N/A *
0N/A * @return AccessibleAction if supported by object; else return null
0N/A * @see AccessibleAction
0N/A */
0N/A public AccessibleAction getAccessibleAction() {
0N/A return this;
0N/A }
0N/A
0N/A /**
0N/A * Gets the AccessibleText associated with this object presenting
0N/A * text on the display.
0N/A *
0N/A * @return AccessibleText if supported by object; else return null
0N/A * @see AccessibleText
0N/A */
0N/A public AccessibleText getAccessibleText() {
0N/A return this;
0N/A }
0N/A
0N/A /*
0N/A * Returns the AccessibleContext for the JSpinner editor
0N/A */
0N/A private AccessibleContext getEditorAccessibleContext() {
0N/A if (editor instanceof DefaultEditor) {
0N/A JTextField textField = ((DefaultEditor)editor).getTextField();
0N/A if (textField != null) {
0N/A return textField.getAccessibleContext();
0N/A }
0N/A } else if (editor instanceof Accessible) {
625N/A return editor.getAccessibleContext();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /*
0N/A * Returns the AccessibleText for the JSpinner editor
0N/A */
0N/A private AccessibleText getEditorAccessibleText() {
0N/A AccessibleContext ac = getEditorAccessibleContext();
0N/A if (ac != null) {
0N/A return ac.getAccessibleText();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /*
0N/A * Returns the AccessibleEditableText for the JSpinner editor
0N/A */
0N/A private AccessibleEditableText getEditorAccessibleEditableText() {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at instanceof AccessibleEditableText) {
0N/A return (AccessibleEditableText)at;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Gets the AccessibleValue associated with this object.
0N/A *
0N/A * @return AccessibleValue if supported by object; else return null
0N/A * @see AccessibleValue
0N/A *
0N/A */
0N/A public AccessibleValue getAccessibleValue() {
0N/A return this;
0N/A }
0N/A
0N/A /* ===== Begin AccessibleValue impl ===== */
0N/A
0N/A /**
0N/A * Get the value of this object as a Number. If the value has not been
0N/A * set, the return value will be null.
0N/A *
0N/A * @return value of the object
0N/A * @see #setCurrentAccessibleValue
0N/A */
0N/A public Number getCurrentAccessibleValue() {
0N/A Object o = model.getValue();
0N/A if (o instanceof Number) {
0N/A return (Number)o;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Set the value of this object as a Number.
0N/A *
0N/A * @param n the value to set for this object
0N/A * @return true if the value was set; else False
0N/A * @see #getCurrentAccessibleValue
0N/A */
0N/A public boolean setCurrentAccessibleValue(Number n) {
0N/A // try to set the new value
0N/A try {
0N/A model.setValue(n);
0N/A return true;
0N/A } catch (IllegalArgumentException iae) {
0N/A // SpinnerModel didn't like new value
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Get the minimum value of this object as a Number.
0N/A *
0N/A * @return Minimum value of the object; null if this object does not
0N/A * have a minimum value
0N/A * @see #getMaximumAccessibleValue
0N/A */
0N/A public Number getMinimumAccessibleValue() {
0N/A if (model instanceof SpinnerNumberModel) {
0N/A SpinnerNumberModel numberModel = (SpinnerNumberModel)model;
0N/A Object o = numberModel.getMinimum();
0N/A if (o instanceof Number) {
0N/A return (Number)o;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Get the maximum value of this object as a Number.
0N/A *
0N/A * @return Maximum value of the object; null if this object does not
0N/A * have a maximum value
0N/A * @see #getMinimumAccessibleValue
0N/A */
0N/A public Number getMaximumAccessibleValue() {
0N/A if (model instanceof SpinnerNumberModel) {
0N/A SpinnerNumberModel numberModel = (SpinnerNumberModel)model;
0N/A Object o = numberModel.getMaximum();
0N/A if (o instanceof Number) {
0N/A return (Number)o;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /* ===== End AccessibleValue impl ===== */
0N/A
0N/A /* ===== Begin AccessibleAction impl ===== */
0N/A
0N/A /**
0N/A * Returns the number of accessible actions available in this object
0N/A * If there are more than one, the first one is considered the "default"
0N/A * action of the object.
0N/A *
0N/A * Two actions are supported: AccessibleAction.INCREMENT which
0N/A * increments the spinner value and AccessibleAction.DECREMENT
0N/A * which decrements the spinner value
0N/A *
0N/A * @return the zero-based number of Actions in this object
0N/A */
0N/A public int getAccessibleActionCount() {
0N/A return 2;
0N/A }
0N/A
0N/A /**
0N/A * Returns a description of the specified action of the object.
0N/A *
0N/A * @param i zero-based index of the actions
0N/A * @return a String description of the action
0N/A * @see #getAccessibleActionCount
0N/A */
0N/A public String getAccessibleActionDescription(int i) {
0N/A if (i == 0) {
0N/A return AccessibleAction.INCREMENT;
0N/A } else if (i == 1) {
0N/A return AccessibleAction.DECREMENT;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Performs the specified Action on the object
0N/A *
0N/A * @param i zero-based index of actions. The first action
0N/A * (index 0) is AccessibleAction.INCREMENT and the second
0N/A * action (index 1) is AccessibleAction.DECREMENT.
0N/A * @return true if the action was performed; otherwise false.
0N/A * @see #getAccessibleActionCount
0N/A */
0N/A public boolean doAccessibleAction(int i) {
0N/A if (i < 0 || i > 1) {
0N/A return false;
0N/A }
625N/A Object o;
0N/A if (i == 0) {
0N/A o = getNextValue(); // AccessibleAction.INCREMENT
0N/A } else {
0N/A o = getPreviousValue(); // AccessibleAction.DECREMENT
0N/A }
0N/A // try to set the new value
0N/A try {
0N/A model.setValue(o);
0N/A return true;
0N/A } catch (IllegalArgumentException iae) {
0N/A // SpinnerModel didn't like new value
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /* ===== End AccessibleAction impl ===== */
0N/A
0N/A /* ===== Begin AccessibleText impl ===== */
0N/A
0N/A /*
0N/A * Returns whether source and destination components have the
0N/A * same window ancestor
0N/A */
0N/A private boolean sameWindowAncestor(Component src, Component dest) {
0N/A if (src == null || dest == null) {
0N/A return false;
0N/A }
0N/A return SwingUtilities.getWindowAncestor(src) ==
0N/A SwingUtilities.getWindowAncestor(dest);
0N/A }
0N/A
0N/A /**
0N/A * Given a point in local coordinates, return the zero-based index
0N/A * of the character under that Point. If the point is invalid,
0N/A * this method returns -1.
0N/A *
0N/A * @param p the Point in local coordinates
0N/A * @return the zero-based index of the character under Point p; if
0N/A * Point is invalid return -1.
0N/A */
0N/A public int getIndexAtPoint(Point p) {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null && sameWindowAncestor(JSpinner.this, editor)) {
0N/A // convert point from the JSpinner bounds (source) to
0N/A // editor bounds (destination)
0N/A Point editorPoint = SwingUtilities.convertPoint(JSpinner.this,
0N/A p,
0N/A editor);
0N/A if (editorPoint != null) {
0N/A return at.getIndexAtPoint(editorPoint);
0N/A }
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Determines the bounding box of the character at the given
0N/A * index into the string. The bounds are returned in local
0N/A * coordinates. If the index is invalid an empty rectangle is
0N/A * returned.
0N/A *
0N/A * @param i the index into the String
0N/A * @return the screen coordinates of the character's bounding box,
0N/A * if index is invalid return an empty rectangle.
0N/A */
0N/A public Rectangle getCharacterBounds(int i) {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null ) {
0N/A Rectangle editorRect = at.getCharacterBounds(i);
0N/A if (editorRect != null &&
0N/A sameWindowAncestor(JSpinner.this, editor)) {
0N/A // return rectangle in the the JSpinner bounds
0N/A return SwingUtilities.convertRectangle(editor,
0N/A editorRect,
0N/A JSpinner.this);
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of characters (valid indicies)
0N/A *
0N/A * @return the number of characters
0N/A */
0N/A public int getCharCount() {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getCharCount();
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Returns the zero-based offset of the caret.
0N/A *
0N/A * Note: That to the right of the caret will have the same index
0N/A * value as the offset (the caret is between two characters).
0N/A * @return the zero-based offset of the caret.
0N/A */
0N/A public int getCaretPosition() {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getCaretPosition();
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Returns the String at a given index.
0N/A *
0N/A * @param part the CHARACTER, WORD, or SENTENCE to retrieve
0N/A * @param index an index within the text
0N/A * @return the letter, word, or sentence
0N/A */
0N/A public String getAtIndex(int part, int index) {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getAtIndex(part, index);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the String after a given index.
0N/A *
0N/A * @param part the CHARACTER, WORD, or SENTENCE to retrieve
0N/A * @param index an index within the text
0N/A * @return the letter, word, or sentence
0N/A */
0N/A public String getAfterIndex(int part, int index) {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getAfterIndex(part, index);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the String before a given index.
0N/A *
0N/A * @param part the CHARACTER, WORD, or SENTENCE to retrieve
0N/A * @param index an index within the text
0N/A * @return the letter, word, or sentence
0N/A */
0N/A public String getBeforeIndex(int part, int index) {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getBeforeIndex(part, index);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the AttributeSet for a given character at a given index
0N/A *
0N/A * @param i the zero-based index into the text
0N/A * @return the AttributeSet of the character
0N/A */
0N/A public AttributeSet getCharacterAttribute(int i) {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getCharacterAttribute(i);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the start offset within the selected text.
0N/A * If there is no selection, but there is
0N/A * a caret, the start and end offsets will be the same.
0N/A *
0N/A * @return the index into the text of the start of the selection
0N/A */
0N/A public int getSelectionStart() {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getSelectionStart();
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Returns the end offset within the selected text.
0N/A * If there is no selection, but there is
0N/A * a caret, the start and end offsets will be the same.
0N/A *
0N/A * @return the index into teh text of the end of the selection
0N/A */
0N/A public int getSelectionEnd() {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getSelectionEnd();
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Returns the portion of the text that is selected.
0N/A *
0N/A * @return the String portion of the text that is selected
0N/A */
0N/A public String getSelectedText() {
0N/A AccessibleText at = getEditorAccessibleText();
0N/A if (at != null) {
0N/A return at.getSelectedText();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /* ===== End AccessibleText impl ===== */
0N/A
0N/A
0N/A /* ===== Begin AccessibleEditableText impl ===== */
0N/A
0N/A /**
0N/A * Sets the text contents to the specified string.
0N/A *
0N/A * @param s the string to set the text contents
0N/A */
0N/A public void setTextContents(String s) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.setTextContents(s);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Inserts the specified string at the given index/
0N/A *
0N/A * @param index the index in the text where the string will
0N/A * be inserted
0N/A * @param s the string to insert in the text
0N/A */
0N/A public void insertTextAtIndex(int index, String s) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.insertTextAtIndex(index, s);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the text string between two indices.
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A * @param endIndex the ending index in the text
0N/A * @return the text string between the indices
0N/A */
0N/A public String getTextRange(int startIndex, int endIndex) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A return at.getTextRange(startIndex, endIndex);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Deletes the text between two indices
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A * @param endIndex the ending index in the text
0N/A */
0N/A public void delete(int startIndex, int endIndex) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.delete(startIndex, endIndex);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Cuts the text between two indices into the system clipboard.
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A * @param endIndex the ending index in the text
0N/A */
0N/A public void cut(int startIndex, int endIndex) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.cut(startIndex, endIndex);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Pastes the text from the system clipboard into the text
0N/A * starting at the specified index.
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A */
0N/A public void paste(int startIndex) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.paste(startIndex);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Replaces the text between two indices with the specified
0N/A * string.
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A * @param endIndex the ending index in the text
0N/A * @param s the string to replace the text between two indices
0N/A */
0N/A public void replaceText(int startIndex, int endIndex, String s) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.replaceText(startIndex, endIndex, s);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Selects the text between two indices.
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A * @param endIndex the ending index in the text
0N/A */
0N/A public void selectText(int startIndex, int endIndex) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.selectText(startIndex, endIndex);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets attributes for the text between two indices.
0N/A *
0N/A * @param startIndex the starting index in the text
0N/A * @param endIndex the ending index in the text
0N/A * @param as the attribute set
0N/A * @see AttributeSet
0N/A */
0N/A public void setAttributes(int startIndex, int endIndex, AttributeSet as) {
0N/A AccessibleEditableText at = getEditorAccessibleEditableText();
0N/A if (at != null) {
0N/A at.setAttributes(startIndex, endIndex, as);
0N/A }
0N/A }
0N/A } /* End AccessibleJSpinner */
0N/A}