< prev index next >

src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSpinnerUI.java

Print this page




  34 import javax.swing.event.*;
  35 import javax.swing.plaf.*;
  36 import javax.swing.text.*;
  37 
  38 import java.beans.*;
  39 import java.text.*;
  40 import java.util.*;
  41 import sun.swing.DefaultLookup;
  42 
  43 
  44 /**
  45  * The default Spinner UI delegate.
  46  *
  47  * @author Hans Muller
  48  * @since 1.4
  49  */
  50 public class BasicSpinnerUI extends SpinnerUI
  51 {
  52     /**
  53      * The spinner that we're a UI delegate for.  Initialized by
  54      * the <code>installUI</code> method, and reset to null
  55      * by <code>uninstallUI</code>.
  56      *
  57      * @see #installUI
  58      * @see #uninstallUI
  59      */
  60     protected JSpinner spinner;
  61     private Handler handler;
  62 
  63 
  64     /**
  65      * The mouse/action listeners that are added to the spinner's
  66      * arrow buttons.  These listeners are shared by all
  67      * spinner arrow buttons.
  68      *
  69      * @see #createNextButton
  70      * @see #createPreviousButton
  71      */
  72     private static final ArrowButtonHandler nextButtonHandler = new ArrowButtonHandler("increment", true);
  73     private static final ArrowButtonHandler previousButtonHandler = new ArrowButtonHandler("decrement", false);
  74     private PropertyChangeListener propertyChangeListener;
  75 


  85      * Returns a new instance of BasicSpinnerUI.  SpinnerListUI
  86      * delegates are allocated one per JSpinner.
  87      *
  88      * @param c the JSpinner (not used)
  89      * @see ComponentUI#createUI
  90      * @return a new BasicSpinnerUI object
  91      */
  92     public static ComponentUI createUI(JComponent c) {
  93         return new BasicSpinnerUI();
  94     }
  95 
  96 
  97     private void maybeAdd(Component c, String s) {
  98         if (c != null) {
  99             spinner.add(c, s);
 100         }
 101     }
 102 
 103 
 104     /**
 105      * Calls <code>installDefaults</code>, <code>installListeners</code>,
 106      * and then adds the components returned by <code>createNextButton</code>,
 107      * <code>createPreviousButton</code>, and <code>createEditor</code>.
 108      *
 109      * @param c the JSpinner
 110      * @see #installDefaults
 111      * @see #installListeners
 112      * @see #createNextButton
 113      * @see #createPreviousButton
 114      * @see #createEditor
 115      */
 116     public void installUI(JComponent c) {
 117         this.spinner = (JSpinner)c;
 118         installDefaults();
 119         installListeners();
 120         maybeAdd(createNextButton(), "Next");
 121         maybeAdd(createPreviousButton(), "Previous");
 122         maybeAdd(createEditor(), "Editor");
 123         updateEnabledState();
 124         installKeyboardActions();
 125     }
 126 
 127 
 128     /**
 129      * Calls <code>uninstallDefaults</code>, <code>uninstallListeners</code>,
 130      * and then removes all of the spinners children.
 131      *
 132      * @param c the JSpinner (not used)
 133      */
 134     public void uninstallUI(JComponent c) {
 135         uninstallDefaults();
 136         uninstallListeners();
 137         this.spinner = null;
 138         c.removeAll();
 139     }
 140 
 141 
 142     /**
 143      * Initializes <code>PropertyChangeListener</code> with
 144      * a shared object that delegates interesting PropertyChangeEvents
 145      * to protected methods.
 146      * <p>
 147      * This method is called by <code>installUI</code>.
 148      *
 149      * @see #replaceEditor
 150      * @see #uninstallListeners
 151      */
 152     protected void installListeners() {
 153         propertyChangeListener = createPropertyChangeListener();
 154         spinner.addPropertyChangeListener(propertyChangeListener);
 155         if (DefaultLookup.getBoolean(spinner, this,
 156             "Spinner.disableOnBoundaryValues", false)) {
 157             spinner.addChangeListener(getHandler());
 158         }
 159         JComponent editor = spinner.getEditor();
 160         if (editor != null && editor instanceof JSpinner.DefaultEditor) {
 161             JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
 162             if (tf != null) {
 163                 tf.addFocusListener(nextButtonHandler);
 164                 tf.addFocusListener(previousButtonHandler);
 165             }
 166         }
 167     }
 168 
 169 
 170     /**
 171      * Removes the <code>PropertyChangeListener</code> added
 172      * by installListeners.
 173      * <p>
 174      * This method is called by <code>uninstallUI</code>.
 175      *
 176      * @see #installListeners
 177      */
 178     protected void uninstallListeners() {
 179         spinner.removePropertyChangeListener(propertyChangeListener);
 180         spinner.removeChangeListener(handler);
 181         JComponent editor = spinner.getEditor();
 182         removeEditorBorderListener(editor);
 183         if (editor instanceof JSpinner.DefaultEditor) {
 184             JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
 185             if (tf != null) {
 186                 tf.removeFocusListener(nextButtonHandler);
 187                 tf.removeFocusListener(previousButtonHandler);
 188             }
 189         }
 190         propertyChangeListener = null;
 191         handler = null;
 192     }
 193 
 194 
 195     /**
 196      * Initialize the <code>JSpinner</code> <code>border</code>,
 197      * <code>foreground</code>, and <code>background</code>, properties
 198      * based on the corresponding "Spinner.*" properties from defaults table.
 199      * The <code>JSpinners</code> layout is set to the value returned by
 200      * <code>createLayout</code>.  This method is called by <code>installUI</code>.
 201      *
 202      * @see #uninstallDefaults
 203      * @see #installUI
 204      * @see #createLayout
 205      * @see LookAndFeel#installBorder
 206      * @see LookAndFeel#installColors
 207      */
 208     protected void installDefaults() {
 209         spinner.setLayout(createLayout());
 210         LookAndFeel.installBorder(spinner, "Spinner.border");
 211         LookAndFeel.installColorsAndFont(spinner, "Spinner.background", "Spinner.foreground", "Spinner.font");
 212         LookAndFeel.installProperty(spinner, "opaque", Boolean.TRUE);
 213     }
 214 
 215 
 216     /**
 217      * Sets the <code>JSpinner's</code> layout manager to null.  This
 218      * method is called by <code>uninstallUI</code>.
 219      *
 220      * @see #installDefaults
 221      * @see #uninstallUI
 222      */
 223     protected void uninstallDefaults() {
 224         spinner.setLayout(null);
 225     }
 226 
 227 
 228     private Handler getHandler() {
 229         if (handler == null) {
 230             handler = new Handler();
 231         }
 232         return handler;
 233     }
 234 
 235 
 236     /**
 237      * Installs the necessary listeners on the next button, <code>c</code>,
 238      * to update the <code>JSpinner</code> in response to a user gesture.
 239      *
 240      * @param c Component to install the listeners on
 241      * @throws NullPointerException if <code>c</code> is null.
 242      * @see #createNextButton
 243      * @since 1.5
 244      */
 245     protected void installNextButtonListeners(Component c) {
 246         installButtonListeners(c, nextButtonHandler);
 247     }
 248 
 249     /**
 250      * Installs the necessary listeners on the previous button, <code>c</code>,
 251      * to update the <code>JSpinner</code> in response to a user gesture.
 252      *
 253      * @param c Component to install the listeners on.
 254      * @throws NullPointerException if <code>c</code> is null.
 255      * @see #createPreviousButton
 256      * @since 1.5
 257      */
 258     protected void installPreviousButtonListeners(Component c) {
 259         installButtonListeners(c, previousButtonHandler);
 260     }
 261 
 262     private void installButtonListeners(Component c,
 263                                         ArrowButtonHandler handler) {
 264         if (c instanceof JButton) {
 265             ((JButton)c).addActionListener(handler);
 266         }
 267         c.addMouseListener(handler);
 268     }
 269 
 270     /**
 271      * Creates a <code>LayoutManager</code> that manages the <code>editor</code>,
 272      * <code>nextButton</code>, and <code>previousButton</code>
 273      * children of the JSpinner.  These three children must be
 274      * added with a constraint that identifies their role:
 275      * "Editor", "Next", and "Previous". The default layout manager
 276      * can handle the absence of any of these children.
 277      *
 278      * @return a LayoutManager for the editor, next button, and previous button.
 279      * @see #createNextButton
 280      * @see #createPreviousButton
 281      * @see #createEditor
 282      */
 283     protected LayoutManager createLayout() {
 284         return getHandler();
 285     }
 286 
 287 
 288     /**
 289      * Creates a <code>PropertyChangeListener</code> that can be
 290      * added to the JSpinner itself.  Typically, this listener
 291      * will call replaceEditor when the "editor" property changes,
 292      * since it's the <code>SpinnerUI's</code> responsibility to
 293      * add the editor to the JSpinner (and remove the old one).
 294      * This method is called by <code>installListeners</code>.
 295      *
 296      * @return A PropertyChangeListener for the JSpinner itself
 297      * @see #installListeners
 298      */
 299     protected PropertyChangeListener createPropertyChangeListener() {
 300         return getHandler();
 301     }
 302 
 303 
 304     /**
 305      * Creates a decrement button, i.e. component that replaces the spinner
 306      * value with the object returned by <code>spinner.getPreviousValue</code>.
 307      * By default the <code>previousButton</code> is a {@code JButton}. If the
 308      * decrement button is not needed this method should return {@code null}.
 309      *
 310      * @return a component that will replace the spinner's value with the
 311      *     previous value in the sequence, or {@code null}
 312      * @see #installUI
 313      * @see #createNextButton
 314      * @see #installPreviousButtonListeners
 315      */
 316     protected Component createPreviousButton() {
 317         Component c = createArrowButton(SwingConstants.SOUTH);
 318         c.setName("Spinner.previousButton");
 319         installPreviousButtonListeners(c);
 320         return c;
 321     }
 322 
 323 
 324     /**
 325      * Creates an increment button, i.e. component that replaces the spinner
 326      * value with the object returned by <code>spinner.getNextValue</code>.
 327      * By default the <code>nextButton</code> is a {@code JButton}. If the
 328      * increment button is not needed this method should return {@code null}.
 329      *
 330      * @return a component that will replace the spinner's value with the
 331      *     next value in the sequence, or {@code null}
 332      * @see #installUI
 333      * @see #createPreviousButton
 334      * @see #installNextButtonListeners
 335      */
 336     protected Component createNextButton() {
 337         Component c = createArrowButton(SwingConstants.NORTH);
 338         c.setName("Spinner.nextButton");
 339         installNextButtonListeners(c);
 340         return c;
 341     }
 342 
 343     private Component createArrowButton(int direction) {
 344         JButton b = new BasicArrowButton(direction);
 345         Border buttonBorder = UIManager.getBorder("Spinner.arrowButtonBorder");
 346         if (buttonBorder instanceof UIResource) {
 347             // Wrap the border to avoid having the UIResource be replaced by
 348             // the ButtonUI. This is the opposite of using BorderUIResource.
 349             b.setBorder(new CompoundBorder(buttonBorder, null));
 350         } else {
 351             b.setBorder(buttonBorder);
 352         }
 353         b.setInheritsPopupMenu(true);
 354         return b;
 355     }
 356 
 357 
 358     /**
 359      * This method is called by installUI to get the editor component
 360      * of the <code>JSpinner</code>.  By default it just returns
 361      * <code>JSpinner.getEditor()</code>.  Subclasses can override
 362      * <code>createEditor</code> to return a component that contains
 363      * the spinner's editor or null, if they're going to handle adding
 364      * the editor to the <code>JSpinner</code> in an
 365      * <code>installUI</code> override.
 366      * <p>
 367      * Typically this method would be overridden to wrap the editor
 368      * with a container with a custom border, since one can't assume
 369      * that the editors border can be set directly.
 370      * <p>
 371      * The <code>replaceEditor</code> method is called when the spinners
 372      * editor is changed with <code>JSpinner.setEditor</code>.  If you've
 373      * overriden this method, then you'll probably want to override
 374      * <code>replaceEditor</code> as well.
 375      *
 376      * @return the JSpinners editor JComponent, spinner.getEditor() by default
 377      * @see #installUI
 378      * @see #replaceEditor
 379      * @see JSpinner#getEditor
 380      */
 381     protected JComponent createEditor() {
 382         JComponent editor = spinner.getEditor();
 383         maybeRemoveEditorBorder(editor);
 384         installEditorBorderListener(editor);
 385         editor.setInheritsPopupMenu(true);
 386         updateEditorAlignment(editor);
 387         return editor;
 388     }
 389 
 390 
 391     /**
 392      * Called by the <code>PropertyChangeListener</code> when the
 393      * <code>JSpinner</code> editor property changes.  It's the responsibility
 394      * of this method to remove the old editor and add the new one.  By
 395      * default this operation is just:
 396      * <pre>
 397      * spinner.remove(oldEditor);
 398      * spinner.add(newEditor, "Editor");
 399      * </pre>
 400      * The implementation of <code>replaceEditor</code> should be coordinated
 401      * with the <code>createEditor</code> method.
 402      *
 403      * @param oldEditor an old instance of editor
 404      * @param newEditor a new instance of editor
 405      * @see #createEditor
 406      * @see #createPropertyChangeListener
 407      */
 408     protected void replaceEditor(JComponent oldEditor, JComponent newEditor) {
 409         spinner.remove(oldEditor);
 410         maybeRemoveEditorBorder(newEditor);
 411         installEditorBorderListener(newEditor);
 412         newEditor.setInheritsPopupMenu(true);
 413         spinner.add(newEditor, "Editor");
 414     }
 415 
 416     private void updateEditorAlignment(JComponent editor) {
 417         if (editor instanceof JSpinner.DefaultEditor) {
 418             // if editor alignment isn't set in LAF, we get 0 (CENTER) here
 419             int alignment = UIManager.getInt("Spinner.editorAlignment");
 420             JTextField text = ((JSpinner.DefaultEditor)editor).getTextField();
 421             text.setHorizontalAlignment(alignment);


 460             }
 461         }
 462     }
 463 
 464     private void removeEditorBorderListener(JComponent editor) {
 465         if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
 466             if (editor instanceof JPanel &&
 467                 editor.getComponentCount() > 0) {
 468 
 469                 editor = (JComponent)editor.getComponent(0);
 470             }
 471             if (editor != null) {
 472                 editor.removePropertyChangeListener(getHandler());
 473             }
 474         }
 475     }
 476 
 477 
 478     /**
 479      * Updates the enabled state of the children Components based on the
 480      * enabled state of the <code>JSpinner</code>.
 481      */
 482     private void updateEnabledState() {
 483         updateEnabledState(spinner, spinner.isEnabled());
 484     }
 485 
 486 
 487     /**
 488      * Recursively updates the enabled state of the child
 489      * <code>Component</code>s of <code>c</code>.
 490      */
 491     private void updateEnabledState(Container c, boolean enabled) {
 492         for (int counter = c.getComponentCount() - 1; counter >= 0;counter--) {
 493             Component child = c.getComponent(counter);
 494 
 495             if (DefaultLookup.getBoolean(spinner, this,
 496                 "Spinner.disableOnBoundaryValues", false)) {
 497                 SpinnerModel model = spinner.getModel();
 498                 if (child.getName() == "Spinner.nextButton" &&
 499                     model.getNextValue() == null) {
 500                     child.setEnabled(false);
 501                 }
 502                 else if (child.getName() == "Spinner.previousButton" &&
 503                          model.getPreviousValue() == null) {
 504                     child.setEnabled(false);
 505                 }
 506                 else {
 507                     child.setEnabled(enabled);
 508                 }
 509             }


 518 
 519 
 520     /**
 521      * Installs the keyboard Actions onto the JSpinner.
 522      *
 523      * @since 1.5
 524      */
 525     protected void installKeyboardActions() {
 526         InputMap iMap = getInputMap(JComponent.
 527                                    WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 528 
 529         SwingUtilities.replaceUIInputMap(spinner, JComponent.
 530                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 531                                          iMap);
 532 
 533         LazyActionMap.installLazyActionMap(spinner, BasicSpinnerUI.class,
 534                 "Spinner.actionMap");
 535     }
 536 
 537     /**
 538      * Returns the InputMap to install for <code>condition</code>.
 539      */
 540     private InputMap getInputMap(int condition) {
 541         if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
 542             return (InputMap)DefaultLookup.get(spinner, this,
 543                     "Spinner.ancestorInputMap");
 544         }
 545         return null;
 546     }
 547 
 548     static void loadActionMap(LazyActionMap map) {
 549         map.put("increment", nextButtonHandler);
 550         map.put("decrement", previousButtonHandler);
 551     }
 552 
 553     /**
 554      * Returns the baseline.
 555      *
 556      * @throws NullPointerException {@inheritDoc}
 557      * @throws IllegalArgumentException {@inheritDoc}
 558      * @see javax.swing.JComponent#getBaseline(int, int)




  34 import javax.swing.event.*;
  35 import javax.swing.plaf.*;
  36 import javax.swing.text.*;
  37 
  38 import java.beans.*;
  39 import java.text.*;
  40 import java.util.*;
  41 import sun.swing.DefaultLookup;
  42 
  43 
  44 /**
  45  * The default Spinner UI delegate.
  46  *
  47  * @author Hans Muller
  48  * @since 1.4
  49  */
  50 public class BasicSpinnerUI extends SpinnerUI
  51 {
  52     /**
  53      * The spinner that we're a UI delegate for.  Initialized by
  54      * the {@code installUI} method, and reset to null
  55      * by {@code uninstallUI}.
  56      *
  57      * @see #installUI
  58      * @see #uninstallUI
  59      */
  60     protected JSpinner spinner;
  61     private Handler handler;
  62 
  63 
  64     /**
  65      * The mouse/action listeners that are added to the spinner's
  66      * arrow buttons.  These listeners are shared by all
  67      * spinner arrow buttons.
  68      *
  69      * @see #createNextButton
  70      * @see #createPreviousButton
  71      */
  72     private static final ArrowButtonHandler nextButtonHandler = new ArrowButtonHandler("increment", true);
  73     private static final ArrowButtonHandler previousButtonHandler = new ArrowButtonHandler("decrement", false);
  74     private PropertyChangeListener propertyChangeListener;
  75 


  85      * Returns a new instance of BasicSpinnerUI.  SpinnerListUI
  86      * delegates are allocated one per JSpinner.
  87      *
  88      * @param c the JSpinner (not used)
  89      * @see ComponentUI#createUI
  90      * @return a new BasicSpinnerUI object
  91      */
  92     public static ComponentUI createUI(JComponent c) {
  93         return new BasicSpinnerUI();
  94     }
  95 
  96 
  97     private void maybeAdd(Component c, String s) {
  98         if (c != null) {
  99             spinner.add(c, s);
 100         }
 101     }
 102 
 103 
 104     /**
 105      * Calls {@code installDefaults}, {@code installListeners},
 106      * and then adds the components returned by {@code createNextButton},
 107      * {@code createPreviousButton}, and {@code createEditor}.
 108      *
 109      * @param c the JSpinner
 110      * @see #installDefaults
 111      * @see #installListeners
 112      * @see #createNextButton
 113      * @see #createPreviousButton
 114      * @see #createEditor
 115      */
 116     public void installUI(JComponent c) {
 117         this.spinner = (JSpinner)c;
 118         installDefaults();
 119         installListeners();
 120         maybeAdd(createNextButton(), "Next");
 121         maybeAdd(createPreviousButton(), "Previous");
 122         maybeAdd(createEditor(), "Editor");
 123         updateEnabledState();
 124         installKeyboardActions();
 125     }
 126 
 127 
 128     /**
 129      * Calls {@code uninstallDefaults}, {@code uninstallListeners},
 130      * and then removes all of the spinners children.
 131      *
 132      * @param c the JSpinner (not used)
 133      */
 134     public void uninstallUI(JComponent c) {
 135         uninstallDefaults();
 136         uninstallListeners();
 137         this.spinner = null;
 138         c.removeAll();
 139     }
 140 
 141 
 142     /**
 143      * Initializes {@code PropertyChangeListener} with
 144      * a shared object that delegates interesting PropertyChangeEvents
 145      * to protected methods.
 146      * <p>
 147      * This method is called by {@code installUI}.
 148      *
 149      * @see #replaceEditor
 150      * @see #uninstallListeners
 151      */
 152     protected void installListeners() {
 153         propertyChangeListener = createPropertyChangeListener();
 154         spinner.addPropertyChangeListener(propertyChangeListener);
 155         if (DefaultLookup.getBoolean(spinner, this,
 156             "Spinner.disableOnBoundaryValues", false)) {
 157             spinner.addChangeListener(getHandler());
 158         }
 159         JComponent editor = spinner.getEditor();
 160         if (editor != null && editor instanceof JSpinner.DefaultEditor) {
 161             JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
 162             if (tf != null) {
 163                 tf.addFocusListener(nextButtonHandler);
 164                 tf.addFocusListener(previousButtonHandler);
 165             }
 166         }
 167     }
 168 
 169 
 170     /**
 171      * Removes the {@code PropertyChangeListener} added
 172      * by installListeners.
 173      * <p>
 174      * This method is called by {@code uninstallUI}.
 175      *
 176      * @see #installListeners
 177      */
 178     protected void uninstallListeners() {
 179         spinner.removePropertyChangeListener(propertyChangeListener);
 180         spinner.removeChangeListener(handler);
 181         JComponent editor = spinner.getEditor();
 182         removeEditorBorderListener(editor);
 183         if (editor instanceof JSpinner.DefaultEditor) {
 184             JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
 185             if (tf != null) {
 186                 tf.removeFocusListener(nextButtonHandler);
 187                 tf.removeFocusListener(previousButtonHandler);
 188             }
 189         }
 190         propertyChangeListener = null;
 191         handler = null;
 192     }
 193 
 194 
 195     /**
 196      * Initialize the {@code JSpinner border},
 197      * {@code foreground}, and {@code background}, properties
 198      * based on the corresponding "Spinner.*" properties from defaults table.
 199      * The {@code JSpinners} layout is set to the value returned by
 200      * {@code createLayout}.  This method is called by {@code installUI}.
 201      *
 202      * @see #uninstallDefaults
 203      * @see #installUI
 204      * @see #createLayout
 205      * @see LookAndFeel#installBorder
 206      * @see LookAndFeel#installColors
 207      */
 208     protected void installDefaults() {
 209         spinner.setLayout(createLayout());
 210         LookAndFeel.installBorder(spinner, "Spinner.border");
 211         LookAndFeel.installColorsAndFont(spinner, "Spinner.background", "Spinner.foreground", "Spinner.font");
 212         LookAndFeel.installProperty(spinner, "opaque", Boolean.TRUE);
 213     }
 214 
 215 
 216     /**
 217      * Sets the {@code JSpinner's} layout manager to null.  This
 218      * method is called by {@code uninstallUI}.
 219      *
 220      * @see #installDefaults
 221      * @see #uninstallUI
 222      */
 223     protected void uninstallDefaults() {
 224         spinner.setLayout(null);
 225     }
 226 
 227 
 228     private Handler getHandler() {
 229         if (handler == null) {
 230             handler = new Handler();
 231         }
 232         return handler;
 233     }
 234 
 235 
 236     /**
 237      * Installs the necessary listeners on the next button, {@code c},
 238      * to update the {@code JSpinner} in response to a user gesture.
 239      *
 240      * @param c Component to install the listeners on
 241      * @throws NullPointerException if {@code c} is null.
 242      * @see #createNextButton
 243      * @since 1.5
 244      */
 245     protected void installNextButtonListeners(Component c) {
 246         installButtonListeners(c, nextButtonHandler);
 247     }
 248 
 249     /**
 250      * Installs the necessary listeners on the previous button, {@code c},
 251      * to update the {@code JSpinner} in response to a user gesture.
 252      *
 253      * @param c Component to install the listeners on.
 254      * @throws NullPointerException if {@code c} is null.
 255      * @see #createPreviousButton
 256      * @since 1.5
 257      */
 258     protected void installPreviousButtonListeners(Component c) {
 259         installButtonListeners(c, previousButtonHandler);
 260     }
 261 
 262     private void installButtonListeners(Component c,
 263                                         ArrowButtonHandler handler) {
 264         if (c instanceof JButton) {
 265             ((JButton)c).addActionListener(handler);
 266         }
 267         c.addMouseListener(handler);
 268     }
 269 
 270     /**
 271      * Creates a {@code LayoutManager} that manages the {@code editor},
 272      * {@code nextButton}, and {@code previousButton}
 273      * children of the JSpinner.  These three children must be
 274      * added with a constraint that identifies their role:
 275      * "Editor", "Next", and "Previous". The default layout manager
 276      * can handle the absence of any of these children.
 277      *
 278      * @return a LayoutManager for the editor, next button, and previous button.
 279      * @see #createNextButton
 280      * @see #createPreviousButton
 281      * @see #createEditor
 282      */
 283     protected LayoutManager createLayout() {
 284         return getHandler();
 285     }
 286 
 287 
 288     /**
 289      * Creates a {@code PropertyChangeListener} that can be
 290      * added to the JSpinner itself.  Typically, this listener
 291      * will call replaceEditor when the "editor" property changes,
 292      * since it's the {@code SpinnerUI's} responsibility to
 293      * add the editor to the JSpinner (and remove the old one).
 294      * This method is called by {@code installListeners}.
 295      *
 296      * @return A PropertyChangeListener for the JSpinner itself
 297      * @see #installListeners
 298      */
 299     protected PropertyChangeListener createPropertyChangeListener() {
 300         return getHandler();
 301     }
 302 
 303 
 304     /**
 305      * Creates a decrement button, i.e. component that replaces the spinner
 306      * value with the object returned by {@code spinner.getPreviousValue}.
 307      * By default the {@code previousButton} is a {@code JButton}. If the
 308      * decrement button is not needed this method should return {@code null}.
 309      *
 310      * @return a component that will replace the spinner's value with the
 311      *     previous value in the sequence, or {@code null}
 312      * @see #installUI
 313      * @see #createNextButton
 314      * @see #installPreviousButtonListeners
 315      */
 316     protected Component createPreviousButton() {
 317         Component c = createArrowButton(SwingConstants.SOUTH);
 318         c.setName("Spinner.previousButton");
 319         installPreviousButtonListeners(c);
 320         return c;
 321     }
 322 
 323 
 324     /**
 325      * Creates an increment button, i.e. component that replaces the spinner
 326      * value with the object returned by {@code spinner.getNextValue}.
 327      * By default the {@code nextButton} is a {@code JButton}. If the
 328      * increment button is not needed this method should return {@code null}.
 329      *
 330      * @return a component that will replace the spinner's value with the
 331      *     next value in the sequence, or {@code null}
 332      * @see #installUI
 333      * @see #createPreviousButton
 334      * @see #installNextButtonListeners
 335      */
 336     protected Component createNextButton() {
 337         Component c = createArrowButton(SwingConstants.NORTH);
 338         c.setName("Spinner.nextButton");
 339         installNextButtonListeners(c);
 340         return c;
 341     }
 342 
 343     private Component createArrowButton(int direction) {
 344         JButton b = new BasicArrowButton(direction);
 345         Border buttonBorder = UIManager.getBorder("Spinner.arrowButtonBorder");
 346         if (buttonBorder instanceof UIResource) {
 347             // Wrap the border to avoid having the UIResource be replaced by
 348             // the ButtonUI. This is the opposite of using BorderUIResource.
 349             b.setBorder(new CompoundBorder(buttonBorder, null));
 350         } else {
 351             b.setBorder(buttonBorder);
 352         }
 353         b.setInheritsPopupMenu(true);
 354         return b;
 355     }
 356 
 357 
 358     /**
 359      * This method is called by installUI to get the editor component
 360      * of the {@code JSpinner}.  By default it just returns
 361      * {@code JSpinner.getEditor()}.  Subclasses can override
 362      * {@code createEditor} to return a component that contains
 363      * the spinner's editor or null, if they're going to handle adding
 364      * the editor to the {@code JSpinner} in an
 365      * {@code installUI} override.
 366      * <p>
 367      * Typically this method would be overridden to wrap the editor
 368      * with a container with a custom border, since one can't assume
 369      * that the editors border can be set directly.
 370      * <p>
 371      * The {@code replaceEditor} method is called when the spinners
 372      * editor is changed with {@code JSpinner.setEditor}.  If you've
 373      * overriden this method, then you'll probably want to override
 374      * {@code replaceEditor} as well.
 375      *
 376      * @return the JSpinners editor JComponent, spinner.getEditor() by default
 377      * @see #installUI
 378      * @see #replaceEditor
 379      * @see JSpinner#getEditor
 380      */
 381     protected JComponent createEditor() {
 382         JComponent editor = spinner.getEditor();
 383         maybeRemoveEditorBorder(editor);
 384         installEditorBorderListener(editor);
 385         editor.setInheritsPopupMenu(true);
 386         updateEditorAlignment(editor);
 387         return editor;
 388     }
 389 
 390 
 391     /**
 392      * Called by the {@code PropertyChangeListener} when the
 393      * {@code JSpinner} editor property changes.  It's the responsibility
 394      * of this method to remove the old editor and add the new one.  By
 395      * default this operation is just:
 396      * <pre>
 397      * spinner.remove(oldEditor);
 398      * spinner.add(newEditor, "Editor");
 399      * </pre>
 400      * The implementation of {@code replaceEditor} should be coordinated
 401      * with the {@code createEditor} method.
 402      *
 403      * @param oldEditor an old instance of editor
 404      * @param newEditor a new instance of editor
 405      * @see #createEditor
 406      * @see #createPropertyChangeListener
 407      */
 408     protected void replaceEditor(JComponent oldEditor, JComponent newEditor) {
 409         spinner.remove(oldEditor);
 410         maybeRemoveEditorBorder(newEditor);
 411         installEditorBorderListener(newEditor);
 412         newEditor.setInheritsPopupMenu(true);
 413         spinner.add(newEditor, "Editor");
 414     }
 415 
 416     private void updateEditorAlignment(JComponent editor) {
 417         if (editor instanceof JSpinner.DefaultEditor) {
 418             // if editor alignment isn't set in LAF, we get 0 (CENTER) here
 419             int alignment = UIManager.getInt("Spinner.editorAlignment");
 420             JTextField text = ((JSpinner.DefaultEditor)editor).getTextField();
 421             text.setHorizontalAlignment(alignment);


 460             }
 461         }
 462     }
 463 
 464     private void removeEditorBorderListener(JComponent editor) {
 465         if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
 466             if (editor instanceof JPanel &&
 467                 editor.getComponentCount() > 0) {
 468 
 469                 editor = (JComponent)editor.getComponent(0);
 470             }
 471             if (editor != null) {
 472                 editor.removePropertyChangeListener(getHandler());
 473             }
 474         }
 475     }
 476 
 477 
 478     /**
 479      * Updates the enabled state of the children Components based on the
 480      * enabled state of the {@code JSpinner}.
 481      */
 482     private void updateEnabledState() {
 483         updateEnabledState(spinner, spinner.isEnabled());
 484     }
 485 
 486 
 487     /**
 488      * Recursively updates the enabled state of the child
 489      * {@code Component}s of {@code c}.
 490      */
 491     private void updateEnabledState(Container c, boolean enabled) {
 492         for (int counter = c.getComponentCount() - 1; counter >= 0;counter--) {
 493             Component child = c.getComponent(counter);
 494 
 495             if (DefaultLookup.getBoolean(spinner, this,
 496                 "Spinner.disableOnBoundaryValues", false)) {
 497                 SpinnerModel model = spinner.getModel();
 498                 if (child.getName() == "Spinner.nextButton" &&
 499                     model.getNextValue() == null) {
 500                     child.setEnabled(false);
 501                 }
 502                 else if (child.getName() == "Spinner.previousButton" &&
 503                          model.getPreviousValue() == null) {
 504                     child.setEnabled(false);
 505                 }
 506                 else {
 507                     child.setEnabled(enabled);
 508                 }
 509             }


 518 
 519 
 520     /**
 521      * Installs the keyboard Actions onto the JSpinner.
 522      *
 523      * @since 1.5
 524      */
 525     protected void installKeyboardActions() {
 526         InputMap iMap = getInputMap(JComponent.
 527                                    WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 528 
 529         SwingUtilities.replaceUIInputMap(spinner, JComponent.
 530                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 531                                          iMap);
 532 
 533         LazyActionMap.installLazyActionMap(spinner, BasicSpinnerUI.class,
 534                 "Spinner.actionMap");
 535     }
 536 
 537     /**
 538      * Returns the InputMap to install for {@code condition}.
 539      */
 540     private InputMap getInputMap(int condition) {
 541         if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
 542             return (InputMap)DefaultLookup.get(spinner, this,
 543                     "Spinner.ancestorInputMap");
 544         }
 545         return null;
 546     }
 547 
 548     static void loadActionMap(LazyActionMap map) {
 549         map.put("increment", nextButtonHandler);
 550         map.put("decrement", previousButtonHandler);
 551     }
 552 
 553     /**
 554      * Returns the baseline.
 555      *
 556      * @throws NullPointerException {@inheritDoc}
 557      * @throws IllegalArgumentException {@inheritDoc}
 558      * @see javax.swing.JComponent#getBaseline(int, int)


< prev index next >