Source Code Cross Referenced for JSpinner.java in  » 6.0-JDK-Core » swing » javax » swing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 2000-2007 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        package javax.swing;
0027
0028        import java.awt.*;
0029        import java.awt.event.*;
0030
0031        import javax.swing.*;
0032        import javax.swing.event.*;
0033        import javax.swing.text.*;
0034        import javax.swing.plaf.SpinnerUI;
0035
0036        import java.util.*;
0037        import java.beans.*;
0038        import java.text.*;
0039        import java.io.*;
0040        import java.util.HashMap;
0041        import sun.util.resources.LocaleData;
0042
0043        import javax.accessibility.*;
0044
0045        /**
0046         * A single line input field that lets the user select a
0047         * number or an object value from an ordered sequence. Spinners typically
0048         * provide a pair of tiny arrow buttons for stepping through the elements
0049         * of the sequence. The keyboard up/down arrow keys also cycle through the 
0050         * elements. The user may also be allowed to type a (legal) value directly 
0051         * into the spinner. Although combo boxes provide similar functionality, 
0052         * spinners are sometimes preferred because they don't require a drop down list
0053         * that can obscure important data.
0054         * <p>
0055         * A <code>JSpinner</code>'s sequence value is defined by its
0056         * <code>SpinnerModel</code>.
0057         * The <code>model</code> can be specified as a constructor argument and
0058         * changed with the <code>model</code> property.  <code>SpinnerModel</code>
0059         * classes for some common types are provided: <code>SpinnerListModel</code>,
0060         * <code>SpinnerNumberModel</code>, and <code>SpinnerDateModel</code>.
0061         * <p>
0062         * A <code>JSpinner</code> has a single child component that's
0063         * responsible for displaying
0064         * and potentially changing the current element or <i>value</i> of 
0065         * the model, which is called the <code>editor</code>.  The editor is created
0066         * by the <code>JSpinner</code>'s constructor and can be changed with the 
0067         * <code>editor</code> property.  The <code>JSpinner</code>'s editor stays
0068         * in sync with the model by listening for <code>ChangeEvent</code>s. If the 
0069         * user has changed the value displayed by the <code>editor</code> it is
0070         * possible for the <code>model</code>'s value to differ from that of
0071         * the <code>editor</code>. To make sure the <code>model</code> has the same
0072         * value as the editor use the <code>commitEdit</code> method, eg:
0073         * <pre>
0074         *   try {
0075         *       spinner.commitEdit();
0076         *   }
0077         *   catch (ParseException pe) {{
0078         *       // Edited value is invalid, spinner.getValue() will return
0079         *       // the last valid value, you could revert the spinner to show that:
0080         *       JComponent editor = spinner.getEditor()
0081         *       if (editor instanceof DefaultEditor) {
0082         *           ((DefaultEditor)editor).getTextField().setValue(spinner.getValue();
0083         *       }
0084         *       // reset the value to some known value:
0085         *       spinner.setValue(fallbackValue);
0086         *       // or treat the last valid value as the current, in which
0087         *       // case you don't need to do anything.
0088         *   }
0089         *   return spinner.getValue();
0090         * </pre>
0091         * <p>
0092         * For information and examples of using spinner see
0093         * <a href="http://java.sun.com/doc/books/tutorial/uiswing/components/spinner.html">How to Use Spinners</a>,
0094         * a section in <em>The Java Tutorial.</em>
0095         * <p>
0096         * <strong>Warning:</strong> Swing is not thread safe. For more
0097         * information see <a
0098         * href="package-summary.html#threading">Swing's Threading
0099         * Policy</a>.
0100         * <p>
0101         * <strong>Warning:</strong>
0102         * Serialized objects of this class will not be compatible with
0103         * future Swing releases. The current serialization support is
0104         * appropriate for short term storage or RMI between applications running
0105         * the same version of Swing.  As of 1.4, support for long term storage
0106         * of all JavaBeans<sup><font size="-2">TM</font></sup>
0107         * has been added to the <code>java.beans</code> package.
0108         * Please see {@link java.beans.XMLEncoder}.
0109         * 
0110         * @beaninfo
0111         *   attribute: isContainer false
0112         * description: A single line input field that lets the user select a 
0113         *     number or an object value from an ordered set.  
0114         * 
0115         * @see SpinnerModel
0116         * @see AbstractSpinnerModel
0117         * @see SpinnerListModel
0118         * @see SpinnerNumberModel
0119         * @see SpinnerDateModel
0120         * @see JFormattedTextField
0121         * 
0122         * @version 1.57 05/05/07
0123         * @author Hans Muller
0124         * @author Lynn Monsanto (accessibility)
0125         * @since 1.4
0126         */
0127        public class JSpinner extends JComponent implements  Accessible {
0128            /**
0129             * @see #getUIClassID
0130             * @see #readObject
0131             */
0132            private static final String uiClassID = "SpinnerUI";
0133
0134            private static final Action DISABLED_ACTION = new DisabledAction();
0135
0136            private SpinnerModel model;
0137            private JComponent editor;
0138            private ChangeListener modelListener;
0139            private transient ChangeEvent changeEvent;
0140            private boolean editorExplicitlySet = false;
0141
0142            /**
0143             * Constructs a spinner for the given model. The spinner has
0144             * a set of previous/next buttons, and an editor appropriate
0145             * for the model.
0146             *
0147             * @throws NullPointerException if the model is {@code null}
0148             */
0149            public JSpinner(SpinnerModel model) {
0150                if (model == null) {
0151                    throw new NullPointerException("model cannot be null");
0152                }
0153                this .model = model;
0154                this .editor = createEditor(model);
0155                setOpaque(true);
0156                updateUI();
0157            }
0158
0159            /**
0160             * Constructs a spinner with an <code>Integer SpinnerNumberModel</code>
0161             * with initial value 0 and no minimum or maximum limits.
0162             */
0163            public JSpinner() {
0164                this (new SpinnerNumberModel());
0165            }
0166
0167            /**
0168             * Returns the look and feel (L&F) object that renders this component.
0169             *
0170             * @return the <code>SpinnerUI</code> object that renders this component
0171             */
0172            public SpinnerUI getUI() {
0173                return (SpinnerUI) ui;
0174            }
0175
0176            /**
0177             * Sets the look and feel (L&F) object that renders this component.
0178             *
0179             * @param ui  the <code>SpinnerUI</code> L&F object
0180             * @see UIDefaults#getUI
0181             */
0182            public void setUI(SpinnerUI ui) {
0183                super .setUI(ui);
0184            }
0185
0186            /**
0187             * Returns the suffix used to construct the name of the look and feel 
0188             * (L&F) class used to render this component.
0189             *
0190             * @return the string "SpinnerUI"
0191             * @see JComponent#getUIClassID
0192             * @see UIDefaults#getUI
0193             */
0194            public String getUIClassID() {
0195                return uiClassID;
0196            }
0197
0198            /**
0199             * Resets the UI property with the value from the current look and feel.
0200             *
0201             * @see UIManager#getUI
0202             */
0203            public void updateUI() {
0204                setUI((SpinnerUI) UIManager.getUI(this ));
0205                invalidate();
0206            }
0207
0208            /**
0209             * This method is called by the constructors to create the 
0210             * <code>JComponent</code>
0211             * that displays the current value of the sequence.  The editor may 
0212             * also allow the user to enter an element of the sequence directly.
0213             * An editor must listen for <code>ChangeEvents</code> on the 
0214             * <code>model</code> and keep the value it displays
0215             * in sync with the value of the model.
0216             * <p>
0217             * Subclasses may override this method to add support for new
0218             * <code>SpinnerModel</code> classes.  Alternatively one can just
0219             * replace the editor created here with the <code>setEditor</code>
0220             * method.  The default mapping from model type to editor is:
0221             * <ul>
0222             * <li> <code>SpinnerNumberModel =&gt; JSpinner.NumberEditor</code>
0223             * <li> <code>SpinnerDateModel =&gt; JSpinner.DateEditor</code>
0224             * <li> <code>SpinnerListModel =&gt; JSpinner.ListEditor</code>
0225             * <li> <i>all others</i> =&gt; <code>JSpinner.DefaultEditor</code>
0226             * </ul>
0227             * 
0228             * @return a component that displays the current value of the sequence
0229             * @param model the value of getModel
0230             * @see #getModel
0231             * @see #setEditor
0232             */
0233            protected JComponent createEditor(SpinnerModel model) {
0234                if (model instanceof  SpinnerDateModel) {
0235                    return new DateEditor(this );
0236                } else if (model instanceof  SpinnerListModel) {
0237                    return new ListEditor(this );
0238                } else if (model instanceof  SpinnerNumberModel) {
0239                    return new NumberEditor(this );
0240                } else {
0241                    return new DefaultEditor(this );
0242                }
0243            }
0244
0245            /**
0246             * Changes the model that represents the value of this spinner.  
0247             * If the editor property has not been explicitly set, 
0248             * the editor property is (implicitly) set after the <code>"model"</code>
0249             * <code>PropertyChangeEvent</code> has been fired.  The editor
0250             * property is set to the value returned by <code>createEditor</code>,
0251             * as in:
0252             * <pre>
0253             * setEditor(createEditor(model));
0254             * </pre>
0255             * 
0256             * @param model the new <code>SpinnerModel</code>
0257             * @see #getModel
0258             * @see #getEditor
0259             * @see #setEditor
0260             * @throws IllegalArgumentException if model is <code>null</code>
0261             * 
0262             * @beaninfo
0263             *        bound: true
0264             *    attribute: visualUpdate true
0265             *  description: Model that represents the value of this spinner.
0266             */
0267            public void setModel(SpinnerModel model) {
0268                if (model == null) {
0269                    throw new IllegalArgumentException("null model");
0270                }
0271                if (!model.equals(this .model)) {
0272                    SpinnerModel oldModel = this .model;
0273                    this .model = model;
0274                    if (modelListener != null) {
0275                        this .model.addChangeListener(modelListener);
0276                    }
0277                    firePropertyChange("model", oldModel, model);
0278                    if (!editorExplicitlySet) {
0279                        setEditor(createEditor(model)); // sets editorExplicitlySet true
0280                        editorExplicitlySet = false;
0281                    }
0282                    repaint();
0283                    revalidate();
0284                }
0285            }
0286
0287            /**
0288             * Returns the <code>SpinnerModel</code> that defines
0289             * this spinners sequence of values.
0290             * 
0291             * @return the value of the model property
0292             * @see #setModel
0293             */
0294            public SpinnerModel getModel() {
0295                return model;
0296            }
0297
0298            /**
0299             * Returns the current value of the model, typically
0300             * this value is displayed by the <code>editor</code>. If the 
0301             * user has changed the value displayed by the <code>editor</code> it is
0302             * possible for the <code>model</code>'s value to differ from that of
0303             * the <code>editor</code>, refer to the class level javadoc for examples
0304             * of how to deal with this.
0305             * <p>
0306             * This method simply delegates to the <code>model</code>.  
0307             * It is equivalent to:
0308             * <pre>
0309             * getModel().getValue()
0310             * </pre>
0311             * 
0312             * @see #setValue
0313             * @see SpinnerModel#getValue
0314             */
0315            public Object getValue() {
0316                return getModel().getValue();
0317            }
0318
0319            /**
0320             * Changes current value of the model, typically
0321             * this value is displayed by the <code>editor</code>.
0322             * If the <code>SpinnerModel</code> implementation 
0323             * doesn't support the specified value then an
0324             * <code>IllegalArgumentException</code> is thrown.  
0325             * <p>
0326             * This method simply delegates to the <code>model</code>.  
0327             * It is equivalent to:
0328             * <pre>
0329             * getModel().setValue(value)
0330             * </pre>
0331             * 
0332             * @throws IllegalArgumentException if <code>value</code> isn't allowed
0333             * @see #getValue
0334             * @see SpinnerModel#setValue
0335             */
0336            public void setValue(Object value) {
0337                getModel().setValue(value);
0338            }
0339
0340            /**
0341             * Returns the object in the sequence that comes after the object returned 
0342             * by <code>getValue()</code>. If the end of the sequence has been reached 
0343             * then return <code>null</code>.  
0344             * Calling this method does not effect <code>value</code>.
0345             * <p>
0346             * This method simply delegates to the <code>model</code>.  
0347             * It is equivalent to:
0348             * <pre>
0349             * getModel().getNextValue()
0350             * </pre>
0351             * 
0352             * @return the next legal value or <code>null</code> if one doesn't exist
0353             * @see #getValue
0354             * @see #getPreviousValue
0355             * @see SpinnerModel#getNextValue
0356             */
0357            public Object getNextValue() {
0358                return getModel().getNextValue();
0359            }
0360
0361            /**
0362             * We pass <code>Change</code> events along to the listeners with the 
0363             * the slider (instead of the model itself) as the event source.
0364             */
0365            private class ModelListener implements  ChangeListener, Serializable {
0366                public void stateChanged(ChangeEvent e) {
0367                    fireStateChanged();
0368                }
0369            }
0370
0371            /**
0372             * Adds a listener to the list that is notified each time a change
0373             * to the model occurs.  The source of <code>ChangeEvents</code> 
0374             * delivered to <code>ChangeListeners</code> will be this 
0375             * <code>JSpinner</code>.  Note also that replacing the model
0376             * will not affect listeners added directly to JSpinner. 
0377             * Applications can add listeners to  the model directly.  In that 
0378             * case is that the source of the event would be the 
0379             * <code>SpinnerModel</code>.  
0380             * 
0381             * @param listener the <code>ChangeListener</code> to add
0382             * @see #removeChangeListener
0383             * @see #getModel
0384             */
0385            public void addChangeListener(ChangeListener listener) {
0386                if (modelListener == null) {
0387                    modelListener = new ModelListener();
0388                    getModel().addChangeListener(modelListener);
0389                }
0390                listenerList.add(ChangeListener.class, listener);
0391            }
0392
0393            /**
0394             * Removes a <code>ChangeListener</code> from this spinner.
0395             *
0396             * @param listener the <code>ChangeListener</code> to remove
0397             * @see #fireStateChanged
0398             * @see #addChangeListener
0399             */
0400            public void removeChangeListener(ChangeListener listener) {
0401                listenerList.remove(ChangeListener.class, listener);
0402            }
0403
0404            /**
0405             * Returns an array of all the <code>ChangeListener</code>s added
0406             * to this JSpinner with addChangeListener().
0407             *
0408             * @return all of the <code>ChangeListener</code>s added or an empty
0409             *         array if no listeners have been added
0410             * @since 1.4
0411             */
0412            public ChangeListener[] getChangeListeners() {
0413                return (ChangeListener[]) listenerList
0414                        .getListeners(ChangeListener.class);
0415            }
0416
0417            /**
0418             * Sends a <code>ChangeEvent</code>, whose source is this 
0419             * <code>JSpinner</code>, to each <code>ChangeListener</code>.  
0420             * When a <code>ChangeListener</code> has been added 
0421             * to the spinner, this method method is called each time 
0422             * a <code>ChangeEvent</code> is received from the model.
0423             * 
0424             * @see #addChangeListener
0425             * @see #removeChangeListener
0426             * @see EventListenerList
0427             */
0428            protected void fireStateChanged() {
0429                Object[] listeners = listenerList.getListenerList();
0430                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0431                    if (listeners[i] == ChangeListener.class) {
0432                        if (changeEvent == null) {
0433                            changeEvent = new ChangeEvent(this );
0434                        }
0435                        ((ChangeListener) listeners[i + 1])
0436                                .stateChanged(changeEvent);
0437                    }
0438                }
0439            }
0440
0441            /**
0442             * Returns the object in the sequence that comes
0443             * before the object returned by <code>getValue()</code>.
0444             * If the end of the sequence has been reached then 
0445             * return <code>null</code>. Calling this method does
0446             * not effect <code>value</code>.
0447             * <p>
0448             * This method simply delegates to the <code>model</code>.  
0449             * It is equivalent to:
0450             * <pre>
0451             * getModel().getPreviousValue()
0452             * </pre>
0453             * 
0454             * @return the previous legal value or <code>null</code>
0455             *   if one doesn't exist
0456             * @see #getValue
0457             * @see #getNextValue
0458             * @see SpinnerModel#getPreviousValue
0459             */
0460            public Object getPreviousValue() {
0461                return getModel().getPreviousValue();
0462            }
0463
0464            /**
0465             * Changes the <code>JComponent</code> that displays the current value 
0466             * of the <code>SpinnerModel</code>.  It is the responsibility of this 
0467             * method to <i>disconnect</i> the old editor from the model and to
0468             * connect the new editor.  This may mean removing the
0469             * old editors <code>ChangeListener</code> from the model or the
0470             * spinner itself and adding one for the new editor.
0471             * 
0472             * @param editor the new editor
0473             * @see #getEditor
0474             * @see #createEditor
0475             * @see #getModel
0476             * @throws IllegalArgumentException if editor is <code>null</code>
0477             * 
0478             * @beaninfo
0479             *        bound: true
0480             *    attribute: visualUpdate true
0481             *  description: JComponent that displays the current value of the model
0482             */
0483            public void setEditor(JComponent editor) {
0484                if (editor == null) {
0485                    throw new IllegalArgumentException("null editor");
0486                }
0487                if (!editor.equals(this .editor)) {
0488                    JComponent oldEditor = this .editor;
0489                    this .editor = editor;
0490                    if (oldEditor instanceof  DefaultEditor) {
0491                        ((DefaultEditor) oldEditor).dismiss(this );
0492                    }
0493                    editorExplicitlySet = true;
0494                    firePropertyChange("editor", oldEditor, editor);
0495                    revalidate();
0496                    repaint();
0497                }
0498            }
0499
0500            /**
0501             * Returns the component that displays and potentially 
0502             * changes the model's value.
0503             * 
0504             * @return the component that displays and potentially
0505             *    changes the model's value
0506             * @see #setEditor
0507             * @see #createEditor
0508             */
0509            public JComponent getEditor() {
0510                return editor;
0511            }
0512
0513            /**
0514             * Commits the currently edited value to the <code>SpinnerModel</code>.
0515             * <p>
0516             * If the editor is an instance of <code>DefaultEditor</code>, the
0517             * call if forwarded to the editor, otherwise this does nothing.
0518             *
0519             * @throws ParseException if the currently edited value couldn't
0520             *         be commited.
0521             */
0522            public void commitEdit() throws ParseException {
0523                JComponent editor = getEditor();
0524                if (editor instanceof  DefaultEditor) {
0525                    ((DefaultEditor) editor).commitEdit();
0526                }
0527            }
0528
0529            /*
0530             * See readObject and writeObject in JComponent for more 
0531             * information about serialization in Swing.
0532             *
0533             * @param s Stream to write to
0534             */
0535            private void writeObject(ObjectOutputStream s) throws IOException {
0536                s.defaultWriteObject();
0537                if (getUIClassID().equals(uiClassID)) {
0538                    byte count = JComponent.getWriteObjCounter(this );
0539                    JComponent.setWriteObjCounter(this , --count);
0540                    if (count == 0 && ui != null) {
0541                        ui.installUI(this );
0542                    }
0543                }
0544            }
0545
0546            /**
0547             * A simple base class for more specialized editors
0548             * that displays a read-only view of the model's current
0549             * value with a <code>JFormattedTextField</code>.  Subclasses
0550             * can configure the <code>JFormattedTextField</code> to create
0551             * an editor that's appropriate for the type of model they
0552             * support and they may want to override
0553             * the <code>stateChanged</code> and <code>propertyChanged</code>
0554             * methods, which keep the model and the text field in sync.
0555             * <p>
0556             * This class defines a <code>dismiss</code> method that removes the
0557             * editors <code>ChangeListener</code> from the <code>JSpinner</code>
0558             * that it's part of.   The <code>setEditor</code> method knows about
0559             * <code>DefaultEditor.dismiss</code>, so if the developer
0560             * replaces an editor that's derived from <code>JSpinner.DefaultEditor</code>
0561             * its <code>ChangeListener</code> connection back to the 
0562             * <code>JSpinner</code> will be removed.  However after that,
0563             * it's up to the developer to manage their editor listeners.
0564             * Similarly, if a subclass overrides <code>createEditor</code>,
0565             * it's up to the subclasser to deal with their editor
0566             * subsequently being replaced (with <code>setEditor</code>).
0567             * We expect that in most cases, and in editor installed
0568             * with <code>setEditor</code> or created by a <code>createEditor</code>
0569             * override, will not be replaced anyway.
0570             * <p>
0571             * This class is the <code>LayoutManager</code> for it's single
0572             * <code>JFormattedTextField</code> child.   By default the
0573             * child is just centered with the parents insets.
0574             * @since 1.4
0575             */
0576            public static class DefaultEditor extends JPanel implements 
0577                    ChangeListener, PropertyChangeListener, LayoutManager {
0578                /**
0579                 * Constructs an editor component for the specified <code>JSpinner</code>.
0580                 * This <code>DefaultEditor</code> is it's own layout manager and 
0581                 * it is added to the spinner's <code>ChangeListener</code> list.
0582                 * The constructor creates a single <code>JFormattedTextField</code> child,
0583                 * initializes it's value to be the spinner model's current value
0584                 * and adds it to <code>this</code> <code>DefaultEditor</code>.  
0585                 * 
0586                 * @param spinner the spinner whose model <code>this</code> editor will monitor
0587                 * @see #getTextField
0588                 * @see JSpinner#addChangeListener
0589                 */
0590                public DefaultEditor(JSpinner spinner) {
0591                    super (null);
0592
0593                    JFormattedTextField ftf = new JFormattedTextField();
0594                    ftf.setName("Spinner.formattedTextField");
0595                    ftf.setValue(spinner.getValue());
0596                    ftf.addPropertyChangeListener(this );
0597                    ftf.setEditable(false);
0598                    ftf.setInheritsPopupMenu(true);
0599
0600                    String toolTipText = spinner.getToolTipText();
0601                    if (toolTipText != null) {
0602                        ftf.setToolTipText(toolTipText);
0603                    }
0604
0605                    add(ftf);
0606
0607                    setLayout(this );
0608                    spinner.addChangeListener(this );
0609
0610                    // We want the spinner's increment/decrement actions to be
0611                    // active vs those of the JFormattedTextField. As such we
0612                    // put disabled actions in the JFormattedTextField's actionmap.
0613                    // A binding to a disabled action is treated as a nonexistant
0614                    // binding.
0615                    ActionMap ftfMap = ftf.getActionMap();
0616
0617                    if (ftfMap != null) {
0618                        ftfMap.put("increment", DISABLED_ACTION);
0619                        ftfMap.put("decrement", DISABLED_ACTION);
0620                    }
0621                }
0622
0623                /**
0624                 * Disconnect <code>this</code> editor from the specified 
0625                 * <code>JSpinner</code>.  By default, this method removes 
0626                 * itself from the spinners <code>ChangeListener</code> list.
0627                 * 
0628                 * @param spinner the <code>JSpinner</code> to disconnect this 
0629                 *    editor from; the same spinner as was passed to the constructor.
0630                 */
0631                public void dismiss(JSpinner spinner) {
0632                    spinner.removeChangeListener(this );
0633                }
0634
0635                /**
0636                 * Returns the <code>JSpinner</code> ancestor of this editor or
0637                 * <code>null</code> if none of the ancestors are a
0638                 * <code>JSpinner</code>.
0639                 * Typically the editor's parent is a <code>JSpinner</code> however 
0640                 * subclasses of <code>JSpinner</code> may override the
0641                 * the <code>createEditor</code> method and insert one or more containers
0642                 * between the <code>JSpinner</code> and it's editor.
0643                 * 
0644                 * @return <code>JSpinner</code> ancestor; <code>null</code>
0645                 *         if none of the ancestors are a <code>JSpinner</code>
0646                 * 
0647                 * @see JSpinner#createEditor
0648                 */
0649                public JSpinner getSpinner() {
0650                    for (Component c = this ; c != null; c = c.getParent()) {
0651                        if (c instanceof  JSpinner) {
0652                            return (JSpinner) c;
0653                        }
0654                    }
0655                    return null;
0656                }
0657
0658                /**
0659                 * Returns the <code>JFormattedTextField</code> child of this 
0660                 * editor.  By default the text field is the first and only 
0661                 * child of editor.
0662                 * 
0663                 * @return the <code>JFormattedTextField</code> that gives the user
0664                 *     access to the <code>SpinnerDateModel's</code> value.
0665                 * @see #getSpinner
0666                 * @see #getModel
0667                 */
0668                public JFormattedTextField getTextField() {
0669                    return (JFormattedTextField) getComponent(0);
0670                }
0671
0672                /**
0673                 * This method is called when the spinner's model's state changes.
0674                 * It sets the <code>value</code> of the text field to the current
0675                 * value of the spinners model.
0676                 * 
0677                 * @param e the <code>ChangeEvent</code> whose source is the
0678                 * <code>JSpinner</code> whose model has changed.
0679                 * @see #getTextField
0680                 * @see JSpinner#getValue
0681                 */
0682                public void stateChanged(ChangeEvent e) {
0683                    JSpinner spinner = (JSpinner) (e.getSource());
0684                    getTextField().setValue(spinner.getValue());
0685                }
0686
0687                /**
0688                 * Called by the <code>JFormattedTextField</code> 
0689                 * <code>PropertyChangeListener</code>.  When the <code>"value"</code>
0690                 * property changes, which implies that the user has typed a new
0691                 * number, we set the value of the spinners model.
0692                 * <p>
0693                 * This class ignores <code>PropertyChangeEvents</code> whose
0694                 * source is not the <code>JFormattedTextField</code>, so subclasses
0695                 * may safely make <code>this</code> <code>DefaultEditor</code> a 
0696                 * <code>PropertyChangeListener</code> on other objects.
0697                 * 
0698                 * @param e the <code>PropertyChangeEvent</code> whose source is
0699                 *    the <code>JFormattedTextField</code> created by this class.
0700                 * @see #getTextField
0701                 */
0702                public void propertyChange(PropertyChangeEvent e) {
0703                    JSpinner spinner = getSpinner();
0704
0705                    if (spinner == null) {
0706                        // Indicates we aren't installed anywhere.
0707                        return;
0708                    }
0709
0710                    Object source = e.getSource();
0711                    String name = e.getPropertyName();
0712                    if ((source instanceof  JFormattedTextField)
0713                            && "value".equals(name)) {
0714                        Object lastValue = spinner.getValue();
0715
0716                        // Try to set the new value
0717                        try {
0718                            spinner.setValue(getTextField().getValue());
0719                        } catch (IllegalArgumentException iae) {
0720                            // SpinnerModel didn't like new value, reset
0721                            try {
0722                                ((JFormattedTextField) source)
0723                                        .setValue(lastValue);
0724                            } catch (IllegalArgumentException iae2) {
0725                                // Still bogus, nothing else we can do, the
0726                                // SpinnerModel and JFormattedTextField are now out
0727                                // of sync.
0728                            }
0729                        }
0730                    }
0731                }
0732
0733                /**
0734                 * This <code>LayoutManager</code> method does nothing.  We're 
0735                 * only managing a single child and there's no support 
0736                 * for layout constraints.
0737                 * 
0738                 * @param name ignored
0739                 * @param child ignored
0740                 */
0741                public void addLayoutComponent(String name, Component child) {
0742                }
0743
0744                /**
0745                 * This <code>LayoutManager</code> method does nothing.  There
0746                 * isn't any per-child state.
0747                 * 
0748                 * @param child ignored
0749                 */
0750                public void removeLayoutComponent(Component child) {
0751                }
0752
0753                /**
0754                 * Returns the size of the parents insets.
0755                 */
0756                private Dimension insetSize(Container parent) {
0757                    Insets insets = parent.getInsets();
0758                    int w = insets.left + insets.right;
0759                    int h = insets.top + insets.bottom;
0760                    return new Dimension(w, h);
0761                }
0762
0763                /**
0764                 * Returns the preferred size of first (and only) child plus the
0765                 * size of the parents insets.
0766                 * 
0767                 * @param parent the Container that's managing the layout
0768                 * @return the preferred dimensions to lay out the subcomponents
0769                 *          of the specified container.
0770                 */
0771                public Dimension preferredLayoutSize(Container parent) {
0772                    Dimension preferredSize = insetSize(parent);
0773                    if (parent.getComponentCount() > 0) {
0774                        Dimension childSize = getComponent(0)
0775                                .getPreferredSize();
0776                        preferredSize.width += childSize.width;
0777                        preferredSize.height += childSize.height;
0778                    }
0779                    return preferredSize;
0780                }
0781
0782                /**
0783                 * Returns the minimum size of first (and only) child plus the
0784                 * size of the parents insets.
0785                 * 
0786                 * @param parent the Container that's managing the layout
0787                 * @return  the minimum dimensions needed to lay out the subcomponents
0788                 *          of the specified container.
0789                 */
0790                public Dimension minimumLayoutSize(Container parent) {
0791                    Dimension minimumSize = insetSize(parent);
0792                    if (parent.getComponentCount() > 0) {
0793                        Dimension childSize = getComponent(0).getMinimumSize();
0794                        minimumSize.width += childSize.width;
0795                        minimumSize.height += childSize.height;
0796                    }
0797                    return minimumSize;
0798                }
0799
0800                /**
0801                 * Resize the one (and only) child to completely fill the area
0802                 * within the parents insets.
0803                 */
0804                public void layoutContainer(Container parent) {
0805                    if (parent.getComponentCount() > 0) {
0806                        Insets insets = parent.getInsets();
0807                        int w = parent.getWidth()
0808                                - (insets.left + insets.right);
0809                        int h = parent.getHeight()
0810                                - (insets.top + insets.bottom);
0811                        getComponent(0)
0812                                .setBounds(insets.left, insets.top, w, h);
0813                    }
0814                }
0815
0816                /**
0817                 * Pushes the currently edited value to the <code>SpinnerModel</code>.
0818                 * <p>
0819                 * The default implementation invokes <code>commitEdit</code> on the
0820                 * <code>JFormattedTextField</code>.
0821                 *
0822                 * @throws ParseException if the edited value is not legal
0823                 */
0824                public void commitEdit() throws ParseException {
0825                    // If the value in the JFormattedTextField is legal, this will have
0826                    // the result of pushing the value to the SpinnerModel
0827                    // by way of the <code>propertyChange</code> method.
0828                    JFormattedTextField ftf = getTextField();
0829
0830                    ftf.commitEdit();
0831                }
0832
0833                /**
0834                 * Returns the baseline.
0835                 *
0836                 * @throws IllegalArgumentException {@inheritDoc}
0837                 * @see javax.swing.JComponent#getBaseline(int,int)
0838                 * @see javax.swing.JComponent#getBaselineResizeBehavior()
0839                 * @since 1.6
0840                 */
0841                public int getBaseline(int width, int height) {
0842                    // check size.
0843                    super .getBaseline(width, height);
0844                    Insets insets = getInsets();
0845                    width = width - insets.left - insets.right;
0846                    height = height - insets.top - insets.bottom;
0847                    int baseline = getComponent(0).getBaseline(width, height);
0848                    if (baseline >= 0) {
0849                        return baseline + insets.top;
0850                    }
0851                    return -1;
0852                }
0853
0854                /**
0855                 * Returns an enum indicating how the baseline of the component
0856                 * changes as the size changes.
0857                 *
0858                 * @throws NullPointerException {@inheritDoc}
0859                 * @see javax.swing.JComponent#getBaseline(int, int)
0860                 * @since 1.6
0861                 */
0862                public BaselineResizeBehavior getBaselineResizeBehavior() {
0863                    return getComponent(0).getBaselineResizeBehavior();
0864                }
0865            }
0866
0867            /**
0868             * This subclass of javax.swing.DateFormatter maps the minimum/maximum
0869             * properties to te start/end properties of a SpinnerDateModel.
0870             */
0871            private static class DateEditorFormatter extends DateFormatter {
0872                private final SpinnerDateModel model;
0873
0874                DateEditorFormatter(SpinnerDateModel model, DateFormat format) {
0875                    super (format);
0876                    this .model = model;
0877                }
0878
0879                public void setMinimum(Comparable min) {
0880                    model.setStart(min);
0881                }
0882
0883                public Comparable getMinimum() {
0884                    return model.getStart();
0885                }
0886
0887                public void setMaximum(Comparable max) {
0888                    model.setEnd(max);
0889                }
0890
0891                public Comparable getMaximum() {
0892                    return model.getEnd();
0893                }
0894            }
0895
0896            /**
0897             * An editor for a <code>JSpinner</code> whose model is a 
0898             * <code>SpinnerDateModel</code>.  The value of the editor is 
0899             * displayed with a <code>JFormattedTextField</code> whose format 
0900             * is defined by a <code>DateFormatter</code> instance whose
0901             * <code>minimum</code> and <code>maximum</code> properties
0902             * are mapped to the <code>SpinnerDateModel</code>.
0903             * @since 1.4
0904             */
0905            // PENDING(hmuller): more example javadoc
0906            public static class DateEditor extends DefaultEditor {
0907                // This is here until SimpleDateFormat gets a constructor that
0908                // takes a Locale: 4923525
0909                private static String getDefaultPattern(Locale loc) {
0910                    ResourceBundle r = LocaleData.getDateFormatData(loc);
0911                    String[] dateTimePatterns = r
0912                            .getStringArray("DateTimePatterns");
0913                    Object[] dateTimeArgs = {
0914                            dateTimePatterns[DateFormat.SHORT],
0915                            dateTimePatterns[DateFormat.SHORT + 4] };
0916                    return MessageFormat.format(dateTimePatterns[8],
0917                            dateTimeArgs);
0918                }
0919
0920                /**
0921                 * Construct a <code>JSpinner</code> editor that supports displaying
0922                 * and editing the value of a <code>SpinnerDateModel</code> 
0923                 * with a <code>JFormattedTextField</code>.  <code>This</code>
0924                 * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
0925                 * on the spinners model and a <code>PropertyChangeListener</code>
0926                 * on the new <code>JFormattedTextField</code>.
0927                 * 
0928                 * @param spinner the spinner whose model <code>this</code> editor will monitor
0929                 * @exception IllegalArgumentException if the spinners model is not 
0930                 *     an instance of <code>SpinnerDateModel</code>
0931                 * 
0932                 * @see #getModel
0933                 * @see #getFormat
0934                 * @see SpinnerDateModel
0935                 */
0936                public DateEditor(JSpinner spinner) {
0937                    this (spinner, getDefaultPattern(spinner.getLocale()));
0938                }
0939
0940                /**
0941                 * Construct a <code>JSpinner</code> editor that supports displaying
0942                 * and editing the value of a <code>SpinnerDateModel</code> 
0943                 * with a <code>JFormattedTextField</code>.  <code>This</code>
0944                 * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
0945                 * on the spinner and a <code>PropertyChangeListener</code>
0946                 * on the new <code>JFormattedTextField</code>.
0947                 * 
0948                 * @param spinner the spinner whose model <code>this</code> editor will monitor
0949                 * @param dateFormatPattern the initial pattern for the 
0950                 *     <code>SimpleDateFormat</code> object that's used to display
0951                 *     and parse the value of the text field.
0952                 * @exception IllegalArgumentException if the spinners model is not 
0953                 *     an instance of <code>SpinnerDateModel</code>
0954                 * 
0955                 * @see #getModel
0956                 * @see #getFormat
0957                 * @see SpinnerDateModel
0958                 * @see java.text.SimpleDateFormat
0959                 */
0960                public DateEditor(JSpinner spinner, String dateFormatPattern) {
0961                    this (spinner, new SimpleDateFormat(dateFormatPattern,
0962                            spinner.getLocale()));
0963                }
0964
0965                /**
0966                 * Construct a <code>JSpinner</code> editor that supports displaying
0967                 * and editing the value of a <code>SpinnerDateModel</code> 
0968                 * with a <code>JFormattedTextField</code>.  <code>This</code>
0969                 * <code>DateEditor</code> becomes both a <code>ChangeListener</code>
0970                 * on the spinner and a <code>PropertyChangeListener</code>
0971                 * on the new <code>JFormattedTextField</code>.
0972                 * 
0973                 * @param spinner the spinner whose model <code>this</code> editor
0974                 *        will monitor
0975                 * @param format <code>DateFormat</code> object that's used to display
0976                 *     and parse the value of the text field.
0977                 * @exception IllegalArgumentException if the spinners model is not 
0978                 *     an instance of <code>SpinnerDateModel</code>
0979                 * 
0980                 * @see #getModel
0981                 * @see #getFormat
0982                 * @see SpinnerDateModel
0983                 * @see java.text.SimpleDateFormat
0984                 */
0985                private DateEditor(JSpinner spinner, DateFormat format) {
0986                    super (spinner);
0987                    if (!(spinner.getModel() instanceof  SpinnerDateModel)) {
0988                        throw new IllegalArgumentException(
0989                                "model not a SpinnerDateModel");
0990                    }
0991
0992                    SpinnerDateModel model = (SpinnerDateModel) spinner
0993                            .getModel();
0994                    DateFormatter formatter = new DateEditorFormatter(model,
0995                            format);
0996                    DefaultFormatterFactory factory = new DefaultFormatterFactory(
0997                            formatter);
0998                    JFormattedTextField ftf = getTextField();
0999                    ftf.setEditable(true);
1000                    ftf.setFormatterFactory(factory);
1001
1002                    /* TBD - initializing the column width of the text field
1003                     * is imprecise and doing it here is tricky because 
1004                     * the developer may configure the formatter later.
1005                     */
1006                    try {
1007                        String maxString = formatter.valueToString(model
1008                                .getStart());
1009                        String minString = formatter.valueToString(model
1010                                .getEnd());
1011                        ftf.setColumns(Math.max(maxString.length(), minString
1012                                .length()));
1013                    } catch (ParseException e) {
1014                        // PENDING: hmuller
1015                    }
1016                }
1017
1018                /**
1019                 * Returns the <code>java.text.SimpleDateFormat</code> object the
1020                 * <code>JFormattedTextField</code> uses to parse and format
1021                 * numbers.  
1022                 * 
1023                 * @return the value of <code>getTextField().getFormatter().getFormat()</code>.
1024                 * @see #getTextField
1025                 * @see java.text.SimpleDateFormat
1026                 */
1027                public SimpleDateFormat getFormat() {
1028                    return (SimpleDateFormat) ((DateFormatter) (getTextField()
1029                            .getFormatter())).getFormat();
1030                }
1031
1032                /**
1033                 * Return our spinner ancestor's <code>SpinnerDateModel</code>.
1034                 * 
1035                 * @return <code>getSpinner().getModel()</code>
1036                 * @see #getSpinner
1037                 * @see #getTextField
1038                 */
1039                public SpinnerDateModel getModel() {
1040                    return (SpinnerDateModel) (getSpinner().getModel());
1041                }
1042            }
1043
1044            /**
1045             * This subclass of javax.swing.NumberFormatter maps the minimum/maximum
1046             * properties to a SpinnerNumberModel and initializes the valueClass
1047             * of the NumberFormatter to match the type of the initial models value.
1048             */
1049            private static class NumberEditorFormatter extends NumberFormatter {
1050                private final SpinnerNumberModel model;
1051
1052                NumberEditorFormatter(SpinnerNumberModel model,
1053                        NumberFormat format) {
1054                    super (format);
1055                    this .model = model;
1056                    setValueClass(model.getValue().getClass());
1057                }
1058
1059                public void setMinimum(Comparable min) {
1060                    model.setMinimum(min);
1061                }
1062
1063                public Comparable getMinimum() {
1064                    return model.getMinimum();
1065                }
1066
1067                public void setMaximum(Comparable max) {
1068                    model.setMaximum(max);
1069                }
1070
1071                public Comparable getMaximum() {
1072                    return model.getMaximum();
1073                }
1074            }
1075
1076            /**
1077             * An editor for a <code>JSpinner</code> whose model is a 
1078             * <code>SpinnerNumberModel</code>.  The value of the editor is 
1079             * displayed with a <code>JFormattedTextField</code> whose format 
1080             * is defined by a <code>NumberFormatter</code> instance whose
1081             * <code>minimum</code> and <code>maximum</code> properties
1082             * are mapped to the <code>SpinnerNumberModel</code>.
1083             * @since 1.4
1084             */
1085            // PENDING(hmuller): more example javadoc
1086            public static class NumberEditor extends DefaultEditor {
1087                // This is here until DecimalFormat gets a constructor that
1088                // takes a Locale: 4923525
1089                private static String getDefaultPattern(Locale locale) {
1090                    // Get the pattern for the default locale.
1091                    ResourceBundle rb = LocaleData.getNumberFormatData(locale);
1092                    String[] all = rb.getStringArray("NumberPatterns");
1093                    return all[0];
1094                }
1095
1096                /**
1097                 * Construct a <code>JSpinner</code> editor that supports displaying
1098                 * and editing the value of a <code>SpinnerNumberModel</code> 
1099                 * with a <code>JFormattedTextField</code>.  <code>This</code>
1100                 * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
1101                 * on the spinner and a <code>PropertyChangeListener</code>
1102                 * on the new <code>JFormattedTextField</code>.
1103                 * 
1104                 * @param spinner the spinner whose model <code>this</code> editor will monitor
1105                 * @exception IllegalArgumentException if the spinners model is not 
1106                 *     an instance of <code>SpinnerNumberModel</code>
1107                 * 
1108                 * @see #getModel
1109                 * @see #getFormat
1110                 * @see SpinnerNumberModel
1111                 */
1112                public NumberEditor(JSpinner spinner) {
1113                    this (spinner, getDefaultPattern(spinner.getLocale()));
1114                }
1115
1116                /**
1117                 * Construct a <code>JSpinner</code> editor that supports displaying
1118                 * and editing the value of a <code>SpinnerNumberModel</code> 
1119                 * with a <code>JFormattedTextField</code>.  <code>This</code>
1120                 * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
1121                 * on the spinner and a <code>PropertyChangeListener</code>
1122                 * on the new <code>JFormattedTextField</code>.
1123                 * 
1124                 * @param spinner the spinner whose model <code>this</code> editor will monitor
1125                 * @param decimalFormatPattern the initial pattern for the 
1126                 *     <code>DecimalFormat</code> object that's used to display
1127                 *     and parse the value of the text field.
1128                 * @exception IllegalArgumentException if the spinners model is not 
1129                 *     an instance of <code>SpinnerNumberModel</code> or if
1130                 *     <code>decimalFormatPattern</code> is not a legal
1131                 *     argument to <code>DecimalFormat</code>
1132                 * 
1133                 * @see #getTextField
1134                 * @see SpinnerNumberModel
1135                 * @see java.text.DecimalFormat
1136                 */
1137                public NumberEditor(JSpinner spinner,
1138                        String decimalFormatPattern) {
1139                    this (spinner, new DecimalFormat(decimalFormatPattern));
1140                }
1141
1142                /**
1143                 * Construct a <code>JSpinner</code> editor that supports displaying
1144                 * and editing the value of a <code>SpinnerNumberModel</code> 
1145                 * with a <code>JFormattedTextField</code>.  <code>This</code>
1146                 * <code>NumberEditor</code> becomes both a <code>ChangeListener</code>
1147                 * on the spinner and a <code>PropertyChangeListener</code>
1148                 * on the new <code>JFormattedTextField</code>.
1149                 * 
1150                 * @param spinner the spinner whose model <code>this</code> editor will monitor
1151                 * @param decimalFormatPattern the initial pattern for the 
1152                 *     <code>DecimalFormat</code> object that's used to display
1153                 *     and parse the value of the text field.
1154                 * @exception IllegalArgumentException if the spinners model is not 
1155                 *     an instance of <code>SpinnerNumberModel</code>
1156                 * 
1157                 * @see #getTextField
1158                 * @see SpinnerNumberModel
1159                 * @see java.text.DecimalFormat
1160                 */
1161                private NumberEditor(JSpinner spinner, DecimalFormat format) {
1162                    super (spinner);
1163                    if (!(spinner.getModel() instanceof  SpinnerNumberModel)) {
1164                        throw new IllegalArgumentException(
1165                                "model not a SpinnerNumberModel");
1166                    }
1167
1168                    SpinnerNumberModel model = (SpinnerNumberModel) spinner
1169                            .getModel();
1170                    NumberFormatter formatter = new NumberEditorFormatter(
1171                            model, format);
1172                    DefaultFormatterFactory factory = new DefaultFormatterFactory(
1173                            formatter);
1174                    JFormattedTextField ftf = getTextField();
1175                    ftf.setEditable(true);
1176                    ftf.setFormatterFactory(factory);
1177                    ftf.setHorizontalAlignment(JTextField.RIGHT);
1178
1179                    /* TBD - initializing the column width of the text field
1180                     * is imprecise and doing it here is tricky because 
1181                     * the developer may configure the formatter later.
1182                     */
1183                    try {
1184                        String maxString = formatter.valueToString(model
1185                                .getMinimum());
1186                        String minString = formatter.valueToString(model
1187                                .getMaximum());
1188                        ftf.setColumns(Math.max(maxString.length(), minString
1189                                .length()));
1190                    } catch (ParseException e) {
1191                        // TBD should throw a chained error here
1192                    }
1193
1194                }
1195
1196                /**
1197                 * Returns the <code>java.text.DecimalFormat</code> object the
1198                 * <code>JFormattedTextField</code> uses to parse and format
1199                 * numbers.  
1200                 * 
1201                 * @return the value of <code>getTextField().getFormatter().getFormat()</code>.
1202                 * @see #getTextField
1203                 * @see java.text.DecimalFormat
1204                 */
1205                public DecimalFormat getFormat() {
1206                    return (DecimalFormat) ((NumberFormatter) (getTextField()
1207                            .getFormatter())).getFormat();
1208                }
1209
1210                /**
1211                 * Return our spinner ancestor's <code>SpinnerNumberModel</code>.
1212                 * 
1213                 * @return <code>getSpinner().getModel()</code>
1214                 * @see #getSpinner
1215                 * @see #getTextField
1216                 */
1217                public SpinnerNumberModel getModel() {
1218                    return (SpinnerNumberModel) (getSpinner().getModel());
1219                }
1220            }
1221
1222            /**
1223             * An editor for a <code>JSpinner</code> whose model is a 
1224             * <code>SpinnerListModel</code>.  
1225             * @since 1.4
1226             */
1227            public static class ListEditor extends DefaultEditor {
1228                /**
1229                 * Construct a <code>JSpinner</code> editor that supports displaying
1230                 * and editing the value of a <code>SpinnerListModel</code> 
1231                 * with a <code>JFormattedTextField</code>.  <code>This</code>
1232                 * <code>ListEditor</code> becomes both a <code>ChangeListener</code>
1233                 * on the spinner and a <code>PropertyChangeListener</code>
1234                 * on the new <code>JFormattedTextField</code>.
1235                 * 
1236                 * @param spinner the spinner whose model <code>this</code> editor will monitor
1237                 * @exception IllegalArgumentException if the spinners model is not 
1238                 *     an instance of <code>SpinnerListModel</code>
1239                 * 
1240                 * @see #getModel
1241                 * @see SpinnerListModel
1242                 */
1243                public ListEditor(JSpinner spinner) {
1244                    super (spinner);
1245                    if (!(spinner.getModel() instanceof  SpinnerListModel)) {
1246                        throw new IllegalArgumentException(
1247                                "model not a SpinnerListModel");
1248                    }
1249                    getTextField().setEditable(true);
1250                    getTextField().setFormatterFactory(
1251                            new DefaultFormatterFactory(new ListFormatter()));
1252                }
1253
1254                /**
1255                 * Return our spinner ancestor's <code>SpinnerNumberModel</code>.
1256                 * 
1257                 * @return <code>getSpinner().getModel()</code>
1258                 * @see #getSpinner
1259                 * @see #getTextField
1260                 */
1261                public SpinnerListModel getModel() {
1262                    return (SpinnerListModel) (getSpinner().getModel());
1263                }
1264
1265                /**
1266                 * ListFormatter provides completion while text is being input
1267                 * into the JFormattedTextField. Completion is only done if the
1268                 * user is inserting text at the end of the document. Completion
1269                 * is done by way of the SpinnerListModel method findNextMatch.
1270                 */
1271                private class ListFormatter extends
1272                        JFormattedTextField.AbstractFormatter {
1273                    private DocumentFilter filter;
1274
1275                    public String valueToString(Object value)
1276                            throws ParseException {
1277                        if (value == null) {
1278                            return "";
1279                        }
1280                        return value.toString();
1281                    }
1282
1283                    public Object stringToValue(String string)
1284                            throws ParseException {
1285                        return string;
1286                    }
1287
1288                    protected DocumentFilter getDocumentFilter() {
1289                        if (filter == null) {
1290                            filter = new Filter();
1291                        }
1292                        return filter;
1293                    }
1294
1295                    private class Filter extends DocumentFilter {
1296                        public void replace(FilterBypass fb, int offset,
1297                                int length, String string, AttributeSet attrs)
1298                                throws BadLocationException {
1299                            if (string != null
1300                                    && (offset + length) == fb.getDocument()
1301                                            .getLength()) {
1302                                Object next = getModel().findNextMatch(
1303                                        fb.getDocument().getText(0, offset)
1304                                                + string);
1305                                String value = (next != null) ? next.toString()
1306                                        : null;
1307
1308                                if (value != null) {
1309                                    fb.remove(0, offset + length);
1310                                    fb.insertString(0, value, null);
1311                                    getFormattedTextField().select(
1312                                            offset + string.length(),
1313                                            value.length());
1314                                    return;
1315                                }
1316                            }
1317                            super .replace(fb, offset, length, string, attrs);
1318                        }
1319
1320                        public void insertString(FilterBypass fb, int offset,
1321                                String string, AttributeSet attr)
1322                                throws BadLocationException {
1323                            replace(fb, offset, 0, string, attr);
1324                        }
1325                    }
1326                }
1327            }
1328
1329            /**
1330             * An Action implementation that is always disabled.
1331             */
1332            private static class DisabledAction implements  Action {
1333                public Object getValue(String key) {
1334                    return null;
1335                }
1336
1337                public void putValue(String key, Object value) {
1338                }
1339
1340                public void setEnabled(boolean b) {
1341                }
1342
1343                public boolean isEnabled() {
1344                    return false;
1345                }
1346
1347                public void addPropertyChangeListener(PropertyChangeListener l) {
1348                }
1349
1350                public void removePropertyChangeListener(
1351                        PropertyChangeListener l) {
1352                }
1353
1354                public void actionPerformed(ActionEvent ae) {
1355                }
1356            }
1357
1358            /////////////////
1359            // Accessibility support
1360            ////////////////
1361
1362            /**
1363             * Gets the <code>AccessibleContext</code> for the <code>JSpinner</code>
1364             *
1365             * @return the <code>AccessibleContext</code> for the <code>JSpinner</code>
1366             * @since 1.5 
1367             */
1368            public AccessibleContext getAccessibleContext() {
1369                if (accessibleContext == null) {
1370                    accessibleContext = new AccessibleJSpinner();
1371                }
1372                return accessibleContext;
1373            }
1374
1375            /**
1376             * <code>AccessibleJSpinner</code> implements accessibility 
1377             * support for the <code>JSpinner</code> class. 
1378             * @since 1.5 
1379             */
1380            protected class AccessibleJSpinner extends AccessibleJComponent
1381                    implements  AccessibleValue, AccessibleAction,
1382                    AccessibleText, AccessibleEditableText, ChangeListener {
1383
1384                private Object oldModelValue = null;
1385
1386                /**
1387                 * AccessibleJSpinner constructor
1388                 */
1389                protected AccessibleJSpinner() {
1390                    // model is guaranteed to be non-null
1391                    oldModelValue = model.getValue();
1392                    JSpinner.this .addChangeListener(this );
1393                }
1394
1395                /**
1396                 * Invoked when the target of the listener has changed its state.
1397                 *
1398                 * @param e  a <code>ChangeEvent</code> object. Must not be null.
1399                 * @throws NullPointerException if the parameter is null.
1400                 */
1401                public void stateChanged(ChangeEvent e) {
1402                    if (e == null) {
1403                        throw new NullPointerException();
1404                    }
1405                    Object newModelValue = model.getValue();
1406                    firePropertyChange(ACCESSIBLE_VALUE_PROPERTY,
1407                            oldModelValue, newModelValue);
1408                    firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, 0); // entire text may have changed
1409
1410                    oldModelValue = newModelValue;
1411                }
1412
1413                /* ===== Begin AccessibleContext methods ===== */
1414
1415                /**
1416                 * Gets the role of this object.  The role of the object is the generic
1417                 * purpose or use of the class of this object.  For example, the role
1418                 * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in 
1419                 * AccessibleRole are provided so component developers can pick from
1420                 * a set of predefined roles.  This enables assistive technologies to
1421                 * provide a consistent interface to various tweaked subclasses of 
1422                 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
1423                 * that act like a push button) as well as distinguish between sublasses
1424                 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
1425                 * and AccessibleRole.RADIO_BUTTON for radio buttons).
1426                 * <p>Note that the AccessibleRole class is also extensible, so 
1427                 * custom component developers can define their own AccessibleRole's
1428                 * if the set of predefined roles is inadequate.
1429                 *
1430                 * @return an instance of AccessibleRole describing the role of the object
1431                 * @see AccessibleRole
1432                 */
1433                public AccessibleRole getAccessibleRole() {
1434                    return AccessibleRole.SPIN_BOX;
1435                }
1436
1437                /**
1438                 * Returns the number of accessible children of the object.
1439                 *
1440                 * @return the number of accessible children of the object.
1441                 */
1442                public int getAccessibleChildrenCount() {
1443                    // the JSpinner has one child, the editor
1444                    if (editor.getAccessibleContext() != null) {
1445                        return 1;
1446                    }
1447                    return 0;
1448                }
1449
1450                /**
1451                 * Returns the specified Accessible child of the object.  The Accessible
1452                 * children of an Accessible object are zero-based, so the first child 
1453                 * of an Accessible child is at index 0, the second child is at index 1,
1454                 * and so on.
1455                 *
1456                 * @param i zero-based index of child
1457                 * @return the Accessible child of the object
1458                 * @see #getAccessibleChildrenCount
1459                 */
1460                public Accessible getAccessibleChild(int i) {
1461                    // the JSpinner has one child, the editor
1462                    if (i != 0) {
1463                        return null;
1464                    }
1465                    if (editor.getAccessibleContext() != null) {
1466                        return (Accessible) editor;
1467                    }
1468                    return null;
1469                }
1470
1471                /* ===== End AccessibleContext methods ===== */
1472
1473                /**
1474                 * Gets the AccessibleAction associated with this object that supports
1475                 * one or more actions. 
1476                 *
1477                 * @return AccessibleAction if supported by object; else return null
1478                 * @see AccessibleAction
1479                 */
1480                public AccessibleAction getAccessibleAction() {
1481                    return this ;
1482                }
1483
1484                /**
1485                 * Gets the AccessibleText associated with this object presenting 
1486                 * text on the display.
1487                 *
1488                 * @return AccessibleText if supported by object; else return null
1489                 * @see AccessibleText
1490                 */
1491                public AccessibleText getAccessibleText() {
1492                    return this ;
1493                }
1494
1495                /*
1496                 * Returns the AccessibleContext for the JSpinner editor
1497                 */
1498                private AccessibleContext getEditorAccessibleContext() {
1499                    if (editor instanceof  DefaultEditor) {
1500                        JTextField textField = ((DefaultEditor) editor)
1501                                .getTextField();
1502                        if (textField != null) {
1503                            return textField.getAccessibleContext();
1504                        }
1505                    } else if (editor instanceof  Accessible) {
1506                        return ((Accessible) editor).getAccessibleContext();
1507                    }
1508                    return null;
1509                }
1510
1511                /*
1512                 * Returns the AccessibleText for the JSpinner editor
1513                 */
1514                private AccessibleText getEditorAccessibleText() {
1515                    AccessibleContext ac = getEditorAccessibleContext();
1516                    if (ac != null) {
1517                        return ac.getAccessibleText();
1518                    }
1519                    return null;
1520                }
1521
1522                /*
1523                 * Returns the AccessibleEditableText for the JSpinner editor
1524                 */
1525                private AccessibleEditableText getEditorAccessibleEditableText() {
1526                    AccessibleText at = getEditorAccessibleText();
1527                    if (at instanceof  AccessibleEditableText) {
1528                        return (AccessibleEditableText) at;
1529                    }
1530                    return null;
1531                }
1532
1533                /**
1534                 * Gets the AccessibleValue associated with this object. 
1535                 * 
1536                 * @return AccessibleValue if supported by object; else return null 
1537                 * @see AccessibleValue
1538                 *
1539                 */
1540                public AccessibleValue getAccessibleValue() {
1541                    return this ;
1542                }
1543
1544                /* ===== Begin AccessibleValue impl ===== */
1545
1546                /**
1547                 * Get the value of this object as a Number.  If the value has not been
1548                 * set, the return value will be null.
1549                 *
1550                 * @return value of the object
1551                 * @see #setCurrentAccessibleValue
1552                 */
1553                public Number getCurrentAccessibleValue() {
1554                    Object o = model.getValue();
1555                    if (o instanceof  Number) {
1556                        return (Number) o;
1557                    }
1558                    return null;
1559                }
1560
1561                /**
1562                 * Set the value of this object as a Number.
1563                 *
1564                 * @param n the value to set for this object
1565                 * @return true if the value was set; else False
1566                 * @see #getCurrentAccessibleValue
1567                 */
1568                public boolean setCurrentAccessibleValue(Number n) {
1569                    // try to set the new value
1570                    try {
1571                        model.setValue(n);
1572                        return true;
1573                    } catch (IllegalArgumentException iae) {
1574                        // SpinnerModel didn't like new value
1575                    }
1576                    return false;
1577                }
1578
1579                /**
1580                 * Get the minimum value of this object as a Number.
1581                 *
1582                 * @return Minimum value of the object; null if this object does not 
1583                 * have a minimum value
1584                 * @see #getMaximumAccessibleValue
1585                 */
1586                public Number getMinimumAccessibleValue() {
1587                    if (model instanceof  SpinnerNumberModel) {
1588                        SpinnerNumberModel numberModel = (SpinnerNumberModel) model;
1589                        Object o = numberModel.getMinimum();
1590                        if (o instanceof  Number) {
1591                            return (Number) o;
1592                        }
1593                    }
1594                    return null;
1595                }
1596
1597                /**
1598                 * Get the maximum value of this object as a Number.
1599                 *
1600                 * @return Maximum value of the object; null if this object does not 
1601                 * have a maximum value
1602                 * @see #getMinimumAccessibleValue
1603                 */
1604                public Number getMaximumAccessibleValue() {
1605                    if (model instanceof  SpinnerNumberModel) {
1606                        SpinnerNumberModel numberModel = (SpinnerNumberModel) model;
1607                        Object o = numberModel.getMaximum();
1608                        if (o instanceof  Number) {
1609                            return (Number) o;
1610                        }
1611                    }
1612                    return null;
1613                }
1614
1615                /* ===== End AccessibleValue impl ===== */
1616
1617                /* ===== Begin AccessibleAction impl ===== */
1618
1619                /**
1620                 * Returns the number of accessible actions available in this object
1621                 * If there are more than one, the first one is considered the "default"
1622                 * action of the object.
1623                 *
1624                 * Two actions are supported: AccessibleAction.INCREMENT which
1625                 * increments the spinner value and AccessibleAction.DECREMENT 
1626                 * which decrements the spinner value
1627                 *
1628                 * @return the zero-based number of Actions in this object
1629                 */
1630                public int getAccessibleActionCount() {
1631                    return 2;
1632                }
1633
1634                /**
1635                 * Returns a description of the specified action of the object.
1636                 *
1637                 * @param i zero-based index of the actions
1638                 * @return a String description of the action
1639                 * @see #getAccessibleActionCount
1640                 */
1641                public String getAccessibleActionDescription(int i) {
1642                    if (i == 0) {
1643                        return AccessibleAction.INCREMENT;
1644                    } else if (i == 1) {
1645                        return AccessibleAction.DECREMENT;
1646                    }
1647                    return null;
1648                }
1649
1650                /**
1651                 * Performs the specified Action on the object
1652                 *
1653                 * @param i zero-based index of actions. The first action
1654                 * (index 0) is AccessibleAction.INCREMENT and the second
1655                 * action (index 1) is AccessibleAction.DECREMENT.
1656                 * @return true if the action was performed; otherwise false.
1657                 * @see #getAccessibleActionCount
1658                 */
1659                public boolean doAccessibleAction(int i) {
1660                    if (i < 0 || i > 1) {
1661                        return false;
1662                    }
1663                    Object o = null;
1664                    if (i == 0) {
1665                        o = getNextValue(); // AccessibleAction.INCREMENT
1666                    } else {
1667                        o = getPreviousValue(); // AccessibleAction.DECREMENT
1668                    }
1669                    // try to set the new value
1670                    try {
1671                        model.setValue(o);
1672                        return true;
1673                    } catch (IllegalArgumentException iae) {
1674                        // SpinnerModel didn't like new value
1675                    }
1676                    return false;
1677                }
1678
1679                /* ===== End AccessibleAction impl ===== */
1680
1681                /* ===== Begin AccessibleText impl ===== */
1682
1683                /*
1684                 * Returns whether source and destination components have the
1685                 * same window ancestor
1686                 */
1687                private boolean sameWindowAncestor(Component src, Component dest) {
1688                    if (src == null || dest == null) {
1689                        return false;
1690                    }
1691                    return SwingUtilities.getWindowAncestor(src) == SwingUtilities
1692                            .getWindowAncestor(dest);
1693                }
1694
1695                /**
1696                 * Given a point in local coordinates, return the zero-based index
1697                 * of the character under that Point.  If the point is invalid,
1698                 * this method returns -1.
1699                 *
1700                 * @param p the Point in local coordinates
1701                 * @return the zero-based index of the character under Point p; if 
1702                 * Point is invalid return -1.
1703                 */
1704                public int getIndexAtPoint(Point p) {
1705                    AccessibleText at = getEditorAccessibleText();
1706                    if (at != null && sameWindowAncestor(JSpinner.this , editor)) {
1707                        // convert point from the JSpinner bounds (source) to 
1708                        // editor bounds (destination)
1709                        Point editorPoint = SwingUtilities.convertPoint(
1710                                JSpinner.this , p, editor);
1711                        if (editorPoint != null) {
1712                            return at.getIndexAtPoint(editorPoint);
1713                        }
1714                    }
1715                    return -1;
1716                }
1717
1718                /**
1719                 * Determines the bounding box of the character at the given 
1720                 * index into the string.  The bounds are returned in local
1721                 * coordinates.  If the index is invalid an empty rectangle is 
1722                 * returned.
1723                 *
1724                 * @param i the index into the String
1725                 * @return the screen coordinates of the character's bounding box,
1726                 * if index is invalid return an empty rectangle.
1727                 */
1728                public Rectangle getCharacterBounds(int i) {
1729                    AccessibleText at = getEditorAccessibleText();
1730                    if (at != null) {
1731                        Rectangle editorRect = at.getCharacterBounds(i);
1732                        if (editorRect != null
1733                                && sameWindowAncestor(JSpinner.this , editor)) {
1734                            // return rectangle in the the JSpinner bounds
1735                            return SwingUtilities.convertRectangle(editor,
1736                                    editorRect, JSpinner.this );
1737                        }
1738                    }
1739                    return null;
1740                }
1741
1742                /**
1743                 * Returns the number of characters (valid indicies) 
1744                 *
1745                 * @return the number of characters
1746                 */
1747                public int getCharCount() {
1748                    AccessibleText at = getEditorAccessibleText();
1749                    if (at != null) {
1750                        return at.getCharCount();
1751                    }
1752                    return -1;
1753                }
1754
1755                /**
1756                 * Returns the zero-based offset of the caret.
1757                 *
1758                 * Note: That to the right of the caret will have the same index
1759                 * value as the offset (the caret is between two characters).
1760                 * @return the zero-based offset of the caret.
1761                 */
1762                public int getCaretPosition() {
1763                    AccessibleText at = getEditorAccessibleText();
1764                    if (at != null) {
1765                        return at.getCaretPosition();
1766                    }
1767                    return -1;
1768                }
1769
1770                /**
1771                 * Returns the String at a given index. 
1772                 *
1773                 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
1774                 * @param index an index within the text
1775                 * @return the letter, word, or sentence
1776                 */
1777                public String getAtIndex(int part, int index) {
1778                    AccessibleText at = getEditorAccessibleText();
1779                    if (at != null) {
1780                        return at.getAtIndex(part, index);
1781                    }
1782                    return null;
1783                }
1784
1785                /**
1786                 * Returns the String after a given index.
1787                 *
1788                 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
1789                 * @param index an index within the text
1790                 * @return the letter, word, or sentence
1791                 */
1792                public String getAfterIndex(int part, int index) {
1793                    AccessibleText at = getEditorAccessibleText();
1794                    if (at != null) {
1795                        return at.getAfterIndex(part, index);
1796                    }
1797                    return null;
1798                }
1799
1800                /**
1801                 * Returns the String before a given index.
1802                 *
1803                 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
1804                 * @param index an index within the text
1805                 * @return the letter, word, or sentence
1806                 */
1807                public String getBeforeIndex(int part, int index) {
1808                    AccessibleText at = getEditorAccessibleText();
1809                    if (at != null) {
1810                        return at.getBeforeIndex(part, index);
1811                    }
1812                    return null;
1813                }
1814
1815                /**
1816                 * Returns the AttributeSet for a given character at a given index
1817                 *
1818                 * @param i the zero-based index into the text 
1819                 * @return the AttributeSet of the character
1820                 */
1821                public AttributeSet getCharacterAttribute(int i) {
1822                    AccessibleText at = getEditorAccessibleText();
1823                    if (at != null) {
1824                        return at.getCharacterAttribute(i);
1825                    }
1826                    return null;
1827                }
1828
1829                /**
1830                 * Returns the start offset within the selected text.
1831                 * If there is no selection, but there is
1832                 * a caret, the start and end offsets will be the same.
1833                 *
1834                 * @return the index into the text of the start of the selection
1835                 */
1836                public int getSelectionStart() {
1837                    AccessibleText at = getEditorAccessibleText();
1838                    if (at != null) {
1839                        return at.getSelectionStart();
1840                    }
1841                    return -1;
1842                }
1843
1844                /**
1845                 * Returns the end offset within the selected text.
1846                 * If there is no selection, but there is
1847                 * a caret, the start and end offsets will be the same.
1848                 *
1849                 * @return the index into teh text of the end of the selection
1850                 */
1851                public int getSelectionEnd() {
1852                    AccessibleText at = getEditorAccessibleText();
1853                    if (at != null) {
1854                        return at.getSelectionEnd();
1855                    }
1856                    return -1;
1857                }
1858
1859                /**
1860                 * Returns the portion of the text that is selected. 
1861                 *
1862                 * @return the String portion of the text that is selected
1863                 */
1864                public String getSelectedText() {
1865                    AccessibleText at = getEditorAccessibleText();
1866                    if (at != null) {
1867                        return at.getSelectedText();
1868                    }
1869                    return null;
1870                }
1871
1872                /* ===== End AccessibleText impl ===== */
1873
1874                /* ===== Begin AccessibleEditableText impl ===== */
1875
1876                /**
1877                 * Sets the text contents to the specified string.
1878                 *
1879                 * @param s the string to set the text contents
1880                 */
1881                public void setTextContents(String s) {
1882                    AccessibleEditableText at = getEditorAccessibleEditableText();
1883                    if (at != null) {
1884                        at.setTextContents(s);
1885                    }
1886                }
1887
1888                /**
1889                 * Inserts the specified string at the given index/
1890                 *
1891                 * @param index the index in the text where the string will 
1892                 * be inserted
1893                 * @param s the string to insert in the text
1894                 */
1895                public void insertTextAtIndex(int index, String s) {
1896                    AccessibleEditableText at = getEditorAccessibleEditableText();
1897                    if (at != null) {
1898                        at.insertTextAtIndex(index, s);
1899                    }
1900                }
1901
1902                /**
1903                 * Returns the text string between two indices.
1904                 * 
1905                 * @param startIndex the starting index in the text
1906                 * @param endIndex the ending index in the text
1907                 * @return the text string between the indices
1908                 */
1909                public String getTextRange(int startIndex, int endIndex) {
1910                    AccessibleEditableText at = getEditorAccessibleEditableText();
1911                    if (at != null) {
1912                        return at.getTextRange(startIndex, endIndex);
1913                    }
1914                    return null;
1915                }
1916
1917                /**
1918                 * Deletes the text between two indices
1919                 *
1920                 * @param startIndex the starting index in the text
1921                 * @param endIndex the ending index in the text
1922                 */
1923                public void delete(int startIndex, int endIndex) {
1924                    AccessibleEditableText at = getEditorAccessibleEditableText();
1925                    if (at != null) {
1926                        at.delete(startIndex, endIndex);
1927                    }
1928                }
1929
1930                /**
1931                 * Cuts the text between two indices into the system clipboard.
1932                 *
1933                 * @param startIndex the starting index in the text
1934                 * @param endIndex the ending index in the text
1935                 */
1936                public void cut(int startIndex, int endIndex) {
1937                    AccessibleEditableText at = getEditorAccessibleEditableText();
1938                    if (at != null) {
1939                        at.cut(startIndex, endIndex);
1940                    }
1941                }
1942
1943                /**
1944                 * Pastes the text from the system clipboard into the text
1945                 * starting at the specified index.
1946                 *
1947                 * @param startIndex the starting index in the text
1948                 */
1949                public void paste(int startIndex) {
1950                    AccessibleEditableText at = getEditorAccessibleEditableText();
1951                    if (at != null) {
1952                        at.paste(startIndex);
1953                    }
1954                }
1955
1956                /**
1957                 * Replaces the text between two indices with the specified
1958                 * string.
1959                 *
1960                 * @param startIndex the starting index in the text
1961                 * @param endIndex the ending index in the text
1962                 * @param s the string to replace the text between two indices
1963                 */
1964                public void replaceText(int startIndex, int endIndex, String s) {
1965                    AccessibleEditableText at = getEditorAccessibleEditableText();
1966                    if (at != null) {
1967                        at.replaceText(startIndex, endIndex, s);
1968                    }
1969                }
1970
1971                /**
1972                 * Selects the text between two indices.
1973                 *
1974                 * @param startIndex the starting index in the text
1975                 * @param endIndex the ending index in the text
1976                 */
1977                public void selectText(int startIndex, int endIndex) {
1978                    AccessibleEditableText at = getEditorAccessibleEditableText();
1979                    if (at != null) {
1980                        at.selectText(startIndex, endIndex);
1981                    }
1982                }
1983
1984                /**
1985                 * Sets attributes for the text between two indices.
1986                 *
1987                 * @param startIndex the starting index in the text
1988                 * @param endIndex the ending index in the text
1989                 * @param as the attribute set
1990                 * @see AttributeSet
1991                 */
1992                public void setAttributes(int startIndex, int endIndex,
1993                        AttributeSet as) {
1994                    AccessibleEditableText at = getEditorAccessibleEditableText();
1995                    if (at != null) {
1996                        at.setAttributes(startIndex, endIndex, as);
1997                    }
1998                }
1999            } /* End AccessibleJSpinner */
2000        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.