2
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
2
Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
4
4
This file is part of GNU Classpath.
45
45
import java.awt.LayoutManager;
46
46
import java.beans.PropertyChangeEvent;
47
47
import java.beans.PropertyChangeListener;
48
import java.text.DateFormat;
48
49
import java.text.DecimalFormat;
50
import java.text.NumberFormat;
49
51
import java.text.ParseException;
50
52
import java.text.SimpleDateFormat;
53
55
import javax.swing.event.ChangeListener;
54
56
import javax.swing.plaf.SpinnerUI;
55
57
import javax.swing.text.DateFormatter;
58
import javax.swing.text.DefaultFormatterFactory;
59
import javax.swing.text.NumberFormatter;
58
* A JSpinner is a component which typically contains a numeric value and a
59
* way to manipulate the value.
62
* A <code>JSpinner</code> is a component that displays a single value from
63
* a sequence of values, and provides a convenient means for selecting the
64
* previous and next values in the sequence. Typically the spinner displays
65
* a numeric value, but it is possible to display dates or arbitrary items
61
68
* @author Ka-Hing Cheung
65
72
public class JSpinner extends JComponent
75
* The base class for the editor used by the {@link JSpinner} component.
76
* The editor is in fact a panel containing a {@link JFormattedTextField}
70
public static class DefaultEditor extends JPanel implements ChangeListener,
71
PropertyChangeListener,
79
public static class DefaultEditor
81
implements ChangeListener, PropertyChangeListener, LayoutManager
83
/** The spinner that the editor is allocated to. */
74
84
private JSpinner spinner;
76
86
/** The JFormattedTextField that backs the editor. */
82
92
private static final long serialVersionUID = -5317788736173368172L;
85
* Creates a new <code>DefaultEditor</code> object.
95
* Creates a new <code>DefaultEditor</code> object. The editor is
96
* registered with the spinner as a {@link ChangeListener} here.
87
98
* @param spinner the <code>JSpinner</code> associated with this editor
94
105
ftf = new JFormattedTextField();
96
107
ftf.setValue(spinner.getValue());
108
ftf.addPropertyChangeListener(this);
109
if(getComponentOrientation().isLeftToRight())
110
ftf.setHorizontalAlignment(JTextField.RIGHT);
112
ftf.setHorizontalAlignment(JTextField.LEFT);
97
113
spinner.addChangeListener(this);
101
* Returns the <code>JSpinner</code> object for this editor.
117
* Returns the <code>JSpinner</code> component that the editor is assigned
120
* @return The spinner that the editor is assigned to.
103
122
public JSpinner getSpinner()
136
* Removes the editor from the {@link ChangeListener} list maintained by
137
* the specified <code>spinner</code>.
119
* @param spinner DOCUMENT ME!
139
* @param spinner the spinner (<code>null</code> not permitted).
121
141
public void dismiss(JSpinner spinner)
158
* Sets the bounds for the child components in this container. In this
159
* case, the text field is the only component to be laid out.
139
* @param parent DOCUMENT ME!
161
* @param parent the parent container.
141
163
public void layoutContainer(Container parent)
153
* @param parent DOCUMENT ME!
155
* @return DOCUMENT ME!
173
* Calculates the minimum size for this component. In this case, the
174
* text field is the only subcomponent, so the return value is the minimum
175
* size of the text field plus the insets of this component.
177
* @param parent the parent container.
179
* @return The minimum size.
157
181
public Dimension minimumLayoutSize(Container parent)
168
* @param parent DOCUMENT ME!
170
* @return DOCUMENT ME!
190
* Calculates the preferred size for this component. In this case, the
191
* text field is the only subcomponent, so the return value is the
192
* preferred size of the text field plus the insets of this component.
194
* @param parent the parent container.
196
* @return The preferred size.
172
198
public Dimension preferredLayoutSize(Container parent)
207
* Receives notification of property changes. If the text field's 'value'
208
* property changes, the spinner's model is updated accordingly.
183
* @param event DOCUMENT ME!
210
* @param event the event.
185
212
public void propertyChange(PropertyChangeEvent event)
187
// TODO: Implement this properly.
214
if (event.getSource() == ftf)
216
if (event.getPropertyName().equals("value"))
217
spinner.getModel().setValue(event.getNewValue());
222
* Receives notification of changes in the state of the {@link JSpinner}
223
* that the editor belongs to - the content of the text field is updated
193
* @param event DOCUMENT ME!
226
* @param event the change event.
195
228
public void stateChanged(ChangeEvent event)
197
// TODO: Implement this properly.
230
ftf.setValue(spinner.getValue());
234
* This method does nothing. It is required by the {@link LayoutManager}
235
* interface, but since this component has a single child, there is no
236
* need to use this method.
238
* @param child the child component to remove.
200
240
public void removeLayoutComponent(Component child)
202
242
// Nothing to do here.
208
* @param name DOCUMENT ME!
209
* @param child DOCUMENT ME!
246
* This method does nothing. It is required by the {@link LayoutManager}
247
* interface, but since this component has a single child, there is no
248
* need to use this method.
250
* @param name the name.
251
* @param child the child component to add.
211
253
public void addLayoutComponent(String name, Component child)
225
271
private static final long serialVersionUID = 3791956183098282942L;
228
* Creates a new NumberEditor object.
274
* Creates a new <code>NumberEditor</code> object for the specified
275
* <code>spinner</code>. The editor is registered with the spinner as a
276
* {@link ChangeListener}.
230
* @param spinner DOCUMENT ME!
278
* @param spinner the component the editor will be used with.
232
280
public NumberEditor(JSpinner spinner)
283
NumberEditorFormatter nef = new NumberEditorFormatter();
284
nef.setMinimum(getModel().getMinimum());
285
nef.setMaximum(getModel().getMaximum());
286
ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
238
* Creates a new NumberEditor object.
290
* Creates a new <code>NumberEditor</code> object.
240
* @param spinner DOCUMENT ME!
292
* @param spinner the spinner.
293
* @param decimalFormatPattern the number format pattern.
242
295
public NumberEditor(JSpinner spinner, String decimalFormatPattern)
298
NumberEditorFormatter nef
299
= new NumberEditorFormatter(decimalFormatPattern);
300
nef.setMinimum(getModel().getMinimum());
301
nef.setMaximum(getModel().getMaximum());
302
ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
306
* Returns the format used by the text field.
250
* @return DOCUMENT ME!
308
* @return The format used by the text field.
252
310
public DecimalFormat getFormat()
312
NumberFormatter formatter = (NumberFormatter) ftf.getFormatter();
313
return (DecimalFormat) formatter.getFormat();
317
* Returns the model used by the editor's {@link JSpinner} component,
318
* cast to a {@link SpinnerNumberModel}.
257
322
public SpinnerNumberModel getModel()
259
324
return (SpinnerNumberModel) getSpinner().getModel();
328
static class NumberEditorFormatter
329
extends NumberFormatter
331
public NumberEditorFormatter()
333
super(NumberFormat.getInstance());
335
public NumberEditorFormatter(String decimalFormatPattern)
337
super(new DecimalFormat(decimalFormatPattern));
264
342
* A <code>JSpinner</code> editor used for the {@link SpinnerListModel}.
299
382
/** The serialVersionUID. */
300
383
private static final long serialVersionUID = -4279356973770397815L;
302
/** The DateFormat instance used to format the date. */
303
SimpleDateFormat dateFormat;
306
386
* Creates a new instance of DateEditor for the specified
307
387
* <code>JSpinner</code>.
312
392
public DateEditor(JSpinner spinner)
315
init(new SimpleDateFormat());
395
DateEditorFormatter nef = new DateEditorFormatter();
396
nef.setMinimum(getModel().getStart());
397
nef.setMaximum(getModel().getEnd());
398
ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
329
412
public DateEditor(JSpinner spinner, String dateFormatPattern)
332
init(new SimpleDateFormat(dateFormatPattern));
336
* Initializes the JFormattedTextField for this editor.
338
* @param format the date format to use in the formatted text field
340
private void init(SimpleDateFormat format)
343
getTextField().setFormatterFactory(
344
new JFormattedTextField.AbstractFormatterFactory()
346
public JFormattedTextField.AbstractFormatter
347
getFormatter(JFormattedTextField ftf)
349
return new DateFormatter(dateFormat);
415
DateEditorFormatter nef = new DateEditorFormatter(dateFormatPattern);
416
nef.setMinimum(getModel().getStart());
417
nef.setMaximum(getModel().getEnd());
418
ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
377
private static final long serialVersionUID = 3412663575706551720L;
445
static class DateEditorFormatter
446
extends DateFormatter
448
public DateEditorFormatter()
450
super(DateFormat.getInstance());
452
public DateEditorFormatter(String dateFormatPattern)
454
super(new SimpleDateFormat(dateFormatPattern));
459
* A listener that forwards {@link ChangeEvent} notifications from the model
460
* to the {@link JSpinner}'s listeners.
462
class ModelListener implements ChangeListener
465
* Creates a new listener.
467
public ModelListener()
469
// nothing to do here
473
* Receives notification from the model that its state has changed.
475
* @param event the event (ignored).
477
public void stateChanged(ChangeEvent event)
484
* The model that defines the current value and permitted values for the
380
487
private SpinnerModel model;
489
/** The current editor. */
383
490
private JComponent editor;
386
private ChangeListener listener = new ChangeListener()
388
public void stateChanged(ChangeEvent evt)
492
private static final long serialVersionUID = 3412663575706551720L;
395
* Creates a JSpinner with <code>SpinnerNumberModel</code>
495
* Creates a new <code>JSpinner</code> with default instance of
496
* {@link SpinnerNumberModel} (that is, a model with value 0, step size 1,
497
* and no upper or lower limit).
397
499
* @see javax.swing.SpinnerNumberModel
405
* Creates a JSpinner with the specific model and sets the default editor
507
* Creates a new <code>JSpinner with the specified model. The
508
* {@link #createEditor(SpinnerModel)} method is used to create an editor
509
* that is suitable for the model.
407
* @param model DOCUMENT ME!
511
* @param model the model (<code>null</code> not permitted).
513
* @throws NullPointerException if <code>model</code> is <code>null</code>.
409
515
public JSpinner(SpinnerModel model)
411
517
this.model = model;
412
model.addChangeListener(listener);
413
setEditor(createEditor(model));
518
this.editor = createEditor(model);
519
model.addChangeListener(new ModelListener());
442
* Changes the current editor to the new editor. This methods should remove
443
* the old listeners (if any) and adds the new listeners (if any).
445
* @param editor the new editor
447
* @throws IllegalArgumentException DOCUMENT ME!
548
* Changes the current editor to the new editor. The old editor is
549
* removed from the spinner's {@link ChangeEvent} list.
551
* @param editor the new editor (<code>null</code> not permitted.
553
* @throws IllegalArgumentException if <code>editor</code> is
449
556
* @see #getEditor
453
560
if (editor == null)
454
561
throw new IllegalArgumentException("editor may not be null");
456
if (this.editor instanceof DefaultEditor)
457
((DefaultEditor) editor).dismiss(this);
458
else if (this.editor instanceof ChangeListener)
459
removeChangeListener((ChangeListener) this.editor);
461
if (editor instanceof ChangeListener)
462
addChangeListener((ChangeListener) editor);
563
JComponent oldEditor = this.editor;
564
if (oldEditor instanceof DefaultEditor)
565
((DefaultEditor) oldEditor).dismiss(this);
566
else if (oldEditor instanceof ChangeListener)
567
removeChangeListener((ChangeListener) oldEditor);
464
569
this.editor = editor;
570
firePropertyChange("editor", oldEditor, editor);
468
* Gets the underly model.
574
* Returns the model used by the {@link JSpinner} component.
470
* @return the underly model
578
* @see #setModel(SpinnerModel)
472
580
public SpinnerModel getModel()
492
600
SpinnerModel oldModel = model;
493
601
model = newModel;
494
602
firePropertyChange("model", oldModel, newModel);
497
setEditor(createEditor(model));
603
setEditor(createEditor(model));
558
* This method returns a name to identify which look and feel class will be
664
* Returns the ID that identifies which look and feel class will be
559
665
* the UI delegate for this spinner.
561
* @return The UIClass identifier. "SpinnerUI"
667
* @return <code>"SpinnerUI"</code>.
563
669
public String getUIClassID()
631
* Creates an editor for this <code>JSpinner</code>. Really, it should be a
632
* <code>JSpinner.DefaultEditor</code>, but since that should be
633
* implemented by a JFormattedTextField, and one is not written, I am just
634
* using a dummy one backed by a JLabel.
636
* @param model DOCUMENT ME!
638
* @return the default editor
737
* Creates an editor that is appropriate for the specified <code>model</code>.
739
* @param model the model.
741
* @return The editor.
640
743
protected JComponent createEditor(SpinnerModel model)
643
746
return new DateEditor(this);
644
747
else if (model instanceof SpinnerNumberModel)
645
748
return new NumberEditor(this);
749
else if (model instanceof SpinnerListModel)
750
return new ListEditor(this);
647
752
return new DefaultEditor(this);