Am Mittwoch, den 15.02.2006, 16:32 +0000 schrieb David Gilbert:
> Hi,
> 
> This patch makes our JSpinner usable (it was broken) and more compatible with 
> the 
> reference implementation (I added a lot of Mauve tests to ensure this).  I 
> also 
> added a small demo app that I used for basic testing:

Wow! Good work you do. :-) The demo is quite impressive. Is it linked
into the activity board (replace that old Spinner demo)?

Thank you very much for this, this has long been broken.

Cheers, Roman

> 2006-02-15  David Gilbert  <[EMAIL PROTECTED]>
> 
>       * javax/swing/JSpinner.java
>       (DefaultEditor.DefaultEditor(JSpinner)): Add self to text field as a
>       PropertyChangeListener,
>       (DefaultEditor.getSpinner): Updated API docs,
>       (DefaultEditor.dismiss): Likewise,
>       (DefaultEditor.getTextField): Likewise,
>       (DefaultEditor.layoutContainer): Likewise,
>       (DefaultEditor.minimumLayoutSize): Likewise,
>       (DefaultEditor.preferredLayoutSize): Likewise,
>       (DefaultEditor.propertyChange): Implemented,
>       (DefaultEditor.stateChanged): Implemented,
>       (DefaultEditor.removeLayoutComponent): Updated API docs,
>       (DefaultEditor.addLayoutComponent): Likewise,
>       (NumberEditor.NumberEditor(JSpinner)): Set formatter for text field,
>       (NumberEditor.NumberEditor(JSpinner, String)): Likewise,
>       (NumberEditor.getFormat): Implemented,
>       (NumberEditor.getModel): Updated API docs,
>       (NumberEditorFormatter): New static inner class,
>       (ListEditor.getModel): Updated API docs,
>       (DateEditor.dateFormat): Removed,
>       (DateEditor.DateEditor(JSpinner)): Set formatter for text field,
>       (DateEditor.DateEditor(JSpinner, String)): Likewise,
>       (DateEditor.init): Removed,
>       (DateEditor.getFormat): Reimplemented,
>       (DateEditorFormatter): New static inner class,
>       (ModelListener): New inner class,
>       (model): Updated API docs,
>       (editor): Likewise,
>       (listener): Removed,
>       (JSpinner()): Updated API docs,
>       (JSpinner(SpinnerModel)): Set up ModelListener,
>       (setEditor): Fire property change,
>       (getModel): Updated API docs,
>       (setModel): Removed check for null editor,
>       (setValue): Updated API docs,
>       (getUIClassID): Updated API docs,
>       (createEditor): Handle SpinnerListModel case,
>       * javax/swing/plaf/basic/BasicSpinnerUI.java
>       (createUI): Updated API docs,
>       (createPropertyChangeListener): Added FIXME,
>       (installDefaults): Set text field border to null,
>       (DefaultLayoutManager): Updated API docs,
>       (DefaultLayoutManager.layoutContainer): Modified layout,
>       (DefaultLayoutManager.minimumLayoutSize): Ignore button heights,
>       (DefaultLayoutManager.preferredLayoutSize): Likewise,
>       (DefaultLayoutManager.removeLayoutComponent): Removed tabs,
>       (DefaultLayoutManager.addLayoutComponent): Likewise,
>       (DefaultLayoutManager.minSize): Renamed prefSize,
>       (DefaultLayoutManager.setBounds): Reformatted,
>       (DefaultLayoutManager.editor): Added API docs,
>       (DefaultLayoutManager.next): Likewise,
>       (DefaultLayoutManager.previous): Likewise,
>       * javax/swing/plaf/metal/MetalLookAndFeel.java
>       (initComponentDefaults): Added entry for 'Spinner.border',
>       * examples/gnu/classpath/examples/swing/SpinnerDemo.java: New file.
> 
> There is still some work to do on this (e.g. font settings aren't reflected 
> in the 
> UI yet, setting the component to disabled doesn't work, etc.)...
> 
> Regards,
> 
> Dave
> einfaches Textdokument-Anlage (diff.txt)
> Index: examples/gnu/classpath/examples/swing/SpinnerDemo.java
> ===================================================================
> RCS file: examples/gnu/classpath/examples/swing/SpinnerDemo.java
> diff -N examples/gnu/classpath/examples/swing/SpinnerDemo.java
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ examples/gnu/classpath/examples/swing/SpinnerDemo.java    15 Feb 2006 
> 15:57:19 -0000
> @@ -0,0 +1,230 @@
> +/* SpinnerDemo.java -- An example showing various spinners in Swing.
> +   Copyright (C) 2006,  Free Software Foundation, Inc.
> +
> +This file is part of GNU Classpath examples.
> +
> +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.
> +*/
> +
> +
> +package gnu.classpath.examples.swing;
> +
> +import java.awt.BorderLayout;
> +import java.awt.Font;
> +import java.awt.GridLayout;
> +import java.awt.event.ActionEvent;
> +import java.awt.event.ActionListener;
> +import java.util.Calendar;
> +import java.util.Date;
> +
> +import javax.swing.BorderFactory;
> +import javax.swing.JButton;
> +import javax.swing.JCheckBox;
> +import javax.swing.JFrame;
> +import javax.swing.JPanel;
> +import javax.swing.JSpinner;
> +import javax.swing.SpinnerDateModel;
> +import javax.swing.SpinnerListModel;
> +import javax.swing.SpinnerNumberModel;
> +import javax.swing.UIManager;
> +import javax.swing.plaf.metal.DefaultMetalTheme;
> +import javax.swing.plaf.metal.MetalLookAndFeel;
> +
> +/**
> + * A simple demo showing various spinners in different states.
> + */
> +public class SpinnerDemo 
> +  extends JFrame 
> +  implements ActionListener 
> +{
> +  private JPanel content;
> +  private JCheckBox spinnerState1;  
> +  private JSpinner spinner1;
> +  private JSpinner spinner2;
> +
> +  private JCheckBox spinnerState2;    
> +  private JSpinner spinner3;
> +  private JSpinner spinner4;
> +    
> +  private JCheckBox spinnerState3;    
> +  private JSpinner spinner5;
> +  private JSpinner spinner6;
> +  
> +  /**
> +   * Creates a new demo instance.
> +   * 
> +   * @param title  the frame title.
> +   */
> +  public SpinnerDemo(String title) 
> +  {
> +    super(title);
> +    JPanel content = createContent();
> +    // initFrameContent() is only called (from main) when running this app 
> +    // standalone
> +  }
> +  
> +  /**
> +   * When the demo is run independently, the frame is displayed, so we should
> +   * initialise the content panel (including the demo content and a close 
> +   * button).  But when the demo is run as part of the Swing activity board,
> +   * only the demo content panel is used, the frame itself is never 
> displayed,
> +   * so we can avoid this step.
> +   */
> +  public void initFrameContent() 
> +  {
> +    JPanel closePanel = new JPanel();
> +    JButton closeButton = new JButton("Close");
> +    closeButton.setActionCommand("CLOSE");
> +    closeButton.addActionListener(this);
> +    closePanel.add(closeButton);
> +    content.add(closePanel, BorderLayout.SOUTH);
> +    getContentPane().add(content);
> +  }
> +       
> +  /**
> +   * Returns a panel with the demo content.  The panel
> +   * uses a BorderLayout(), and the BorderLayout.SOUTH area
> +   * is empty, to allow callers to add controls to the 
> +   * bottom of the panel if they want to (a close button is
> +   * added if this demo is being run as a standalone demo).
> +   */       
> +  JPanel createContent() 
> +  {
> +    if (content == null)
> +      {
> +        content = new JPanel(new BorderLayout());
> +        JPanel panel = new JPanel(new GridLayout(3, 1));
> +        panel.add(createPanel1());
> +        panel.add(createPanel2());
> +        panel.add(createPanel3());
> +        content.add(panel);
> +      }
> +    return content;        
> +  }
> +    
> +  private JPanel createPanel1() 
> +  {
> +    JPanel panel = new JPanel(new BorderLayout());
> +    this.spinnerState1 = new JCheckBox("Enabled", true);
> +    this.spinnerState1.setActionCommand("COMBO_STATE1");
> +    this.spinnerState1.addActionListener(this);
> +    panel.add(this.spinnerState1, BorderLayout.EAST);
> +        
> +    JPanel controlPanel = new JPanel();
> +    controlPanel.setBorder(BorderFactory.createTitledBorder(
> +        "Number Spinner: "));
> +    this.spinner1 = new JSpinner(new SpinnerNumberModel(5.0, 0.0, 10.0, 
> 0.5));
> +    this.spinner2 = new JSpinner(new SpinnerNumberModel(50, 0, 100, 5));
> +    this.spinner2.setFont(new Font("Dialog", Font.PLAIN, 20));
> +    controlPanel.add(this.spinner1);
> +    controlPanel.add(this.spinner2);
> +        
> +    panel.add(controlPanel);
> +     
> +    return panel;
> +  }
> +    
> +  private JPanel createPanel2() 
> +  {
> +    JPanel panel = new JPanel(new BorderLayout());
> +    this.spinnerState2 = new JCheckBox("Enabled", true);
> +    this.spinnerState2.setActionCommand("COMBO_STATE2");
> +    this.spinnerState2.addActionListener(this);
> +    panel.add(this.spinnerState2, BorderLayout.EAST);
> +        
> +    JPanel controlPanel = new JPanel();
> +    controlPanel.setBorder(BorderFactory.createTitledBorder("Date Spinner: 
> "));
> +    this.spinner3 = new JSpinner(new SpinnerDateModel(new Date(), null, 
> null, 
> +        Calendar.DATE));
> +        
> +    this.spinner4 = new JSpinner(new SpinnerDateModel(new Date(), null, 
> null, 
> +        Calendar.YEAR));
> +    this.spinner4.setFont(new Font("Dialog", Font.PLAIN, 20));
> +        
> +    controlPanel.add(this.spinner3);
> +    controlPanel.add(this.spinner4);
> +        
> +    panel.add(controlPanel);
> +     
> +    return panel;
> +  }
> +
> +  private JPanel createPanel3() 
> +  {
> +    JPanel panel = new JPanel(new BorderLayout());
> +    this.spinnerState3 = new JCheckBox("Enabled", true);
> +    this.spinnerState3.setActionCommand("COMBO_STATE3");
> +    this.spinnerState3.addActionListener(this);
> +    panel.add(this.spinnerState3, BorderLayout.EAST);
> +        
> +    JPanel controlPanel = new JPanel();
> +    controlPanel.setBorder(BorderFactory.createTitledBorder("List Spinner: 
> "));
> +    this.spinner5 = new JSpinner(new SpinnerListModel(new Object[] {"Red", 
> +        "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"}));
> +    
> +    this.spinner6 = new JSpinner(new SpinnerListModel(new Object[] {"Red", 
> +        "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"}));
> +    this.spinner6.setValue("Yellow");
> +    this.spinner6.setFont(new Font("Dialog", Font.PLAIN, 20));
> +        
> +    controlPanel.add(this.spinner5);
> +    controlPanel.add(this.spinner6);
> +        
> +    panel.add(controlPanel);
> +     
> +    return panel;
> +  }
> +    
> +  public void actionPerformed(ActionEvent e) 
> +  {
> +    if (e.getActionCommand().equals("COMBO_STATE1")) 
> +    {
> +      spinner1.setEnabled(spinnerState1.isSelected());
> +      spinner2.setEnabled(spinnerState1.isSelected());
> +    }
> +    else if (e.getActionCommand().equals("COMBO_STATE2")) 
> +    {
> +      spinner3.setEnabled(spinnerState2.isSelected());
> +      spinner4.setEnabled(spinnerState2.isSelected());
> +    }
> +    else if (e.getActionCommand().equals("COMBO_STATE3")) 
> +    {
> +      spinner5.setEnabled(spinnerState3.isSelected());
> +      spinner6.setEnabled(spinnerState3.isSelected());
> +    }
> +    else if (e.getActionCommand().equals("CLOSE"))
> +    {
> +      System.exit(0);
> +    }
> +  }
> +
> +  public static void main(String[] args) 
> +  {
> +    try
> +    {
> +      MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
> +      UIManager.setLookAndFeel(new 
> javax.swing.plaf.metal.MetalLookAndFeel());
> +    }
> +    catch (Exception e) {
> +        e.printStackTrace();
> +    }
> +    SpinnerDemo app = new SpinnerDemo("Spinner Demo");
> +    app.initFrameContent();
> +    app.pack();
> +    app.setVisible(true);
> +  }
> +
> +}
> Index: javax/swing/JSpinner.java
> ===================================================================
> RCS file: /sources/classpath/classpath/javax/swing/JSpinner.java,v
> retrieving revision 1.14
> diff -u -r1.14 JSpinner.java
> --- javax/swing/JSpinner.java 19 Oct 2005 15:45:04 -0000      1.14
> +++ javax/swing/JSpinner.java 15 Feb 2006 15:57:29 -0000
> @@ -1,5 +1,5 @@
>  /* JSpinner.java --
> -   Copyright (C) 2004, 2005  Free Software Foundation, Inc.
> +   Copyright (C) 2004, 2005, 2006  Free Software Foundation, Inc.
>  
>  This file is part of GNU Classpath.
>  
> @@ -45,7 +45,9 @@
>  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;
>  
> @@ -53,10 +55,15 @@
>  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 JSpinner is a component which typically contains a numeric value and a
> - * way to manipulate the value.
> + * 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
>   * 
> @@ -65,12 +72,15 @@
>  public class JSpinner extends JComponent
>  {
>    /**
> -   * DOCUMENT ME!
> -   */
> -  public static class DefaultEditor extends JPanel implements ChangeListener,
> -                                                              
> PropertyChangeListener,
> -                                                              LayoutManager
> +   * The base class for the editor used by the [EMAIL PROTECTED] JSpinner} 
> component.  
> +   * The editor is in fact a panel containing a [EMAIL PROTECTED] 
> 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. */
> @@ -82,7 +92,8 @@
>      private static final long serialVersionUID = -5317788736173368172L;
>  
>      /**
> -     * Creates a new <code>DefaultEditor</code> object.
> +     * Creates a new <code>DefaultEditor</code> object.  The editor is 
> +     * registered with the spinner as a [EMAIL PROTECTED] ChangeListener} 
> here.
>       *
>       * @param spinner the <code>JSpinner</code> associated with this editor
>       */
> @@ -94,11 +105,15 @@
>        ftf = new JFormattedTextField();
>        add(ftf);
>        ftf.setValue(spinner.getValue());
> +      ftf.addPropertyChangeListener(this);
>        spinner.addChangeListener(this);
>      }
>  
>      /**
> -     * Returns the <code>JSpinner</code> object for this editor.
> +     * Returns the <code>JSpinner</code> component that the editor is 
> assigned
> +     * to.
> +     * 
> +     * @return The spinner that the editor is assigned to.
>       */
>      public JSpinner getSpinner()
>      {
> @@ -114,9 +129,10 @@
>      }
>  
>      /**
> -     * DOCUMENT ME!
> +     * Removes the editor from the [EMAIL PROTECTED] ChangeListener} list 
> maintained by
> +     * the specified <code>spinner</code>.
>       *
> -     * @param spinner DOCUMENT ME!
> +     * @param spinner  the spinner (<code>null</code> not permitted).
>       */
>      public void dismiss(JSpinner spinner)
>      {
> @@ -124,9 +140,10 @@
>      }
>  
>      /**
> -     * DOCUMENT ME!
> +     * Returns the text field used to display and edit the current value in 
> +     * the spinner.
>       *
> -     * @return DOCUMENT ME!
> +     * @return The text field.
>       */
>      public JFormattedTextField getTextField()
>      {
> @@ -134,9 +151,10 @@
>      }
>      
>      /**
> -     * DOCUMENT ME!
> +     * 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 DOCUMENT ME!
> +     * @param parent the parent container.
>       */
>      public void layoutContainer(Container parent)
>      {
> @@ -148,11 +166,13 @@
>      }
>      
>      /**
> -     * DOCUMENT ME!
> +     * 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 DOCUMENT ME!
> +     * @param parent  the parent container.
>       *
> -     * @return DOCUMENT ME!
> +     * @return The minimum size.
>       */
>      public Dimension minimumLayoutSize(Container parent)
>      {
> @@ -163,11 +183,13 @@
>      }
>      
>      /**
> -     * DOCUMENT ME!
> +     * 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 DOCUMENT ME!
> +     * @param parent  the parent container.
>       *
> -     * @return DOCUMENT ME!
> +     * @return The preferred size.
>       */
>      public Dimension preferredLayoutSize(Container parent)
>      {
> @@ -178,35 +200,51 @@
>      }
>      
>      /**
> -     * DOCUMENT ME!
> +     * Receives notification of property changes.  If the text field's 
> 'value' 
> +     * property changes, the spinner's model is updated accordingly.
>       *
> -     * @param event DOCUMENT ME!
> +     * @param event the event.
>       */
>      public void propertyChange(PropertyChangeEvent event)
>      {
> -      // TODO: Implement this properly.
> +      if (event.getSource() == ftf) 
> +        {
> +          if (event.getPropertyName().equals("value"))
> +            spinner.getModel().setValue(event.getNewValue());
> +        }
>      }
>      
>      /**
> -     * DOCUMENT ME!
> +     * Receives notification of changes in the state of the [EMAIL 
> PROTECTED] JSpinner}
> +     * that the editor belongs to - the content of the text field is updated
> +     * accordingly.  
>       *
> -     * @param event DOCUMENT ME!
> +     * @param event  the change event.
>       */
>      public void stateChanged(ChangeEvent event)
>      {
> -      // TODO: Implement this properly.
> +      ftf.setValue(spinner.getValue());
>      }
>      
> +    /**
> +     * This method does nothing.  It is required by the [EMAIL PROTECTED] 
> 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.
>      }
>  
>      /**
> -     * DOCUMENT ME!
> -     *
> -     * @param name DOCUMENT ME!
> -     * @param child DOCUMENT ME!
> +     * This method does nothing.  It is required by the [EMAIL PROTECTED] 
> 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)
>      {
> @@ -215,7 +253,11 @@
>    }
>  
>    /**
> -   * DOCUMENT ME!
> +   * A panel containing a [EMAIL PROTECTED] JFormattedTextField} that is 
> configured for
> +   * displaying and editing numbers.  The panel is used as a subcomponent of
> +   * a [EMAIL PROTECTED] JSpinner}.
> +   * 
> +   * @see JSpinner#createEditor(SpinnerModel)
>     */
>    public static class NumberEditor extends DefaultEditor
>    {
> @@ -225,40 +267,72 @@
>      private static final long serialVersionUID = 3791956183098282942L;
>  
>      /**
> -     * Creates a new NumberEditor object.
> +     * Creates a new <code>NumberEditor</code> object for the specified 
> +     * <code>spinner</code>.  The editor is registered with the spinner as a 
> +     * [EMAIL PROTECTED] ChangeListener}.
>       *
> -     * @param spinner DOCUMENT ME!
> +     * @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 NumberEditor object.
> +     * Creates a new <code>NumberEditor</code> object.
>       *
> -     * @param spinner DOCUMENT ME!
> +     * @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));
>      }
>  
>      /**
> -     * DOCUMENT ME!
> +     * Returns the format used by the text field.
>       *
> -     * @return DOCUMENT ME!
> +     * @return The format used by the text field.
>       */
>      public DecimalFormat getFormat()
>      {
> -      return null;
> +      NumberFormatter formatter = (NumberFormatter) ftf.getFormatter();
> +      return (DecimalFormat) formatter.getFormat();
>      }
>  
> +    /**
> +     * Returns the model used by the editor's [EMAIL PROTECTED] JSpinner} 
> component,
> +     * cast to a [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 
> SpinnerListModel}.
> @@ -279,6 +353,11 @@
>        super(spinner);
>      }
>  
> +    /**
> +     * Returns the spinner's model cast as a [EMAIL PROTECTED] 
> SpinnerListModel}.
> +     * 
> +     * @return The spinner's model.
> +     */
>      public SpinnerListModel getModel()
>      {
>        return (SpinnerListModel) getSpinner().getModel();
> @@ -299,9 +378,6 @@
>      /** The serialVersionUID. */
>      private static final long serialVersionUID = -4279356973770397815L;
>  
> -    /** The DateFormat instance used to format the date. */
> -    SimpleDateFormat dateFormat;
> -
>      /**
>       * Creates a new instance of DateEditor for the specified
>       * <code>JSpinner</code>.
> @@ -312,7 +388,10 @@
>      public DateEditor(JSpinner spinner)
>      {
>        super(spinner);
> -      init(new SimpleDateFormat());
> +      DateEditorFormatter nef = new DateEditorFormatter();
> +      nef.setMinimum(getModel().getStart());
> +      nef.setMaximum(getModel().getEnd());
> +      ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
>      }
>  
>      /**
> @@ -329,26 +408,10 @@
>      public DateEditor(JSpinner spinner, String dateFormatPattern)
>      {
>        super(spinner);
> -      init(new SimpleDateFormat(dateFormatPattern));
> -    }
> -
> -    /**
> -     * Initializes the JFormattedTextField for this editor.
> -     *
> -     * @param format the date format to use in the formatted text field
> -     */
> -    private void init(SimpleDateFormat format)
> -    {
> -      dateFormat = format;
> -      getTextField().setFormatterFactory(
> -        new JFormattedTextField.AbstractFormatterFactory()
> -        {
> -          public JFormattedTextField.AbstractFormatter
> -          getFormatter(JFormattedTextField ftf)
> -          {
> -            return new DateFormatter(dateFormat);
> -          }
> -        });
> +      DateEditorFormatter nef = new DateEditorFormatter(dateFormatPattern);
> +      nef.setMinimum(getModel().getStart());
> +      nef.setMaximum(getModel().getEnd());
> +      ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
>      }
>  
>      /**
> @@ -360,7 +423,8 @@
>       */
>      public SimpleDateFormat getFormat()
>      {
> -      return dateFormat;
> +      DateFormatter formatter = (DateFormatter) ftf.getFormatter();
> +      return (SimpleDateFormat) formatter.getFormat();
>      }
>  
>      /**
> @@ -374,25 +438,59 @@
>      }
>    }
>  
> -  private static final long serialVersionUID = 3412663575706551720L;
> +  static class DateEditorFormatter 
> +    extends DateFormatter
> +  {
> +    public DateEditorFormatter() 
> +    {
> +      super(DateFormat.getInstance());
> +    }
> +    public DateEditorFormatter(String dateFormatPattern)
> +    {
> +      super(new SimpleDateFormat(dateFormatPattern));
> +    }
> +  }
>  
> -  /** DOCUMENT ME! */
> +  /** 
> +   * A listener that forwards [EMAIL PROTECTED] ChangeEvent} notifications 
> from the model
> +   * to the [EMAIL PROTECTED] 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;
>  
> -  /** DOCUMENT ME! */
> +  /** The current editor. */
>    private JComponent editor;
>  
> -  /** DOCUMENT ME! */
> -  private ChangeListener listener = new ChangeListener()
> -    {
> -      public void stateChanged(ChangeEvent evt)
> -      {
> -     fireStateChanged();
> -      }
> -    };
> +  private static final long serialVersionUID = 3412663575706551720L;
>  
>    /**
> -   * Creates a JSpinner with <code>SpinnerNumberModel</code>
> +   * Creates a new <code>JSpinner</code> with default instance of 
> +   * [EMAIL PROTECTED] SpinnerNumberModel} (that is, a model with value 0, 
> step size 1, 
> +   * and no upper or lower limit).
>     *
>     * @see javax.swing.SpinnerNumberModel
>     */
> @@ -402,15 +500,19 @@
>    }
>  
>    /**
> -   * Creates a JSpinner with the specific model and sets the default editor
> -   *
> -   * @param model DOCUMENT ME!
> +   * Creates a new <code>JSpinner with the specified model.  The 
> +   * [EMAIL PROTECTED] #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;
> -    model.addChangeListener(listener);
> -    setEditor(createEditor(model));
> +    this.editor = createEditor(model);
> +    model.addChangeListener(new ModelListener());
>      updateUI();
>    }
>  
> @@ -439,12 +541,13 @@
>    }
>  
>    /**
> -   * Changes the current editor to the new editor. This methods should remove
> -   * the old listeners (if any) and adds the new listeners (if any).
> +   * Changes the current editor to the new editor. The old editor is
> +   * removed from the spinner's [EMAIL PROTECTED] ChangeEvent} list.
>     *
> -   * @param editor the new editor
> +   * @param editor the new editor (<code>null</code> not permitted.
>     *
> -   * @throws IllegalArgumentException DOCUMENT ME!
> +   * @throws IllegalArgumentException if <code>editor</code> is 
> +   *                                  <code>null</code>.
>     *
>     * @see #getEditor
>     */
> @@ -453,21 +556,22 @@
>      if (editor == null)
>        throw new IllegalArgumentException("editor may not be null");
>  
> -    if (this.editor instanceof DefaultEditor)
> -      ((DefaultEditor) editor).dismiss(this);
> -    else if (this.editor instanceof ChangeListener)
> -      removeChangeListener((ChangeListener) this.editor);
> -
> -    if (editor instanceof ChangeListener)
> -      addChangeListener((ChangeListener) editor);
> -
> +    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);
>    }
>  
>    /**
> -   * Gets the underly model.
> +   * Returns the model used by the [EMAIL PROTECTED] JSpinner} component.
>     *
> -   * @return the underly model
> +   * @return The model.
> +   * 
> +   * @see #setModel(SpinnerModel)
>     */
>    public SpinnerModel getModel()
>    {
> @@ -492,9 +596,7 @@
>      SpinnerModel oldModel = model;
>      model = newModel;
>      firePropertyChange("model", oldModel, newModel);
> -
> -    if (editor == null)
> -      setEditor(createEditor(model));
> +    setEditor(createEditor(model));
>    }
>  
>    /**
> @@ -545,9 +647,9 @@
>    }
>  
>    /**
> -   * DOCUMENT ME!
> +   * Sets the value in the model.
>     *
> -   * @param value DOCUMENT ME!
> +   * @param value the new value.
>     */
>    public void setValue(Object value)
>    {
> @@ -555,10 +657,10 @@
>    }
>  
>    /**
> -   * This method returns a name to identify which look and feel class will be
> +   * Returns the ID that identifies which look and feel class will be
>     * the UI delegate for this spinner.
>     *
> -   * @return The UIClass identifier. "SpinnerUI"
> +   * @return <code>"SpinnerUI"</code>.
>     */
>    public String getUIClassID()
>    {
> @@ -575,7 +677,7 @@
>    }
>  
>    /**
> -   * This method sets the spinner's UI delegate.
> +   * Sets the UI delegate for the component.
>     *
>     * @param ui The spinner's UI delegate.
>     */
> @@ -628,14 +730,11 @@
>    }
>  
>    /**
> -   * Creates an editor for this <code>JSpinner</code>. Really, it should be a
> -   * <code>JSpinner.DefaultEditor</code>, but since that should be
> -   * implemented by a JFormattedTextField, and one is not written, I am just
> -   * using a dummy one backed by a JLabel.
> +   * Creates an editor that is appropriate for the specified 
> <code>model</code>.
>     *
> -   * @param model DOCUMENT ME!
> +   * @param model the model.
>     *
> -   * @return the default editor
> +   * @return The editor.
>     */
>    protected JComponent createEditor(SpinnerModel model)
>    {
> @@ -643,6 +742,8 @@
>        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);
>    }
> Index: javax/swing/plaf/basic/BasicSpinnerUI.java
> ===================================================================
> RCS file: 
> /sources/classpath/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java,v
> retrieving revision 1.7
> diff -u -r1.7 BasicSpinnerUI.java
> --- javax/swing/plaf/basic/BasicSpinnerUI.java        12 Oct 2005 12:10:00 
> -0000      1.7
> +++ javax/swing/plaf/basic/BasicSpinnerUI.java        15 Feb 2006 15:57:30 
> -0000
> @@ -1,5 +1,5 @@
> -/* SpinnerUI.java --
> -   Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc.
> +/* BasicSpinnerUI.java --
> +   Copyright (C) 2003, 2004, 2005, 2006,  Free Software Foundation, Inc.
>  
>  This file is part of GNU Classpath.
>  
> @@ -41,6 +41,7 @@
>  import java.awt.Component;
>  import java.awt.Container;
>  import java.awt.Dimension;
> +import java.awt.Font;
>  import java.awt.Insets;
>  import java.awt.LayoutManager;
>  import java.awt.event.ActionEvent;
> @@ -59,22 +60,21 @@
>  import javax.swing.plaf.SpinnerUI;
>  
>  /**
> - * DOCUMENT ME!
> + * A UI delegate for the [EMAIL PROTECTED] JSpinner} component.
>   *
>   * @author Ka-Hing Cheung
>   *
> - * @see javax.swing.JSpinner
>   * @since 1.4
>   */
>  public class BasicSpinnerUI extends SpinnerUI
>  {
>    /**
> -   * Creates a new <code>ComponentUI</code> for the specified
> +   * Creates a new <code>BasicSpinnerUI</code> for the specified
>     * <code>JComponent</code>
>     *
> -   * @param c DOCUMENT ME!
> +   * @param c  the component (ignored).
>     *
> -   * @return a ComponentUI
> +   * @return A new instance of [EMAIL PROTECTED] BasicSpinnerUI}.
>     */
>    public static ComponentUI createUI(JComponent c)
>    {
> @@ -144,14 +144,15 @@
>    {
>      return new PropertyChangeListener()
>        {
> -     public void propertyChange(PropertyChangeEvent evt)
> -     {
> -       // FIXME: Add check for enabled property change. Need to
> -       // disable the buttons.
> -       if ("editor".equals(evt.getPropertyName()))
> -         BasicSpinnerUI.this.replaceEditor((JComponent) evt.getOldValue(),
> -                                           (JComponent) evt.getNewValue());
> -     }
> +        public void propertyChange(PropertyChangeEvent event)
> +        {
> +          // FIXME: Add check for enabled property change. Need to
> +          // disable the buttons.
> +          if ("editor".equals(event.getPropertyName()))
> +            BasicSpinnerUI.this.replaceEditor((JComponent) 
> event.getOldValue(),
> +                (JComponent) event.getNewValue());
> +          // FIXME: Handle 'font' property change
> +        }
>        };
>    }
>  
> @@ -169,6 +170,12 @@
>      LookAndFeel.installColorsAndFont(spinner, "Spinner.background",
>                                       "Spinner.foreground", "Spinner.font");
>      LookAndFeel.installBorder(spinner, "Spinner.border");
> +    JComponent e = spinner.getEditor();
> +    if (e instanceof JSpinner.DefaultEditor)
> +      {
> +        JSpinner.DefaultEditor de = (JSpinner.DefaultEditor) e;
> +        de.getTextField().setBorder(null);  
> +      }
>      spinner.setLayout(createLayout());
>      spinner.setOpaque(true);
>    }
> @@ -352,7 +359,8 @@
>    private PropertyChangeListener listener = createPropertyChangeListener();
>  
>    /**
> -   * DOCUMENT ME!
> +   * A layout manager for the [EMAIL PROTECTED] JSpinner} component.  The 
> spinner has
> +   * three subcomponents: an editor, a 'next' button and a 'previous' button.
>     */
>    private class DefaultLayoutManager implements LayoutManager
>    {
> @@ -365,58 +373,52 @@
>      {
>        synchronized (parent.getTreeLock())
>          {
> -       Insets i = parent.getInsets();
> -       boolean l2r = parent.getComponentOrientation().isLeftToRight();
> -       /*
> -         --------------    --------------
> -         |        | n |    | n |        |
> -         |   e    | - | or | - |   e    |
> -         |        | p |    | p |        |
> -         --------------    --------------
> -       */
> -       Dimension e = minSize(editor);
> -       Dimension n = minSize(next);
> -       Dimension p = minSize(previous);
> -       Dimension s = spinner.getPreferredSize();
> -
> -       int x = l2r ? i.left : i.right;
> -       int y = i.top;
> -       int w = Math.max(p.width, n.width);
> -       int h = Math.max(p.height, n.height);
> -       h = Math.max(h, e.height / 2);
> -       int e_width = s.width - w;
> -
> -       if (l2r)
> -         {
> -           setBounds(editor, x, y + (s.height - e.height) / 2, e_width,
> -                     e.height);
> -           x += e_width;
> -
> -           setBounds(next, x, y, w, h);
> -           y += h;
> -
> -           setBounds(previous, x, y, w, h);
> -         }
> -       else
> -         {
> -           setBounds(next, x, y + (s.height - e.height) / 2, w, h);
> -           y += h;
> -
> -           setBounds(previous, x, y, w, h);
> -           x += w;
> -           y -= h;
> -
> -           setBounds(editor, x, y, e_width, e.height);
> -         }
> +          Insets i = parent.getInsets();
> +          boolean l2r = parent.getComponentOrientation().isLeftToRight();
> +          /*
> +            --------------    --------------
> +            |        | n |    | n |        |
> +            |   e    | - | or | - |   e    |
> +            |        | p |    | p |        |
> +            --------------    --------------
> +          */
> +          Dimension e = prefSize(editor);
> +          Dimension n = prefSize(next);
> +          Dimension p = prefSize(previous);
> +          Dimension s = spinner.getPreferredSize();
> +
> +          int x = l2r ? i.left : i.right;
> +          int y = i.top;
> +          int w = Math.max(p.width, n.width);
> +          int h = e.height / 2;
> +          int e_width = s.width - w - i.left - i.right;
> +
> +          if (l2r)
> +            {
> +              setBounds(editor, x, y, e_width, 2 * h);
> +              x += e_width;
> +              setBounds(next, x, y, w, h);
> +              y += h;
> +              setBounds(previous, x, y, w, h);
> +            }
> +          else
> +            {
> +              setBounds(next, x, y + (s.height - e.height) / 2, w, h);
> +              y += h;
> +              setBounds(previous, x, y + (s.height - e.height) / 2, w, h);
> +              x += w;
> +              y -= h;
> +              setBounds(editor, x, y, e_width, e.height);
> +            }
>          }
>      }
>  
>      /**
> -     * DOCUMENT ME!
> +     * Calculates the minimum layout size.
>       *
> -     * @param parent DOCUMENT ME!
> +     * @param parent  the parent.
>       *
> -     * @return DOCUMENT ME!
> +     * @return The minimum layout size.
>       */
>      public Dimension minimumLayoutSize(Container parent)
>      {
> @@ -424,36 +426,32 @@
>  
>        if (editor != null)
>          {
> -       Dimension tmp = editor.getMinimumSize();
> -       d.width += tmp.width;
> -       d.height = tmp.height;
> +          Dimension tmp = editor.getMinimumSize();
> +          d.width += tmp.width;
> +          d.height = tmp.height;
>          }
>  
>        int nextWidth = 0;
>        int previousWidth = 0;
> -      int otherHeight = 0;
>  
>        if (next != null)
>          {
> -       Dimension tmp = next.getMinimumSize();
> -       nextWidth = tmp.width;
> -       otherHeight += tmp.height;
> +          Dimension tmp = next.getMinimumSize();
> +          nextWidth = tmp.width;
>          }
>        if (previous != null)
>          {
> -       Dimension tmp = previous.getMinimumSize();
> -       previousWidth = tmp.width;
> -       otherHeight += tmp.height;
> +          Dimension tmp = previous.getMinimumSize();
> +          previousWidth = tmp.width;
>          }
>  
> -      d.height = Math.max(d.height, otherHeight);
>        d.width += Math.max(nextWidth, previousWidth);
>  
>        return d;
>      }
>  
>      /**
> -     * DOCUMENT ME!
> +     * Returns the preferred layout size of the container.
>       *
>       * @param parent DOCUMENT ME!
>       *
> @@ -465,31 +463,29 @@
>  
>        if (editor != null)
>          {
> -       Dimension tmp = editor.getPreferredSize();
> -       d.width += Math.max(tmp.width, 40);
> -       d.height = tmp.height;
> +          Dimension tmp = editor.getPreferredSize();
> +          d.width += Math.max(tmp.width, 40);
> +          d.height = tmp.height;
>          }
>  
>        int nextWidth = 0;
>        int previousWidth = 0;
> -      int otherHeight = 0;
>  
>        if (next != null)
>          {
> -       Dimension tmp = next.getPreferredSize();
> -       nextWidth = tmp.width;
> -       otherHeight += tmp.height;
> +          Dimension tmp = next.getPreferredSize();
> +          nextWidth = tmp.width;
>          }
>        if (previous != null)
>          {
> -       Dimension tmp = previous.getPreferredSize();
> -       previousWidth = tmp.width;
> -       otherHeight += tmp.height;
> +          Dimension tmp = previous.getPreferredSize();
> +          previousWidth = tmp.width;
>          }
>  
> -      d.height = Math.max(d.height, otherHeight);
>        d.width += Math.max(nextWidth, previousWidth);
> -
> +      Insets insets = parent.getInsets();
> +      d.width = d.width + insets.left + insets.right;
> +      d.height = d.height + insets.top + insets.bottom;
>        return d;
>      }
>  
> @@ -501,11 +497,11 @@
>      public void removeLayoutComponent(Component child)
>      {
>        if (child == editor)
> -     editor = null;
> +        editor = null;
>        else if (child == next)
> -     next = null;
> +        next = null;
>        else if (previous == child)
> -     previous = null;
> +        previous = null;
>      }
>  
>      /**
> @@ -517,11 +513,11 @@
>      public void addLayoutComponent(String name, Component child)
>      {
>        if ("Editor".equals(name))
> -     editor = child;
> +        editor = child;
>        else if ("Next".equals(name))
> -     next = child;
> +        next = child;
>        else if ("Previous".equals(name))
> -     previous = child;
> +        previous = child;
>      }
>  
>      /**
> @@ -531,36 +527,36 @@
>       *
>       * @return DOCUMENT ME!
>       */
> -    private Dimension minSize(Component c)
> +    private Dimension prefSize(Component c)
>      {
>        if (c == null)
> -     return new Dimension();
> +        return new Dimension();
>        else
> -     return c.getMinimumSize();
> +        return c.getPreferredSize();
>      }
>  
>      /**
> -     * DOCUMENT ME!
> +     * Sets the bounds for the specified component.
>       *
> -     * @param c DOCUMENT ME!
> -     * @param x DOCUMENT ME!
> -     * @param y DOCUMENT ME!
> -     * @param w DOCUMENT ME!
> -     * @param h DOCUMENT ME!
> +     * @param c  the component.
> +     * @param x  the x-coordinate for the top-left of the component bounds.
> +     * @param y  the y-coordinate for the top-left of the component bounds.
> +     * @param w  the width of the bounds.
> +     * @param h  the height of the bounds.
>       */
>      private void setBounds(Component c, int x, int y, int w, int h)
>      {
>        if (c != null)
> -     c.setBounds(x, y, w, h);
> +        c.setBounds(x, y, w, h);
>      }
>  
> -    /** DOCUMENT ME! */
> +    /** The editor component. */
>      private Component editor;
>  
> -    /** DOCUMENT ME! */
> +    /** The next button. */
>      private Component next;
>  
> -    /** DOCUMENT ME! */
> +    /** The previous button. */
>      private Component previous;
>    }
>  }
> Index: javax/swing/plaf/metal/MetalLookAndFeel.java
> ===================================================================
> RCS file: 
> /sources/classpath/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java,v
> retrieving revision 1.77
> diff -u -r1.77 MetalLookAndFeel.java
> --- javax/swing/plaf/metal/MetalLookAndFeel.java      30 Jan 2006 15:32:21 
> -0000      1.77
> +++ javax/swing/plaf/metal/MetalLookAndFeel.java      15 Feb 2006 15:57:32 
> -0000
> @@ -1171,6 +1171,7 @@
>  
>        "Spinner.arrowButtonInsets", new InsetsUIResource(0, 0, 0, 0),
>        "Spinner.background", getControl(),
> +      "Spinner.border", MetalBorders.getTextFieldBorder(),
>        "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12),
>        "Spinner.foreground", getControl(),
>  

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil

Reply via email to