Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]

    1   /*
    2    * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package javax.swing.plaf.basic;
   27   
   28   import java.awt;
   29   import java.awt.event;
   30   import java.text.ParseException;
   31   
   32   import javax.swing;
   33   import javax.swing.border;
   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   
   76   
   77       /**
   78        * Used by the default LayoutManager class - SpinnerLayout for
   79        * missing (null) editor/nextButton/previousButton children.
   80        */
   81       private static final Dimension zeroSize = new Dimension(0, 0);
   82   
   83   
   84       /**
   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        * @see #createEditor
  404        * @see #createPropertyChangeListener
  405        */
  406       protected void replaceEditor(JComponent oldEditor, JComponent newEditor) {
  407           spinner.remove(oldEditor);
  408           maybeRemoveEditorBorder(newEditor);
  409           installEditorBorderListener(newEditor);
  410           newEditor.setInheritsPopupMenu(true);
  411           spinner.add(newEditor, "Editor");
  412       }
  413   
  414       private void updateEditorAlignment(JComponent editor) {
  415           if (editor instanceof JSpinner.DefaultEditor) {
  416               // if editor alignment isn't set in LAF, we get 0 (CENTER) here
  417               int alignment = UIManager.getInt("Spinner.editorAlignment");
  418               JTextField text = ((JSpinner.DefaultEditor)editor).getTextField();
  419               text.setHorizontalAlignment(alignment);
  420           }
  421       }
  422   
  423       /**
  424        * Remove the border around the inner editor component for LaFs
  425        * that install an outside border around the spinner,
  426        */
  427       private void maybeRemoveEditorBorder(JComponent editor) {
  428           if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
  429               if (editor instanceof JPanel &&
  430                   editor.getBorder() == null &&
  431                   editor.getComponentCount() > 0) {
  432   
  433                   editor = (JComponent)editor.getComponent(0);
  434               }
  435   
  436               if (editor != null && editor.getBorder() instanceof UIResource) {
  437                   editor.setBorder(null);
  438               }
  439           }
  440       }
  441   
  442       /**
  443        * Remove the border around the inner editor component for LaFs
  444        * that install an outside border around the spinner,
  445        */
  446       private void installEditorBorderListener(JComponent editor) {
  447           if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
  448               if (editor instanceof JPanel &&
  449                   editor.getBorder() == null &&
  450                   editor.getComponentCount() > 0) {
  451   
  452                   editor = (JComponent)editor.getComponent(0);
  453               }
  454               if (editor != null &&
  455                   (editor.getBorder() == null ||
  456                    editor.getBorder() instanceof UIResource)) {
  457                   editor.addPropertyChangeListener(getHandler());
  458               }
  459           }
  460       }
  461   
  462       private void removeEditorBorderListener(JComponent editor) {
  463           if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
  464               if (editor instanceof JPanel &&
  465                   editor.getComponentCount() > 0) {
  466   
  467                   editor = (JComponent)editor.getComponent(0);
  468               }
  469               if (editor != null) {
  470                   editor.removePropertyChangeListener(getHandler());
  471               }
  472           }
  473       }
  474   
  475   
  476       /**
  477        * Updates the enabled state of the children Components based on the
  478        * enabled state of the <code>JSpinner</code>.
  479        */
  480       private void updateEnabledState() {
  481           updateEnabledState(spinner, spinner.isEnabled());
  482       }
  483   
  484   
  485       /**
  486        * Recursively updates the enabled state of the child
  487        * <code>Component</code>s of <code>c</code>.
  488        */
  489       private void updateEnabledState(Container c, boolean enabled) {
  490           for (int counter = c.getComponentCount() - 1; counter >= 0;counter--) {
  491               Component child = c.getComponent(counter);
  492   
  493               if (DefaultLookup.getBoolean(spinner, this,
  494                   "Spinner.disableOnBoundaryValues", false)) {
  495                   SpinnerModel model = spinner.getModel();
  496                   if (child.getName() == "Spinner.nextButton" &&
  497                       model.getNextValue() == null) {
  498                       child.setEnabled(false);
  499                   }
  500                   else if (child.getName() == "Spinner.previousButton" &&
  501                            model.getPreviousValue() == null) {
  502                       child.setEnabled(false);
  503                   }
  504                   else {
  505                       child.setEnabled(enabled);
  506                   }
  507               }
  508               else {
  509                   child.setEnabled(enabled);
  510               }
  511               if (child instanceof Container) {
  512                   updateEnabledState((Container)child, enabled);
  513               }
  514           }
  515       }
  516   
  517   
  518       /**
  519        * Installs the keyboard Actions onto the JSpinner.
  520        *
  521        * @since 1.5
  522        */
  523       protected void installKeyboardActions() {
  524           InputMap iMap = getInputMap(JComponent.
  525                                      WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  526   
  527           SwingUtilities.replaceUIInputMap(spinner, JComponent.
  528                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  529                                            iMap);
  530   
  531           LazyActionMap.installLazyActionMap(spinner, BasicSpinnerUI.class,
  532                   "Spinner.actionMap");
  533       }
  534   
  535       /**
  536        * Returns the InputMap to install for <code>condition</code>.
  537        */
  538       private InputMap getInputMap(int condition) {
  539           if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  540               return (InputMap)DefaultLookup.get(spinner, this,
  541                       "Spinner.ancestorInputMap");
  542           }
  543           return null;
  544       }
  545   
  546       static void loadActionMap(LazyActionMap map) {
  547           map.put("increment", nextButtonHandler);
  548           map.put("decrement", previousButtonHandler);
  549       }
  550   
  551       /**
  552        * Returns the baseline.
  553        *
  554        * @throws NullPointerException {@inheritDoc}
  555        * @throws IllegalArgumentException {@inheritDoc}
  556        * @see javax.swing.JComponent#getBaseline(int, int)
  557        * @since 1.6
  558        */
  559       public int getBaseline(JComponent c, int width, int height) {
  560           super.getBaseline(c, width, height);
  561           JComponent editor = spinner.getEditor();
  562           Insets insets = spinner.getInsets();
  563           width = width - insets.left - insets.right;
  564           height = height - insets.top - insets.bottom;
  565           if (width >= 0 && height >= 0) {
  566               int baseline = editor.getBaseline(width, height);
  567               if (baseline >= 0) {
  568                   return insets.top + baseline;
  569               }
  570           }
  571           return -1;
  572       }
  573   
  574       /**
  575        * Returns an enum indicating how the baseline of the component
  576        * changes as the size changes.
  577        *
  578        * @throws NullPointerException {@inheritDoc}
  579        * @see javax.swing.JComponent#getBaseline(int, int)
  580        * @since 1.6
  581        */
  582       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
  583               JComponent c) {
  584           super.getBaselineResizeBehavior(c);
  585           return spinner.getEditor().getBaselineResizeBehavior();
  586       }
  587   
  588       /**
  589        * A handler for spinner arrow button mouse and action events.  When
  590        * a left mouse pressed event occurs we look up the (enabled) spinner
  591        * that's the source of the event and start the autorepeat timer.  The
  592        * timer fires action events until any button is released at which
  593        * point the timer is stopped and the reference to the spinner cleared.
  594        * The timer doesn't start until after a 300ms delay, so often the
  595        * source of the initial (and final) action event is just the button
  596        * logic for mouse released - which means that we're relying on the fact
  597        * that our mouse listener runs after the buttons mouse listener.
  598        * <p>
  599        * Note that one instance of this handler is shared by all slider previous
  600        * arrow buttons and likewise for all of the next buttons,
  601        * so it doesn't have any state that persists beyond the limits
  602        * of a single button pressed/released gesture.
  603        */
  604       private static class ArrowButtonHandler extends AbstractAction
  605                                               implements FocusListener, MouseListener, UIResource {
  606           final javax.swing.Timer autoRepeatTimer;
  607           final boolean isNext;
  608           JSpinner spinner = null;
  609           JButton arrowButton = null;
  610   
  611           ArrowButtonHandler(String name, boolean isNext) {
  612               super(name);
  613               this.isNext = isNext;
  614               autoRepeatTimer = new javax.swing.Timer(60, this);
  615               autoRepeatTimer.setInitialDelay(300);
  616           }
  617   
  618           private JSpinner eventToSpinner(AWTEvent e) {
  619               Object src = e.getSource();
  620               while ((src instanceof Component) && !(src instanceof JSpinner)) {
  621                   src = ((Component)src).getParent();
  622               }
  623               return (src instanceof JSpinner) ? (JSpinner)src : null;
  624           }
  625   
  626           public void actionPerformed(ActionEvent e) {
  627               JSpinner spinner = this.spinner;
  628   
  629               if (!(e.getSource() instanceof javax.swing.Timer)) {
  630                   // Most likely resulting from being in ActionMap.
  631                   spinner = eventToSpinner(e);
  632                   if (e.getSource() instanceof JButton) {
  633                       arrowButton = (JButton)e.getSource();
  634                   }
  635               } else {
  636                   if (arrowButton!=null && !arrowButton.getModel().isPressed()
  637                       && autoRepeatTimer.isRunning()) {
  638                       autoRepeatTimer.stop();
  639                       spinner = null;
  640                       arrowButton = null;
  641                   }
  642               }
  643               if (spinner != null) {
  644                   try {
  645                       int calendarField = getCalendarField(spinner);
  646                       spinner.commitEdit();
  647                       if (calendarField != -1) {
  648                           ((SpinnerDateModel)spinner.getModel()).
  649                                    setCalendarField(calendarField);
  650                       }
  651                       Object value = (isNext) ? spinner.getNextValue() :
  652                                  spinner.getPreviousValue();
  653                       if (value != null) {
  654                           spinner.setValue(value);
  655                           select(spinner);
  656                       }
  657                   } catch (IllegalArgumentException iae) {
  658                       UIManager.getLookAndFeel().provideErrorFeedback(spinner);
  659                   } catch (ParseException pe) {
  660                       UIManager.getLookAndFeel().provideErrorFeedback(spinner);
  661                   }
  662               }
  663           }
  664   
  665           /**
  666            * If the spinner's editor is a DateEditor, this selects the field
  667            * associated with the value that is being incremented.
  668            */
  669           private void select(JSpinner spinner) {
  670               JComponent editor = spinner.getEditor();
  671   
  672               if (editor instanceof JSpinner.DateEditor) {
  673                   JSpinner.DateEditor dateEditor = (JSpinner.DateEditor)editor;
  674                   JFormattedTextField ftf = dateEditor.getTextField();
  675                   Format format = dateEditor.getFormat();
  676                   Object value;
  677   
  678                   if (format != null && (value = spinner.getValue()) != null) {
  679                       SpinnerDateModel model = dateEditor.getModel();
  680                       DateFormat.Field field = DateFormat.Field.ofCalendarField(
  681                           model.getCalendarField());
  682   
  683                       if (field != null) {
  684                           try {
  685                               AttributedCharacterIterator iterator = format.
  686                                   formatToCharacterIterator(value);
  687                               if (!select(ftf, iterator, field) &&
  688                                          field == DateFormat.Field.HOUR0) {
  689                                   select(ftf, iterator, DateFormat.Field.HOUR1);
  690                               }
  691                           }
  692                           catch (IllegalArgumentException iae) {}
  693                       }
  694                   }
  695               }
  696           }
  697   
  698           /**
  699            * Selects the passed in field, returning true if it is found,
  700            * false otherwise.
  701            */
  702           private boolean select(JFormattedTextField ftf,
  703                                  AttributedCharacterIterator iterator,
  704                                  DateFormat.Field field) {
  705               int max = ftf.getDocument().getLength();
  706   
  707               iterator.first();
  708               do {
  709                   Map attrs = iterator.getAttributes();
  710   
  711                   if (attrs != null && attrs.containsKey(field)){
  712                       int start = iterator.getRunStart(field);
  713                       int end = iterator.getRunLimit(field);
  714   
  715                       if (start != -1 && end != -1 && start <= max &&
  716                                          end <= max) {
  717                           ftf.select(start, end);
  718                       }
  719                       return true;
  720                   }
  721               } while (iterator.next() != CharacterIterator.DONE);
  722               return false;
  723           }
  724   
  725           /**
  726            * Returns the calendarField under the start of the selection, or
  727            * -1 if there is no valid calendar field under the selection (or
  728            * the spinner isn't editing dates.
  729            */
  730           private int getCalendarField(JSpinner spinner) {
  731               JComponent editor = spinner.getEditor();
  732   
  733               if (editor instanceof JSpinner.DateEditor) {
  734                   JSpinner.DateEditor dateEditor = (JSpinner.DateEditor)editor;
  735                   JFormattedTextField ftf = dateEditor.getTextField();
  736                   int start = ftf.getSelectionStart();
  737                   JFormattedTextField.AbstractFormatter formatter =
  738                                       ftf.getFormatter();
  739   
  740                   if (formatter instanceof InternationalFormatter) {
  741                       Format.Field[] fields = ((InternationalFormatter)
  742                                                formatter).getFields(start);
  743   
  744                       for (int counter = 0; counter < fields.length; counter++) {
  745                           if (fields[counter] instanceof DateFormat.Field) {
  746                               int calendarField;
  747   
  748                               if (fields[counter] == DateFormat.Field.HOUR1) {
  749                                   calendarField = Calendar.HOUR;
  750                               }
  751                               else {
  752                                   calendarField = ((DateFormat.Field)
  753                                           fields[counter]).getCalendarField();
  754                               }
  755                               if (calendarField != -1) {
  756                                   return calendarField;
  757                               }
  758                           }
  759                       }
  760                   }
  761               }
  762               return -1;
  763           }
  764   
  765           public void mousePressed(MouseEvent e) {
  766               if (SwingUtilities.isLeftMouseButton(e) && e.getComponent().isEnabled()) {
  767                   spinner = eventToSpinner(e);
  768                   autoRepeatTimer.start();
  769   
  770                   focusSpinnerIfNecessary();
  771               }
  772           }
  773   
  774           public void mouseReleased(MouseEvent e) {
  775               autoRepeatTimer.stop();
  776               arrowButton = null;
  777               spinner = null;
  778           }
  779   
  780           public void mouseClicked(MouseEvent e) {
  781           }
  782   
  783           public void mouseEntered(MouseEvent e) {
  784               if (spinner != null && !autoRepeatTimer.isRunning() && spinner == eventToSpinner(e)) {
  785                   autoRepeatTimer.start();
  786               }
  787           }
  788   
  789           public void mouseExited(MouseEvent e) {
  790               if (autoRepeatTimer.isRunning()) {
  791                   autoRepeatTimer.stop();
  792               }
  793           }
  794   
  795           /**
  796            * Requests focus on a child of the spinner if the spinner doesn't
  797            * have focus.
  798            */
  799           private void focusSpinnerIfNecessary() {
  800               Component fo = KeyboardFocusManager.
  801                                 getCurrentKeyboardFocusManager().getFocusOwner();
  802               if (spinner.isRequestFocusEnabled() && (
  803                           fo == null ||
  804                           !SwingUtilities.isDescendingFrom(fo, spinner))) {
  805                   Container root = spinner;
  806   
  807                   if (!root.isFocusCycleRoot()) {
  808                       root = root.getFocusCycleRootAncestor();
  809                   }
  810                   if (root != null) {
  811                       FocusTraversalPolicy ftp = root.getFocusTraversalPolicy();
  812                       Component child = ftp.getComponentAfter(root, spinner);
  813   
  814                       if (child != null && SwingUtilities.isDescendingFrom(
  815                                                           child, spinner)) {
  816                           child.requestFocus();
  817                       }
  818                   }
  819               }
  820           }
  821   
  822           public void focusGained(FocusEvent e) {
  823           }
  824   
  825           public void focusLost(FocusEvent e) {
  826               if (spinner == eventToSpinner(e)) {
  827                   if (autoRepeatTimer.isRunning()) {
  828                       autoRepeatTimer.stop();
  829                   }
  830                   spinner = null;
  831                   if (arrowButton != null) {
  832                       ButtonModel model = arrowButton.getModel();
  833                       model.setPressed(false);
  834                       model.setArmed(false);
  835                       arrowButton = null;
  836                   }
  837               }
  838           }
  839       }
  840   
  841   
  842       private static class Handler implements LayoutManager,
  843               PropertyChangeListener, ChangeListener {
  844           //
  845           // LayoutManager
  846           //
  847           private Component nextButton = null;
  848           private Component previousButton = null;
  849           private Component editor = null;
  850   
  851           public void addLayoutComponent(String name, Component c) {
  852               if ("Next".equals(name)) {
  853                   nextButton = c;
  854               }
  855               else if ("Previous".equals(name)) {
  856                   previousButton = c;
  857               }
  858               else if ("Editor".equals(name)) {
  859                   editor = c;
  860               }
  861           }
  862   
  863           public void removeLayoutComponent(Component c) {
  864               if (c == nextButton) {
  865                   nextButton = null;
  866               }
  867               else if (c == previousButton) {
  868                   previousButton = null;
  869               }
  870               else if (c == editor) {
  871                   editor = null;
  872               }
  873           }
  874   
  875           private Dimension preferredSize(Component c) {
  876               return (c == null) ? zeroSize : c.getPreferredSize();
  877           }
  878   
  879           public Dimension preferredLayoutSize(Container parent) {
  880               Dimension nextD = preferredSize(nextButton);
  881               Dimension previousD = preferredSize(previousButton);
  882               Dimension editorD = preferredSize(editor);
  883   
  884               /* Force the editors height to be a multiple of 2
  885                */
  886               editorD.height = ((editorD.height + 1) / 2) * 2;
  887   
  888               Dimension size = new Dimension(editorD.width, editorD.height);
  889               size.width += Math.max(nextD.width, previousD.width);
  890               Insets insets = parent.getInsets();
  891               size.width += insets.left + insets.right;
  892               size.height += insets.top + insets.bottom;
  893               return size;
  894           }
  895   
  896           public Dimension minimumLayoutSize(Container parent) {
  897               return preferredLayoutSize(parent);
  898           }
  899   
  900           private void setBounds(Component c, int x, int y, int width, int height) {
  901               if (c != null) {
  902                   c.setBounds(x, y, width, height);
  903               }
  904           }
  905   
  906           public void layoutContainer(Container parent) {
  907               int width  = parent.getWidth();
  908               int height = parent.getHeight();
  909   
  910               Insets insets = parent.getInsets();
  911   
  912               if (nextButton == null && previousButton == null) {
  913                   setBounds(editor, insets.left,  insets.top, width - insets.left - insets.right,
  914                           height - insets.top - insets.bottom);
  915   
  916                   return;
  917               }
  918   
  919               Dimension nextD = preferredSize(nextButton);
  920               Dimension previousD = preferredSize(previousButton);
  921               int buttonsWidth = Math.max(nextD.width, previousD.width);
  922               int editorHeight = height - (insets.top + insets.bottom);
  923   
  924               // The arrowButtonInsets value is used instead of the JSpinner's
  925               // insets if not null. Defining this to be (0, 0, 0, 0) causes the
  926               // buttons to be aligned with the outer edge of the spinner's
  927               // border, and leaving it as "null" places the buttons completely
  928               // inside the spinner's border.
  929               Insets buttonInsets = UIManager.getInsets("Spinner.arrowButtonInsets");
  930               if (buttonInsets == null) {
  931                   buttonInsets = insets;
  932               }
  933   
  934               /* Deal with the spinner's componentOrientation property.
  935                */
  936               int editorX, editorWidth, buttonsX;
  937               if (parent.getComponentOrientation().isLeftToRight()) {
  938                   editorX = insets.left;
  939                   editorWidth = width - insets.left - buttonsWidth - buttonInsets.right;
  940                   buttonsX = width - buttonsWidth - buttonInsets.right;
  941               } else {
  942                   buttonsX = buttonInsets.left;
  943                   editorX = buttonsX + buttonsWidth;
  944                   editorWidth = width - buttonInsets.left - buttonsWidth - insets.right;
  945               }
  946   
  947               int nextY = buttonInsets.top;
  948               int nextHeight = (height / 2) + (height % 2) - nextY;
  949               int previousY = buttonInsets.top + nextHeight;
  950               int previousHeight = height - previousY - buttonInsets.bottom;
  951   
  952               setBounds(editor,         editorX,  insets.top, editorWidth, editorHeight);
  953               setBounds(nextButton,     buttonsX, nextY,      buttonsWidth, nextHeight);
  954               setBounds(previousButton, buttonsX, previousY,  buttonsWidth, previousHeight);
  955           }
  956   
  957   
  958           //
  959           // PropertyChangeListener
  960           //
  961           public void propertyChange(PropertyChangeEvent e)
  962           {
  963               String propertyName = e.getPropertyName();
  964               if (e.getSource() instanceof JSpinner) {
  965                   JSpinner spinner = (JSpinner)(e.getSource());
  966                   SpinnerUI spinnerUI = spinner.getUI();
  967   
  968                   if (spinnerUI instanceof BasicSpinnerUI) {
  969                       BasicSpinnerUI ui = (BasicSpinnerUI)spinnerUI;
  970   
  971                       if ("editor".equals(propertyName)) {
  972                           JComponent oldEditor = (JComponent)e.getOldValue();
  973                           JComponent newEditor = (JComponent)e.getNewValue();
  974                           ui.replaceEditor(oldEditor, newEditor);
  975                           ui.updateEnabledState();
  976                           if (oldEditor instanceof JSpinner.DefaultEditor) {
  977                               JTextField tf =
  978                                   ((JSpinner.DefaultEditor)oldEditor).getTextField();
  979                               if (tf != null) {
  980                                   tf.removeFocusListener(nextButtonHandler);
  981                                   tf.removeFocusListener(previousButtonHandler);
  982                               }
  983                           }
  984                           if (newEditor instanceof JSpinner.DefaultEditor) {
  985                               JTextField tf =
  986                                   ((JSpinner.DefaultEditor)newEditor).getTextField();
  987                               if (tf != null) {
  988                                   if (tf.getFont() instanceof UIResource) {
  989                                       tf.setFont(spinner.getFont());
  990                                   }
  991                                   tf.addFocusListener(nextButtonHandler);
  992                                   tf.addFocusListener(previousButtonHandler);
  993                               }
  994                           }
  995                       }
  996                       else if ("enabled".equals(propertyName) ||
  997                                "model".equals(propertyName)) {
  998                           ui.updateEnabledState();
  999                       }
 1000                   else if ("font".equals(propertyName)) {
 1001                       JComponent editor = spinner.getEditor();
 1002                       if (editor!=null && editor instanceof JSpinner.DefaultEditor) {
 1003                           JTextField tf =
 1004                               ((JSpinner.DefaultEditor)editor).getTextField();
 1005                           if (tf != null) {
 1006                               if (tf.getFont() instanceof UIResource) {
 1007                                   tf.setFont(spinner.getFont());
 1008                               }
 1009                           }
 1010                       }
 1011                   }
 1012                   else if (JComponent.TOOL_TIP_TEXT_KEY.equals(propertyName)) {
 1013                       updateToolTipTextForChildren(spinner);
 1014                   }
 1015                   }
 1016               } else if (e.getSource() instanceof JComponent) {
 1017                   JComponent c = (JComponent)e.getSource();
 1018                   if ((c.getParent() instanceof JPanel) &&
 1019                       (c.getParent().getParent() instanceof JSpinner) &&
 1020                       "border".equals(propertyName)) {
 1021   
 1022                       JSpinner spinner = (JSpinner)c.getParent().getParent();
 1023                       SpinnerUI spinnerUI = spinner.getUI();
 1024                       if (spinnerUI instanceof BasicSpinnerUI) {
 1025                           BasicSpinnerUI ui = (BasicSpinnerUI)spinnerUI;
 1026                           ui.maybeRemoveEditorBorder(c);
 1027                       }
 1028                   }
 1029               }
 1030           }
 1031   
 1032           // Syncronizes the ToolTip text for the components within the spinner
 1033           // to be the same value as the spinner ToolTip text.
 1034           private void updateToolTipTextForChildren(JComponent spinner) {
 1035               String toolTipText = spinner.getToolTipText();
 1036               Component[] children = spinner.getComponents();
 1037               for (int i = 0; i < children.length; i++) {
 1038                   if (children[i] instanceof JSpinner.DefaultEditor) {
 1039                       JTextField tf = ((JSpinner.DefaultEditor)children[i]).getTextField();
 1040                       if (tf != null) {
 1041                           tf.setToolTipText(toolTipText);
 1042                       }
 1043                   } else if (children[i] instanceof JComponent) {
 1044                       ((JComponent)children[i]).setToolTipText( spinner.getToolTipText() );
 1045                   }
 1046               }
 1047           }
 1048   
 1049           public void stateChanged(ChangeEvent e) {
 1050               if (e.getSource() instanceof JSpinner) {
 1051                   JSpinner spinner = (JSpinner)e.getSource();
 1052                   SpinnerUI spinnerUI = spinner.getUI();
 1053                   if (DefaultLookup.getBoolean(spinner, spinnerUI,
 1054                       "Spinner.disableOnBoundaryValues", false) &&
 1055                       spinnerUI instanceof BasicSpinnerUI) {
 1056                       BasicSpinnerUI ui = (BasicSpinnerUI)spinnerUI;
 1057                       ui.updateEnabledState();
 1058                   }
 1059               }
 1060           }
 1061       }
 1062   }

Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]