OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / JSpinner.java
1 /* JSpinner.java --
2    Copyright (C) 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing;
40
41 import java.awt.Component;
42 import java.awt.Container;
43 import java.awt.Dimension;
44 import java.awt.Insets;
45 import java.awt.LayoutManager;
46 import java.beans.PropertyChangeEvent;
47 import java.beans.PropertyChangeListener;
48 import java.text.DecimalFormat;
49 import java.text.ParseException;
50 import java.text.SimpleDateFormat;
51
52 import javax.swing.border.EtchedBorder;
53 import javax.swing.event.ChangeEvent;
54 import javax.swing.event.ChangeListener;
55 import javax.swing.plaf.SpinnerUI;
56 import javax.swing.text.DateFormatter;
57
58 /**
59  * A JSpinner is a component which typically contains a numeric value and a
60  * way to manipulate the value.
61  *
62  * @author Ka-Hing Cheung
63  * 
64  * @since 1.4
65  */
66 public class JSpinner extends JComponent
67 {
68   /**
69    * DOCUMENT ME!
70    */
71   public static class DefaultEditor extends JPanel implements ChangeListener,
72                                                               PropertyChangeListener,
73                                                               LayoutManager
74   {
75     private JSpinner spinner;
76
77     /** The JFormattedTextField that backs the editor. */
78     JFormattedTextField ftf;
79
80     /**
81      * For compatability with Sun's JDK 1.4.2 rev. 5
82      */
83     private static final long serialVersionUID = -5317788736173368172L;
84
85     /**
86      * Creates a new <code>DefaultEditor</code> object.
87      *
88      * @param spinner the <code>JSpinner</code> associated with this editor
89      */
90     public DefaultEditor(JSpinner spinner)
91     {
92       super();
93       setLayout(this);
94       this.spinner = spinner;
95       ftf = new JFormattedTextField();
96       add(ftf);
97       ftf.setValue(spinner.getValue());
98       spinner.addChangeListener(this);
99     }
100
101     /**
102      * Returns the <code>JSpinner</code> object for this editor.
103      */
104     public JSpinner getSpinner()
105     {
106       return spinner;
107     }
108     
109     /**
110      * DOCUMENT ME!
111      */
112     public void commitEdit()
113       throws ParseException
114     {
115     } /* TODO */
116
117     /**
118      * DOCUMENT ME!
119      *
120      * @param spinner DOCUMENT ME!
121      */
122     public void dismiss(JSpinner spinner)
123     {
124       spinner.removeChangeListener(this);
125     }
126
127     /**
128      * DOCUMENT ME!
129      *
130      * @return DOCUMENT ME!
131      */
132     public JFormattedTextField getTextField()
133     {
134       return ftf;
135     }
136     
137     /**
138      * DOCUMENT ME!
139      *
140      * @param parent DOCUMENT ME!
141      */
142     public void layoutContainer(Container parent)
143     {
144       Insets insets = getInsets();
145       Dimension size = getSize();
146       ftf.setBounds(insets.left, insets.top,
147                     size.width - insets.left - insets.right,
148                     size.height - insets.top - insets.bottom);
149     }
150     
151     /**
152      * DOCUMENT ME!
153      *
154      * @param parent DOCUMENT ME!
155      *
156      * @return DOCUMENT ME!
157      */
158     public Dimension minimumLayoutSize(Container parent)
159     {
160       Insets insets = getInsets();
161       Dimension minSize = ftf.getMinimumSize();
162       return new Dimension(minSize.width + insets.left + insets.right,
163                             minSize.height + insets.top + insets.bottom);
164     }
165     
166     /**
167      * DOCUMENT ME!
168      *
169      * @param parent DOCUMENT ME!
170      *
171      * @return DOCUMENT ME!
172      */
173     public Dimension preferredLayoutSize(Container parent)
174     {
175       Insets insets = getInsets();
176       Dimension prefSize = ftf.getPreferredSize();
177       return new Dimension(prefSize.width + insets.left + insets.right,
178                             prefSize.height + insets.top + insets.bottom);
179     }
180     
181     /**
182      * DOCUMENT ME!
183      *
184      * @param event DOCUMENT ME!
185      */
186     public void propertyChange(PropertyChangeEvent event)
187     {
188     } /* TODO */
189     
190     /**
191      * DOCUMENT ME!
192      *
193      * @param event DOCUMENT ME!
194      */
195     public void stateChanged(ChangeEvent event)
196     {
197     } /* TODO */
198     
199     /* no-ops */
200     public void removeLayoutComponent(Component child)
201     {
202     }
203
204     /**
205      * DOCUMENT ME!
206      *
207      * @param name DOCUMENT ME!
208      * @param child DOCUMENT ME!
209      */
210     public void addLayoutComponent(String name, Component child)
211     {
212     }
213   }
214
215   /**
216    * DOCUMENT ME!
217    */
218   public static class NumberEditor extends DefaultEditor
219   {
220     /**
221      * For compatability with Sun's JDK
222      */
223     private static final long serialVersionUID = 3791956183098282942L;
224
225     /**
226      * Creates a new NumberEditor object.
227      *
228      * @param spinner DOCUMENT ME!
229      */
230     public NumberEditor(JSpinner spinner)
231     {
232       super(spinner);
233     }
234
235     /**
236      * Creates a new NumberEditor object.
237      *
238      * @param spinner DOCUMENT ME!
239      */
240     public NumberEditor(JSpinner spinner, String decimalFormatPattern)
241     {
242       super(spinner);
243     }
244
245     /**
246      * DOCUMENT ME!
247      *
248      * @return DOCUMENT ME!
249      */
250     public DecimalFormat getFormat()
251     {
252       return null;
253     }
254
255     public SpinnerNumberModel getModel()
256     {
257       return (SpinnerNumberModel) getSpinner().getModel();
258     }
259   }
260
261   /**
262    * An editor class for a <code>JSpinner</code> that is used
263    * for displaying and editing dates (e.g. that uses
264    * <code>SpinnerDateModel</code> as model).
265    *
266    * The editor uses a {@link JTextField} with the value
267    * displayed by a {@link DateFormatter} instance.
268    */
269   public static class DateEditor extends DefaultEditor
270   {
271
272     /** The serialVersionUID. */
273     private static final long serialVersionUID = -4279356973770397815L;
274
275     /** The DateFormat instance used to format the date. */
276     SimpleDateFormat dateFormat;
277
278     /**
279      * Creates a new instance of DateEditor for the specified
280      * <code>JSpinner</code>.
281      *
282      * @param spinner the <code>JSpinner</code> for which to
283      *     create a <code>DateEditor</code> instance
284      */
285     public DateEditor(JSpinner spinner)
286     {
287       super(spinner);
288       init(new SimpleDateFormat());
289     }
290
291     /**
292      * Creates a new instance of DateEditor for the specified
293      * <code>JSpinner</code> using the specified date format
294      * pattern.
295      *
296      * @param spinner the <code>JSpinner</code> for which to
297      *     create a <code>DateEditor</code> instance
298      * @param dateFormatPattern the date format to use
299      *
300      * @see SimpleDateFormat(String)
301      */
302     public DateEditor(JSpinner spinner, String dateFormatPattern)
303     {
304       super(spinner);
305       init(new SimpleDateFormat(dateFormatPattern));
306     }
307
308     /**
309      * Initializes the JFormattedTextField for this editor.
310      *
311      * @param the date format to use in the formatted text field
312      */
313     private void init(SimpleDateFormat format)
314     {
315       dateFormat = format;
316       getTextField().setFormatterFactory(
317         new JFormattedTextField.AbstractFormatterFactory()
318         {
319           public JFormattedTextField.AbstractFormatter
320           getFormatter(JFormattedTextField ftf)
321           {
322             return new DateFormatter(dateFormat);
323           }
324         });
325     }
326
327     /**
328      * Returns the <code>SimpleDateFormat</code> instance that is used to
329      * format the date value.
330      *
331      * @return the <code>SimpleDateFormat</code> instance that is used to
332      *     format the date value
333      */
334     public SimpleDateFormat getFormat()
335     {
336       return dateFormat;
337     }
338
339     /**
340      * Returns the {@link SpinnerDateModel} that is edited by this editor.
341      *
342      * @return the <code>SpinnerDateModel</code> that is edited by this editor
343      */
344     public SpinnerDateModel getModel()
345     {
346       return (SpinnerDateModel) getSpinner().getModel();
347     }
348   }
349
350   private static final long serialVersionUID = 3412663575706551720L;
351
352   /** DOCUMENT ME! */
353   private SpinnerModel model;
354
355   /** DOCUMENT ME! */
356   private JComponent editor;
357
358   /** DOCUMENT ME! */
359   private ChangeListener listener = new ChangeListener()
360     {
361       public void stateChanged(ChangeEvent evt)
362       {
363         fireStateChanged();
364       }
365     };
366
367   /**
368    * Creates a JSpinner with <code>SpinnerNumberModel</code>
369    *
370    * @see javax.swing.SpinnerNumberModel
371    */
372   public JSpinner()
373   {
374     this(new SpinnerNumberModel());
375   }
376
377   /**
378    * Creates a JSpinner with the specific model and sets the default editor
379    *
380    * @param model DOCUMENT ME!
381    */
382   public JSpinner(SpinnerModel model)
383   {
384     this.model = model;
385     model.addChangeListener(listener);
386     setEditor(createEditor(model));
387     updateUI();
388   }
389
390   /**
391    * If the editor is <code>JSpinner.DefaultEditor</code>, then forwards the
392    * call to it, otherwise do nothing.
393    *
394    * @throws ParseException DOCUMENT ME!
395    */
396   public void commitEdit() throws ParseException
397   {
398     if (editor instanceof DefaultEditor)
399       ((DefaultEditor) editor).commitEdit();
400   }
401
402   /**
403    * Gets the current editor
404    *
405    * @return the current editor
406    *
407    * @see #setEditor
408    */
409   public JComponent getEditor()
410   {
411     return editor;
412   }
413
414   /**
415    * Changes the current editor to the new editor. This methods should remove
416    * the old listeners (if any) and adds the new listeners (if any).
417    *
418    * @param editor the new editor
419    *
420    * @throws IllegalArgumentException DOCUMENT ME!
421    *
422    * @see #getEditor
423    */
424   public void setEditor(JComponent editor)
425   {
426     if (editor == null)
427       throw new IllegalArgumentException("editor may not be null");
428
429     if (this.editor instanceof DefaultEditor)
430       ((DefaultEditor) editor).dismiss(this);
431     else if (this.editor instanceof ChangeListener)
432       removeChangeListener((ChangeListener) this.editor);
433
434     if (editor instanceof ChangeListener)
435       addChangeListener((ChangeListener) editor);
436
437     this.editor = editor;
438   }
439
440   /**
441    * Gets the underly model.
442    *
443    * @return the underly model
444    */
445   public SpinnerModel getModel()
446   {
447     return model;
448   }
449
450   /**
451    * Sets a new underlying model.
452    *
453    * @param newModel the new model to set
454    *
455    * @exception IllegalArgumentException if newModel is <code>null</code>
456    */
457   public void setModel(SpinnerModel newModel)
458   {
459     if (newModel == null)
460       throw new IllegalArgumentException();
461     
462     if (model == newModel)
463       return;
464
465     SpinnerModel oldModel = model;
466     model = newModel;
467     firePropertyChange("model", oldModel, newModel);
468
469     if (editor == null)
470       setEditor(createEditor(model));
471   }
472
473   /**
474    * Gets the next value without changing the current value.
475    *
476    * @return the next value
477    *
478    * @see javax.swing.SpinnerModel#getNextValue
479    */
480   public Object getNextValue()
481   {
482     return model.getNextValue();
483   }
484
485   /**
486    * Gets the previous value without changing the current value.
487    *
488    * @return the previous value
489    *
490    * @see javax.swing.SpinnerModel#getPreviousValue
491    */
492   public Object getPreviousValue()
493   {
494     return model.getPreviousValue();
495   }
496
497   /**
498    * Gets the <code>SpinnerUI</code> that handles this spinner
499    *
500    * @return the <code>SpinnerUI</code>
501    */
502   public SpinnerUI getUI()
503   {
504     return (SpinnerUI) ui;
505   }
506
507   /**
508    * Gets the current value of the spinner, according to the underly model,
509    * not the UI.
510    *
511    * @return the current value
512    *
513    * @see javax.swing.SpinnerModel#getValue
514    */
515   public Object getValue()
516   {
517     return model.getValue();
518   }
519
520   /**
521    * DOCUMENT ME!
522    *
523    * @param value DOCUMENT ME!
524    */
525   public void setValue(Object value)
526   {
527     model.setValue(value);
528   }
529
530   /**
531    * This method returns a name to identify which look and feel class will be
532    * the UI delegate for this spinner.
533    *
534    * @return The UIClass identifier. "SpinnerUI"
535    */
536   public String getUIClassID()
537   {
538     return "SpinnerUI";
539   }
540
541   /**
542    * This method resets the spinner's UI delegate to the default UI for the
543    * current look and feel.
544    */
545   public void updateUI()
546   {
547     setUI((SpinnerUI) UIManager.getUI(this));
548   }
549
550   /**
551    * This method sets the spinner's UI delegate.
552    *
553    * @param ui The spinner's UI delegate.
554    */
555   public void setUI(SpinnerUI ui)
556   {
557     super.setUI(ui);
558   }
559
560   /**
561    * Adds a <code>ChangeListener</code>
562    *
563    * @param listener the listener to add
564    */
565   public void addChangeListener(ChangeListener listener)
566   {
567     listenerList.add(ChangeListener.class, listener);
568   }
569
570   /**
571    * Remove a particular listener
572    *
573    * @param listener the listener to remove
574    */
575   public void removeChangeListener(ChangeListener listener)
576   {
577     listenerList.remove(ChangeListener.class, listener);
578   }
579
580   /**
581    * Gets all the <code>ChangeListener</code>s
582    *
583    * @return all the <code>ChangeListener</code>s
584    */
585   public ChangeListener[] getChangeListeners()
586   {
587     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
588   }
589
590   /**
591    * Fires a <code>ChangeEvent</code> to all the <code>ChangeListener</code>s
592    * added to this <code>JSpinner</code>
593    */
594   protected void fireStateChanged()
595   {
596     ChangeEvent evt = new ChangeEvent(this);
597     ChangeListener[] listeners = getChangeListeners();
598
599     for (int i = 0; i < listeners.length; ++i)
600       listeners[i].stateChanged(evt);
601   }
602
603   /**
604    * Creates an editor for this <code>JSpinner</code>. Really, it should be a
605    * <code>JSpinner.DefaultEditor</code>, but since that should be
606    * implemented by a JFormattedTextField, and one is not written, I am just
607    * using a dummy one backed by a JLabel.
608    *
609    * @param model DOCUMENT ME!
610    *
611    * @return the default editor
612    */
613   protected JComponent createEditor(SpinnerModel model)
614   {
615     if (model instanceof SpinnerDateModel)
616       return new DateEditor(this);
617     else if (model instanceof SpinnerNumberModel)
618       return new NumberEditor(this);
619     else
620       return new DefaultEditor(this);
621   }
622 }