summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/JSpinner.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/JSpinner.java')
-rw-r--r--libjava/classpath/javax/swing/JSpinner.java754
1 files changed, 754 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/JSpinner.java b/libjava/classpath/javax/swing/JSpinner.java
new file mode 100644
index 000000000..e942caead
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSpinner.java
@@ -0,0 +1,754 @@
+/* JSpinner.java --
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.SpinnerUI;
+import javax.swing.text.DateFormatter;
+import javax.swing.text.DefaultFormatterFactory;
+import javax.swing.text.NumberFormatter;
+
+/**
+ * A <code>JSpinner</code> is a component that displays a single value from
+ * a sequence of values, and provides a convenient means for selecting the
+ * previous and next values in the sequence. Typically the spinner displays
+ * a numeric value, but it is possible to display dates or arbitrary items
+ * from a list.
+ *
+ * @author Ka-Hing Cheung
+ *
+ * @since 1.4
+ */
+public class JSpinner extends JComponent
+{
+ /**
+ * The base class for the editor used by the {@link JSpinner} component.
+ * The editor is in fact a panel containing a {@link JFormattedTextField}
+ * component.
+ */
+ public static class DefaultEditor
+ extends JPanel
+ implements ChangeListener, PropertyChangeListener, LayoutManager
+ {
+ /** The spinner that the editor is allocated to. */
+ private JSpinner spinner;
+
+ /** The JFormattedTextField that backs the editor. */
+ JFormattedTextField ftf;
+
+ /**
+ * For compatability with Sun's JDK 1.4.2 rev. 5
+ */
+ private static final long serialVersionUID = -5317788736173368172L;
+
+ /**
+ * Creates a new <code>DefaultEditor</code> object. The editor is
+ * registered with the spinner as a {@link ChangeListener} here.
+ *
+ * @param spinner the <code>JSpinner</code> associated with this editor
+ */
+ public DefaultEditor(JSpinner spinner)
+ {
+ super();
+ setLayout(this);
+ this.spinner = spinner;
+ ftf = new JFormattedTextField();
+ add(ftf);
+ ftf.setValue(spinner.getValue());
+ ftf.addPropertyChangeListener(this);
+ if (getComponentOrientation().isLeftToRight())
+ ftf.setHorizontalAlignment(JTextField.RIGHT);
+ else
+ ftf.setHorizontalAlignment(JTextField.LEFT);
+ spinner.addChangeListener(this);
+ }
+
+ /**
+ * Returns the <code>JSpinner</code> component that the editor is assigned
+ * to.
+ *
+ * @return The spinner that the editor is assigned to.
+ */
+ public JSpinner getSpinner()
+ {
+ return spinner;
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ public void commitEdit() throws ParseException
+ {
+ // TODO: Implement this properly.
+ }
+
+ /**
+ * Removes the editor from the {@link ChangeListener} list maintained by
+ * the specified <code>spinner</code>.
+ *
+ * @param spinner the spinner (<code>null</code> not permitted).
+ */
+ public void dismiss(JSpinner spinner)
+ {
+ spinner.removeChangeListener(this);
+ }
+
+ /**
+ * Returns the text field used to display and edit the current value in
+ * the spinner.
+ *
+ * @return The text field.
+ */
+ public JFormattedTextField getTextField()
+ {
+ return ftf;
+ }
+
+ /**
+ * Sets the bounds for the child components in this container. In this
+ * case, the text field is the only component to be laid out.
+ *
+ * @param parent the parent container.
+ */
+ public void layoutContainer(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension size = getSize();
+ ftf.setBounds(insets.left, insets.top,
+ size.width - insets.left - insets.right,
+ size.height - insets.top - insets.bottom);
+ }
+
+ /**
+ * Calculates the minimum size for this component. In this case, the
+ * text field is the only subcomponent, so the return value is the minimum
+ * size of the text field plus the insets of this component.
+ *
+ * @param parent the parent container.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension minSize = ftf.getMinimumSize();
+ return new Dimension(minSize.width + insets.left + insets.right,
+ minSize.height + insets.top + insets.bottom);
+ }
+
+ /**
+ * Calculates the preferred size for this component. In this case, the
+ * text field is the only subcomponent, so the return value is the
+ * preferred size of the text field plus the insets of this component.
+ *
+ * @param parent the parent container.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension prefSize = ftf.getPreferredSize();
+ return new Dimension(prefSize.width + insets.left + insets.right,
+ prefSize.height + insets.top + insets.bottom);
+ }
+
+ /**
+ * Receives notification of property changes. If the text field's 'value'
+ * property changes, the spinner's model is updated accordingly.
+ *
+ * @param event the event.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getSource() == ftf)
+ {
+ if (event.getPropertyName().equals("value"))
+ spinner.getModel().setValue(event.getNewValue());
+ }
+ }
+
+ /**
+ * Receives notification of changes in the state of the {@link JSpinner}
+ * that the editor belongs to - the content of the text field is updated
+ * accordingly.
+ *
+ * @param event the change event.
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ ftf.setValue(spinner.getValue());
+ }
+
+ /**
+ * This method does nothing. It is required by the {@link LayoutManager}
+ * interface, but since this component has a single child, there is no
+ * need to use this method.
+ *
+ * @param child the child component to remove.
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method does nothing. It is required by the {@link LayoutManager}
+ * interface, but since this component has a single child, there is no
+ * need to use this method.
+ *
+ * @param name the name.
+ * @param child the child component to add.
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A panel containing a {@link JFormattedTextField} that is configured for
+ * displaying and editing numbers. The panel is used as a subcomponent of
+ * a {@link JSpinner}.
+ *
+ * @see JSpinner#createEditor(SpinnerModel)
+ */
+ public static class NumberEditor extends DefaultEditor
+ {
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 3791956183098282942L;
+
+ /**
+ * Creates a new <code>NumberEditor</code> object for the specified
+ * <code>spinner</code>. The editor is registered with the spinner as a
+ * {@link ChangeListener}.
+ *
+ * @param spinner the component the editor will be used with.
+ */
+ public NumberEditor(JSpinner spinner)
+ {
+ super(spinner);
+ NumberEditorFormatter nef = new NumberEditorFormatter();
+ nef.setMinimum(getModel().getMinimum());
+ nef.setMaximum(getModel().getMaximum());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Creates a new <code>NumberEditor</code> object.
+ *
+ * @param spinner the spinner.
+ * @param decimalFormatPattern the number format pattern.
+ */
+ public NumberEditor(JSpinner spinner, String decimalFormatPattern)
+ {
+ super(spinner);
+ NumberEditorFormatter nef
+ = new NumberEditorFormatter(decimalFormatPattern);
+ nef.setMinimum(getModel().getMinimum());
+ nef.setMaximum(getModel().getMaximum());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Returns the format used by the text field.
+ *
+ * @return The format used by the text field.
+ */
+ public DecimalFormat getFormat()
+ {
+ NumberFormatter formatter = (NumberFormatter) ftf.getFormatter();
+ return (DecimalFormat) formatter.getFormat();
+ }
+
+ /**
+ * Returns the model used by the editor's {@link JSpinner} component,
+ * cast to a {@link SpinnerNumberModel}.
+ *
+ * @return The model.
+ */
+ public SpinnerNumberModel getModel()
+ {
+ return (SpinnerNumberModel) getSpinner().getModel();
+ }
+ }
+
+ static class NumberEditorFormatter
+ extends NumberFormatter
+ {
+ public NumberEditorFormatter()
+ {
+ super(NumberFormat.getInstance());
+ }
+ public NumberEditorFormatter(String decimalFormatPattern)
+ {
+ super(new DecimalFormat(decimalFormatPattern));
+ }
+ }
+
+ /**
+ * A <code>JSpinner</code> editor used for the {@link SpinnerListModel}.
+ * This editor uses a <code>JFormattedTextField</code> to edit the values
+ * of the spinner.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public static class ListEditor extends DefaultEditor
+ {
+ /**
+ * Creates a new instance of <code>ListEditor</code>.
+ *
+ * @param spinner the spinner for which this editor is used
+ */
+ public ListEditor(JSpinner spinner)
+ {
+ super(spinner);
+ }
+
+ /**
+ * Returns the spinner's model cast as a {@link SpinnerListModel}.
+ *
+ * @return The spinner's model.
+ */
+ public SpinnerListModel getModel()
+ {
+ return (SpinnerListModel) getSpinner().getModel();
+ }
+ }
+
+ /**
+ * An editor class for a <code>JSpinner</code> that is used
+ * for displaying and editing dates (e.g. that uses
+ * <code>SpinnerDateModel</code> as model).
+ *
+ * The editor uses a {@link JTextField} with the value
+ * displayed by a {@link DateFormatter} instance.
+ */
+ public static class DateEditor extends DefaultEditor
+ {
+
+ /** The serialVersionUID. */
+ private static final long serialVersionUID = -4279356973770397815L;
+
+ /**
+ * Creates a new instance of DateEditor for the specified
+ * <code>JSpinner</code>.
+ *
+ * @param spinner the <code>JSpinner</code> for which to
+ * create a <code>DateEditor</code> instance
+ */
+ public DateEditor(JSpinner spinner)
+ {
+ super(spinner);
+ DateEditorFormatter nef = new DateEditorFormatter();
+ nef.setMinimum(getModel().getStart());
+ nef.setMaximum(getModel().getEnd());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Creates a new instance of DateEditor for the specified
+ * <code>JSpinner</code> using the specified date format
+ * pattern.
+ *
+ * @param spinner the <code>JSpinner</code> for which to
+ * create a <code>DateEditor</code> instance
+ * @param dateFormatPattern the date format to use
+ *
+ * @see SimpleDateFormat#SimpleDateFormat(String)
+ */
+ public DateEditor(JSpinner spinner, String dateFormatPattern)
+ {
+ super(spinner);
+ DateEditorFormatter nef = new DateEditorFormatter(dateFormatPattern);
+ nef.setMinimum(getModel().getStart());
+ nef.setMaximum(getModel().getEnd());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Returns the <code>SimpleDateFormat</code> instance that is used to
+ * format the date value.
+ *
+ * @return the <code>SimpleDateFormat</code> instance that is used to
+ * format the date value
+ */
+ public SimpleDateFormat getFormat()
+ {
+ DateFormatter formatter = (DateFormatter) ftf.getFormatter();
+ return (SimpleDateFormat) formatter.getFormat();
+ }
+
+ /**
+ * Returns the {@link SpinnerDateModel} that is edited by this editor.
+ *
+ * @return the <code>SpinnerDateModel</code> that is edited by this editor
+ */
+ public SpinnerDateModel getModel()
+ {
+ return (SpinnerDateModel) getSpinner().getModel();
+ }
+ }
+
+ static class DateEditorFormatter
+ extends DateFormatter
+ {
+ public DateEditorFormatter()
+ {
+ super(DateFormat.getInstance());
+ }
+ public DateEditorFormatter(String dateFormatPattern)
+ {
+ super(new SimpleDateFormat(dateFormatPattern));
+ }
+ }
+
+ /**
+ * A listener that forwards {@link ChangeEvent} notifications from the model
+ * to the {@link JSpinner}'s listeners.
+ */
+ class ModelListener implements ChangeListener
+ {
+ /**
+ * Creates a new listener.
+ */
+ public ModelListener()
+ {
+ // nothing to do here
+ }
+
+ /**
+ * Receives notification from the model that its state has changed.
+ *
+ * @param event the event (ignored).
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * The model that defines the current value and permitted values for the
+ * spinner.
+ */
+ private SpinnerModel model;
+
+ /** The current editor. */
+ private JComponent editor;
+
+ private static final long serialVersionUID = 3412663575706551720L;
+
+ /**
+ * Creates a new <code>JSpinner</code> with default instance of
+ * {@link SpinnerNumberModel} (that is, a model with value 0, step size 1,
+ * and no upper or lower limit).
+ *
+ * @see javax.swing.SpinnerNumberModel
+ */
+ public JSpinner()
+ {
+ this(new SpinnerNumberModel());
+ }
+
+ /**
+ * Creates a new <code>JSpinner with the specified model. The
+ * {@link #createEditor(SpinnerModel)} method is used to create an editor
+ * that is suitable for the model.
+ *
+ * @param model the model (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>model</code> is <code>null</code>.
+ */
+ public JSpinner(SpinnerModel model)
+ {
+ this.model = model;
+ this.editor = createEditor(model);
+ model.addChangeListener(new ModelListener());
+ updateUI();
+ }
+
+ /**
+ * If the editor is <code>JSpinner.DefaultEditor</code>, then forwards the
+ * call to it, otherwise do nothing.
+ *
+ * @throws ParseException DOCUMENT ME!
+ */
+ public void commitEdit() throws ParseException
+ {
+ if (editor instanceof DefaultEditor)
+ ((DefaultEditor) editor).commitEdit();
+ }
+
+ /**
+ * Gets the current editor
+ *
+ * @return the current editor
+ *
+ * @see #setEditor
+ */
+ public JComponent getEditor()
+ {
+ return editor;
+ }
+
+ /**
+ * Changes the current editor to the new editor. The old editor is
+ * removed from the spinner's {@link ChangeEvent} list.
+ *
+ * @param editor the new editor (<code>null</code> not permitted.
+ *
+ * @throws IllegalArgumentException if <code>editor</code> is
+ * <code>null</code>.
+ *
+ * @see #getEditor
+ */
+ public void setEditor(JComponent editor)
+ {
+ if (editor == null)
+ throw new IllegalArgumentException("editor may not be null");
+
+ JComponent oldEditor = this.editor;
+ if (oldEditor instanceof DefaultEditor)
+ ((DefaultEditor) oldEditor).dismiss(this);
+ else if (oldEditor instanceof ChangeListener)
+ removeChangeListener((ChangeListener) oldEditor);
+
+ this.editor = editor;
+ firePropertyChange("editor", oldEditor, editor);
+ }
+
+ /**
+ * Returns the model used by the {@link JSpinner} component.
+ *
+ * @return The model.
+ *
+ * @see #setModel(SpinnerModel)
+ */
+ public SpinnerModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets a new underlying model.
+ *
+ * @param newModel the new model to set
+ *
+ * @exception IllegalArgumentException if newModel is <code>null</code>
+ */
+ public void setModel(SpinnerModel newModel)
+ {
+ if (newModel == null)
+ throw new IllegalArgumentException();
+
+ if (model == newModel)
+ return;
+
+ SpinnerModel oldModel = model;
+ model = newModel;
+ firePropertyChange("model", oldModel, newModel);
+ setEditor(createEditor(model));
+ }
+
+ /**
+ * Gets the next value without changing the current value.
+ *
+ * @return the next value
+ *
+ * @see javax.swing.SpinnerModel#getNextValue
+ */
+ public Object getNextValue()
+ {
+ return model.getNextValue();
+ }
+
+ /**
+ * Gets the previous value without changing the current value.
+ *
+ * @return the previous value
+ *
+ * @see javax.swing.SpinnerModel#getPreviousValue
+ */
+ public Object getPreviousValue()
+ {
+ return model.getPreviousValue();
+ }
+
+ /**
+ * Gets the <code>SpinnerUI</code> that handles this spinner
+ *
+ * @return the <code>SpinnerUI</code>
+ */
+ public SpinnerUI getUI()
+ {
+ return (SpinnerUI) ui;
+ }
+
+ /**
+ * Gets the current value of the spinner, according to the underly model,
+ * not the UI.
+ *
+ * @return the current value
+ *
+ * @see javax.swing.SpinnerModel#getValue
+ */
+ public Object getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * Sets the value in the model.
+ *
+ * @param value the new value.
+ */
+ public void setValue(Object value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * Returns the ID that identifies which look and feel class will be
+ * the UI delegate for this spinner.
+ *
+ * @return <code>"SpinnerUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "SpinnerUI";
+ }
+
+ /**
+ * This method resets the spinner's UI delegate to the default UI for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((SpinnerUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Sets the UI delegate for the component.
+ *
+ * @param ui The spinner's UI delegate.
+ */
+ public void setUI(SpinnerUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Adds a <code>ChangeListener</code>
+ *
+ * @param listener the listener to add
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Remove a particular listener
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Gets all the <code>ChangeListener</code>s
+ *
+ * @return all the <code>ChangeListener</code>s
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Fires a <code>ChangeEvent</code> to all the <code>ChangeListener</code>s
+ * added to this <code>JSpinner</code>
+ */
+ protected void fireStateChanged()
+ {
+ ChangeEvent evt = new ChangeEvent(this);
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(evt);
+ }
+
+ /**
+ * Creates an editor that is appropriate for the specified <code>model</code>.
+ *
+ * @param model the model.
+ *
+ * @return The editor.
+ */
+ protected JComponent createEditor(SpinnerModel model)
+ {
+ if (model instanceof SpinnerDateModel)
+ return new DateEditor(this);
+ else if (model instanceof SpinnerNumberModel)
+ return new NumberEditor(this);
+ else if (model instanceof SpinnerListModel)
+ return new ListEditor(this);
+ else
+ return new DefaultEditor(this);
+ }
+}