alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (BasicSpinnerUI.java)

This example Java source code file (BasicSpinnerUI.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

arrowbuttonhandler, awt, basicspinnerui, component, dimension, editor, event, gui, insets, jbutton, jcomponent, jpanel, jspinner, jtextfield, plaf, propertychangelistener, string, swing, text, uiresource

The BasicSpinnerUI.java Java example source code

/*
 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing.plaf.basic;

import java.awt.*;
import java.awt.event.*;
import java.text.ParseException;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.text.*;

import java.beans.*;
import java.text.*;
import java.util.*;
import sun.swing.DefaultLookup;


/**
 * The default Spinner UI delegate.
 *
 * @author Hans Muller
 * @since 1.4
 */
public class BasicSpinnerUI extends SpinnerUI
{
    /**
     * The spinner that we're a UI delegate for.  Initialized by
     * the <code>installUI method, and reset to null
     * by <code>uninstallUI.
     *
     * @see #installUI
     * @see #uninstallUI
     */
    protected JSpinner spinner;
    private Handler handler;


    /**
     * The mouse/action listeners that are added to the spinner's
     * arrow buttons.  These listeners are shared by all
     * spinner arrow buttons.
     *
     * @see #createNextButton
     * @see #createPreviousButton
     */
    private static final ArrowButtonHandler nextButtonHandler = new ArrowButtonHandler("increment", true);
    private static final ArrowButtonHandler previousButtonHandler = new ArrowButtonHandler("decrement", false);
    private PropertyChangeListener propertyChangeListener;


    /**
     * Used by the default LayoutManager class - SpinnerLayout for
     * missing (null) editor/nextButton/previousButton children.
     */
    private static final Dimension zeroSize = new Dimension(0, 0);


    /**
     * Returns a new instance of BasicSpinnerUI.  SpinnerListUI
     * delegates are allocated one per JSpinner.
     *
     * @param c the JSpinner (not used)
     * @see ComponentUI#createUI
     * @return a new BasicSpinnerUI object
     */
    public static ComponentUI createUI(JComponent c) {
        return new BasicSpinnerUI();
    }


    private void maybeAdd(Component c, String s) {
        if (c != null) {
            spinner.add(c, s);
        }
    }


    /**
     * Calls <code>installDefaults, installListeners,
     * and then adds the components returned by <code>createNextButton,
     * <code>createPreviousButton, and createEditor.
     *
     * @param c the JSpinner
     * @see #installDefaults
     * @see #installListeners
     * @see #createNextButton
     * @see #createPreviousButton
     * @see #createEditor
     */
    public void installUI(JComponent c) {
        this.spinner = (JSpinner)c;
        installDefaults();
        installListeners();
        maybeAdd(createNextButton(), "Next");
        maybeAdd(createPreviousButton(), "Previous");
        maybeAdd(createEditor(), "Editor");
        updateEnabledState();
        installKeyboardActions();
    }


    /**
     * Calls <code>uninstallDefaults, uninstallListeners,
     * and then removes all of the spinners children.
     *
     * @param c the JSpinner (not used)
     */
    public void uninstallUI(JComponent c) {
        uninstallDefaults();
        uninstallListeners();
        this.spinner = null;
        c.removeAll();
    }


    /**
     * Initializes <code>PropertyChangeListener with
     * a shared object that delegates interesting PropertyChangeEvents
     * to protected methods.
     * <p>
     * This method is called by <code>installUI.
     *
     * @see #replaceEditor
     * @see #uninstallListeners
     */
    protected void installListeners() {
        propertyChangeListener = createPropertyChangeListener();
        spinner.addPropertyChangeListener(propertyChangeListener);
        if (DefaultLookup.getBoolean(spinner, this,
            "Spinner.disableOnBoundaryValues", false)) {
            spinner.addChangeListener(getHandler());
        }
        JComponent editor = spinner.getEditor();
        if (editor != null && editor instanceof JSpinner.DefaultEditor) {
            JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
            if (tf != null) {
                tf.addFocusListener(nextButtonHandler);
                tf.addFocusListener(previousButtonHandler);
            }
        }
    }


    /**
     * Removes the <code>PropertyChangeListener added
     * by installListeners.
     * <p>
     * This method is called by <code>uninstallUI.
     *
     * @see #installListeners
     */
    protected void uninstallListeners() {
        spinner.removePropertyChangeListener(propertyChangeListener);
        spinner.removeChangeListener(handler);
        JComponent editor = spinner.getEditor();
        removeEditorBorderListener(editor);
        if (editor instanceof JSpinner.DefaultEditor) {
            JTextField tf = ((JSpinner.DefaultEditor)editor).getTextField();
            if (tf != null) {
                tf.removeFocusListener(nextButtonHandler);
                tf.removeFocusListener(previousButtonHandler);
            }
        }
        propertyChangeListener = null;
        handler = null;
    }


    /**
     * Initialize the <code>JSpinner border,
     * <code>foreground, and background, properties
     * based on the corresponding "Spinner.*" properties from defaults table.
     * The <code>JSpinners layout is set to the value returned by
     * <code>createLayout.  This method is called by installUI.
     *
     * @see #uninstallDefaults
     * @see #installUI
     * @see #createLayout
     * @see LookAndFeel#installBorder
     * @see LookAndFeel#installColors
     */
    protected void installDefaults() {
        spinner.setLayout(createLayout());
        LookAndFeel.installBorder(spinner, "Spinner.border");
        LookAndFeel.installColorsAndFont(spinner, "Spinner.background", "Spinner.foreground", "Spinner.font");
        LookAndFeel.installProperty(spinner, "opaque", Boolean.TRUE);
    }


    /**
     * Sets the <code>JSpinner's layout manager to null.  This
     * method is called by <code>uninstallUI.
     *
     * @see #installDefaults
     * @see #uninstallUI
     */
    protected void uninstallDefaults() {
        spinner.setLayout(null);
    }


    private Handler getHandler() {
        if (handler == null) {
            handler = new Handler();
        }
        return handler;
    }


    /**
     * Installs the necessary listeners on the next button, <code>c,
     * to update the <code>JSpinner in response to a user gesture.
     *
     * @param c Component to install the listeners on
     * @throws NullPointerException if <code>c is null.
     * @see #createNextButton
     * @since 1.5
     */
    protected void installNextButtonListeners(Component c) {
        installButtonListeners(c, nextButtonHandler);
    }

    /**
     * Installs the necessary listeners on the previous button, <code>c,
     * to update the <code>JSpinner in response to a user gesture.
     *
     * @param c Component to install the listeners on.
     * @throws NullPointerException if <code>c is null.
     * @see #createPreviousButton
     * @since 1.5
     */
    protected void installPreviousButtonListeners(Component c) {
        installButtonListeners(c, previousButtonHandler);
    }

    private void installButtonListeners(Component c,
                                        ArrowButtonHandler handler) {
        if (c instanceof JButton) {
            ((JButton)c).addActionListener(handler);
        }
        c.addMouseListener(handler);
    }

    /**
     * Creates a <code>LayoutManager that manages the editor,
     * <code>nextButton, and previousButton
     * children of the JSpinner.  These three children must be
     * added with a constraint that identifies their role:
     * "Editor", "Next", and "Previous". The default layout manager
     * can handle the absence of any of these children.
     *
     * @return a LayoutManager for the editor, next button, and previous button.
     * @see #createNextButton
     * @see #createPreviousButton
     * @see #createEditor
     */
    protected LayoutManager createLayout() {
        return getHandler();
    }


    /**
     * Creates a <code>PropertyChangeListener that can be
     * added to the JSpinner itself.  Typically, this listener
     * will call replaceEditor when the "editor" property changes,
     * since it's the <code>SpinnerUI's responsibility to
     * add the editor to the JSpinner (and remove the old one).
     * This method is called by <code>installListeners.
     *
     * @return A PropertyChangeListener for the JSpinner itself
     * @see #installListeners
     */
    protected PropertyChangeListener createPropertyChangeListener() {
        return getHandler();
    }


    /**
     * Creates a decrement button, i.e. component that replaces the spinner
     * value with the object returned by <code>spinner.getPreviousValue.
     * By default the <code>previousButton is a {@code JButton}. If the
     * decrement button is not needed this method should return {@code null}.
     *
     * @return a component that will replace the spinner's value with the
     *     previous value in the sequence, or {@code null}
     * @see #installUI
     * @see #createNextButton
     * @see #installPreviousButtonListeners
     */
    protected Component createPreviousButton() {
        Component c = createArrowButton(SwingConstants.SOUTH);
        c.setName("Spinner.previousButton");
        installPreviousButtonListeners(c);
        return c;
    }


    /**
     * Creates an increment button, i.e. component that replaces the spinner
     * value with the object returned by <code>spinner.getNextValue.
     * By default the <code>nextButton is a {@code JButton}. If the
     * increment button is not needed this method should return {@code null}.
     *
     * @return a component that will replace the spinner's value with the
     *     next value in the sequence, or {@code null}
     * @see #installUI
     * @see #createPreviousButton
     * @see #installNextButtonListeners
     */
    protected Component createNextButton() {
        Component c = createArrowButton(SwingConstants.NORTH);
        c.setName("Spinner.nextButton");
        installNextButtonListeners(c);
        return c;
    }

    private Component createArrowButton(int direction) {
        JButton b = new BasicArrowButton(direction);
        Border buttonBorder = UIManager.getBorder("Spinner.arrowButtonBorder");
        if (buttonBorder instanceof UIResource) {
            // Wrap the border to avoid having the UIResource be replaced by
            // the ButtonUI. This is the opposite of using BorderUIResource.
            b.setBorder(new CompoundBorder(buttonBorder, null));
        } else {
            b.setBorder(buttonBorder);
        }
        b.setInheritsPopupMenu(true);
        return b;
    }


    /**
     * This method is called by installUI to get the editor component
     * of the <code>JSpinner.  By default it just returns
     * <code>JSpinner.getEditor().  Subclasses can override
     * <code>createEditor to return a component that contains
     * the spinner's editor or null, if they're going to handle adding
     * the editor to the <code>JSpinner in an
     * <code>installUI override.
     * <p>
     * Typically this method would be overridden to wrap the editor
     * with a container with a custom border, since one can't assume
     * that the editors border can be set directly.
     * <p>
     * The <code>replaceEditor method is called when the spinners
     * editor is changed with <code>JSpinner.setEditor.  If you've
     * overriden this method, then you'll probably want to override
     * <code>replaceEditor as well.
     *
     * @return the JSpinners editor JComponent, spinner.getEditor() by default
     * @see #installUI
     * @see #replaceEditor
     * @see JSpinner#getEditor
     */
    protected JComponent createEditor() {
        JComponent editor = spinner.getEditor();
        maybeRemoveEditorBorder(editor);
        installEditorBorderListener(editor);
        editor.setInheritsPopupMenu(true);
        updateEditorAlignment(editor);
        return editor;
    }


    /**
     * Called by the <code>PropertyChangeListener when the
     * <code>JSpinner editor property changes.  It's the responsibility
     * of this method to remove the old editor and add the new one.  By
     * default this operation is just:
     * <pre>
     * spinner.remove(oldEditor);
     * spinner.add(newEditor, "Editor");
     * </pre>
     * The implementation of <code>replaceEditor should be coordinated
     * with the <code>createEditor method.
     *
     * @see #createEditor
     * @see #createPropertyChangeListener
     */
    protected void replaceEditor(JComponent oldEditor, JComponent newEditor) {
        spinner.remove(oldEditor);
        maybeRemoveEditorBorder(newEditor);
        installEditorBorderListener(newEditor);
        newEditor.setInheritsPopupMenu(true);
        spinner.add(newEditor, "Editor");
    }

    private void updateEditorAlignment(JComponent editor) {
        if (editor instanceof JSpinner.DefaultEditor) {
            // if editor alignment isn't set in LAF, we get 0 (CENTER) here
            int alignment = UIManager.getInt("Spinner.editorAlignment");
            JTextField text = ((JSpinner.DefaultEditor)editor).getTextField();
            text.setHorizontalAlignment(alignment);
        }
    }

    /**
     * Remove the border around the inner editor component for LaFs
     * that install an outside border around the spinner,
     */
    private void maybeRemoveEditorBorder(JComponent editor) {
        if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
            if (editor instanceof JPanel &&
                editor.getBorder() == null &&
                editor.getComponentCount() > 0) {

                editor = (JComponent)editor.getComponent(0);
            }

            if (editor != null && editor.getBorder() instanceof UIResource) {
                editor.setBorder(null);
            }
        }
    }

    /**
     * Remove the border around the inner editor component for LaFs
     * that install an outside border around the spinner,
     */
    private void installEditorBorderListener(JComponent editor) {
        if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
            if (editor instanceof JPanel &&
                editor.getBorder() == null &&
                editor.getComponentCount() > 0) {

                editor = (JComponent)editor.getComponent(0);
            }
            if (editor != null &&
                (editor.getBorder() == null ||
                 editor.getBorder() instanceof UIResource)) {
                editor.addPropertyChangeListener(getHandler());
            }
        }
    }

    private void removeEditorBorderListener(JComponent editor) {
        if (!UIManager.getBoolean("Spinner.editorBorderPainted")) {
            if (editor instanceof JPanel &&
                editor.getComponentCount() > 0) {

                editor = (JComponent)editor.getComponent(0);
            }
            if (editor != null) {
                editor.removePropertyChangeListener(getHandler());
            }
        }
    }


    /**
     * Updates the enabled state of the children Components based on the
     * enabled state of the <code>JSpinner.
     */
    private void updateEnabledState() {
        updateEnabledState(spinner, spinner.isEnabled());
    }


    /**
     * Recursively updates the enabled state of the child
     * <code>Components of c.
     */
    private void updateEnabledState(Container c, boolean enabled) {
        for (int counter = c.getComponentCount() - 1; counter >= 0;counter--) {
            Component child = c.getComponent(counter);

            if (DefaultLookup.getBoolean(spinner, this,
                "Spinner.disableOnBoundaryValues", false)) {
                SpinnerModel model = spinner.getModel();
                if (child.getName() == "Spinner.nextButton" &&
                    model.getNextValue() == null) {
                    child.setEnabled(false);
                }
                else if (child.getName() == "Spinner.previousButton" &&
                         model.getPreviousValue() == null) {
                    child.setEnabled(false);
                }
                else {
                    child.setEnabled(enabled);
                }
            }
            else {
                child.setEnabled(enabled);
            }
            if (child instanceof Container) {
                updateEnabledState((Container)child, enabled);
            }
        }
    }


    /**
     * Installs the keyboard Actions onto the JSpinner.
     *
     * @since 1.5
     */
    protected void installKeyboardActions() {
        InputMap iMap = getInputMap(JComponent.
                                   WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

        SwingUtilities.replaceUIInputMap(spinner, JComponent.
                                         WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
                                         iMap);

        LazyActionMap.installLazyActionMap(spinner, BasicSpinnerUI.class,
                "Spinner.actionMap");
    }

    /**
     * Returns the InputMap to install for <code>condition.
     */
    private InputMap getInputMap(int condition) {
        if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
            return (InputMap)DefaultLookup.get(spinner, this,
                    "Spinner.ancestorInputMap");
        }
        return null;
    }

    static void loadActionMap(LazyActionMap map) {
        map.put("increment", nextButtonHandler);
        map.put("decrement", previousButtonHandler);
    }

    /**
     * Returns the baseline.
     *
     * @throws NullPointerException {@inheritDoc}
     * @throws IllegalArgumentException {@inheritDoc}
     * @see javax.swing.JComponent#getBaseline(int, int)
     * @since 1.6
     */
    public int getBaseline(JComponent c, int width, int height) {
        super.getBaseline(c, width, height);
        JComponent editor = spinner.getEditor();
        Insets insets = spinner.getInsets();
        width = width - insets.left - insets.right;
        height = height - insets.top - insets.bottom;
        if (width >= 0 && height >= 0) {
            int baseline = editor.getBaseline(width, height);
            if (baseline >= 0) {
                return insets.top + baseline;
            }
        }
        return -1;
    }

    /**
     * Returns an enum indicating how the baseline of the component
     * changes as the size changes.
     *
     * @throws NullPointerException {@inheritDoc}
     * @see javax.swing.JComponent#getBaseline(int, int)
     * @since 1.6
     */
    public Component.BaselineResizeBehavior getBaselineResizeBehavior(
            JComponent c) {
        super.getBaselineResizeBehavior(c);
        return spinner.getEditor().getBaselineResizeBehavior();
    }

    /**
     * A handler for spinner arrow button mouse and action events.  When
     * a left mouse pressed event occurs we look up the (enabled) spinner
     * that's the source of the event and start the autorepeat timer.  The
     * timer fires action events until any button is released at which
     * point the timer is stopped and the reference to the spinner cleared.
     * The timer doesn't start until after a 300ms delay, so often the
     * source of the initial (and final) action event is just the button
     * logic for mouse released - which means that we're relying on the fact
     * that our mouse listener runs after the buttons mouse listener.
     * <p>
     * Note that one instance of this handler is shared by all slider previous
     * arrow buttons and likewise for all of the next buttons,
     * so it doesn't have any state that persists beyond the limits
     * of a single button pressed/released gesture.
     */
    private static class ArrowButtonHandler extends AbstractAction
                                            implements FocusListener, MouseListener, UIResource {
        final javax.swing.Timer autoRepeatTimer;
        final boolean isNext;
        JSpinner spinner = null;
        JButton arrowButton = null;

        ArrowButtonHandler(String name, boolean isNext) {
            super(name);
            this.isNext = isNext;
            autoRepeatTimer = new javax.swing.Timer(60, this);
            autoRepeatTimer.setInitialDelay(300);
        }

        private JSpinner eventToSpinner(AWTEvent e) {
            Object src = e.getSource();
            while ((src instanceof Component) && !(src instanceof JSpinner)) {
                src = ((Component)src).getParent();
            }
            return (src instanceof JSpinner) ? (JSpinner)src : null;
        }

        public void actionPerformed(ActionEvent e) {
            JSpinner spinner = this.spinner;

            if (!(e.getSource() instanceof javax.swing.Timer)) {
                // Most likely resulting from being in ActionMap.
                spinner = eventToSpinner(e);
                if (e.getSource() instanceof JButton) {
                    arrowButton = (JButton)e.getSource();
                }
            } else {
                if (arrowButton!=null && !arrowButton.getModel().isPressed()
                    && autoRepeatTimer.isRunning()) {
                    autoRepeatTimer.stop();
                    spinner = null;
                    arrowButton = null;
                }
            }
            if (spinner != null) {
                try {
                    int calendarField = getCalendarField(spinner);
                    spinner.commitEdit();
                    if (calendarField != -1) {
                        ((SpinnerDateModel)spinner.getModel()).
                                 setCalendarField(calendarField);
                    }
                    Object value = (isNext) ? spinner.getNextValue() :
                               spinner.getPreviousValue();
                    if (value != null) {
                        spinner.setValue(value);
                        select(spinner);
                    }
                } catch (IllegalArgumentException iae) {
                    UIManager.getLookAndFeel().provideErrorFeedback(spinner);
                } catch (ParseException pe) {
                    UIManager.getLookAndFeel().provideErrorFeedback(spinner);
                }
            }
        }

        /**
         * If the spinner's editor is a DateEditor, this selects the field
         * associated with the value that is being incremented.
         */
        private void select(JSpinner spinner) {
            JComponent editor = spinner.getEditor();

            if (editor instanceof JSpinner.DateEditor) {
                JSpinner.DateEditor dateEditor = (JSpinner.DateEditor)editor;
                JFormattedTextField ftf = dateEditor.getTextField();
                Format format = dateEditor.getFormat();
                Object value;

                if (format != null && (value = spinner.getValue()) != null) {
                    SpinnerDateModel model = dateEditor.getModel();
                    DateFormat.Field field = DateFormat.Field.ofCalendarField(
                        model.getCalendarField());

                    if (field != null) {
                        try {
                            AttributedCharacterIterator iterator = format.
                                formatToCharacterIterator(value);
                            if (!select(ftf, iterator, field) &&
                                       field == DateFormat.Field.HOUR0) {
                                select(ftf, iterator, DateFormat.Field.HOUR1);
                            }
                        }
                        catch (IllegalArgumentException iae) {}
                    }
                }
            }
        }

        /**
         * Selects the passed in field, returning true if it is found,
         * false otherwise.
         */
        private boolean select(JFormattedTextField ftf,
                               AttributedCharacterIterator iterator,
                               DateFormat.Field field) {
            int max = ftf.getDocument().getLength();

            iterator.first();
            do {
                Map attrs = iterator.getAttributes();

                if (attrs != null && attrs.containsKey(field)){
                    int start = iterator.getRunStart(field);
                    int end = iterator.getRunLimit(field);

                    if (start != -1 && end != -1 && start <= max &&
                                       end <= max) {
                        ftf.select(start, end);
                    }
                    return true;
                }
            } while (iterator.next() != CharacterIterator.DONE);
            return false;
        }

        /**
         * Returns the calendarField under the start of the selection, or
         * -1 if there is no valid calendar field under the selection (or
         * the spinner isn't editing dates.
         */
        private int getCalendarField(JSpinner spinner) {
            JComponent editor = spinner.getEditor();

            if (editor instanceof JSpinner.DateEditor) {
                JSpinner.DateEditor dateEditor = (JSpinner.DateEditor)editor;
                JFormattedTextField ftf = dateEditor.getTextField();
                int start = ftf.getSelectionStart();
                JFormattedTextField.AbstractFormatter formatter =
                                    ftf.getFormatter();

                if (formatter instanceof InternationalFormatter) {
                    Format.Field[] fields = ((InternationalFormatter)
                                             formatter).getFields(start);

                    for (int counter = 0; counter < fields.length; counter++) {
                        if (fields[counter] instanceof DateFormat.Field) {
                            int calendarField;

                            if (fields[counter] == DateFormat.Field.HOUR1) {
                                calendarField = Calendar.HOUR;
                            }
                            else {
                                calendarField = ((DateFormat.Field)
                                        fields[counter]).getCalendarField();
                            }
                            if (calendarField != -1) {
                                return calendarField;
                            }
                        }
                    }
                }
            }
            return -1;
        }

        public void mousePressed(MouseEvent e) {
            if (SwingUtilities.isLeftMouseButton(e) && e.getComponent().isEnabled()) {
                spinner = eventToSpinner(e);
                autoRepeatTimer.start();

                focusSpinnerIfNecessary();
            }
        }

        public void mouseReleased(MouseEvent e) {
            autoRepeatTimer.stop();
            arrowButton = null;
            spinner = null;
        }

        public void mouseClicked(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
            if (spinner != null && !autoRepeatTimer.isRunning() && spinner == eventToSpinner(e)) {
                autoRepeatTimer.start();
            }
        }

        public void mouseExited(MouseEvent e) {
            if (autoRepeatTimer.isRunning()) {
                autoRepeatTimer.stop();
            }
        }

        /**
         * Requests focus on a child of the spinner if the spinner doesn't
         * have focus.
         */
        private void focusSpinnerIfNecessary() {
            Component fo = KeyboardFocusManager.
                              getCurrentKeyboardFocusManager().getFocusOwner();
            if (spinner.isRequestFocusEnabled() && (
                        fo == null ||
                        !SwingUtilities.isDescendingFrom(fo, spinner))) {
                Container root = spinner;

                if (!root.isFocusCycleRoot()) {
                    root = root.getFocusCycleRootAncestor();
                }
                if (root != null) {
                    FocusTraversalPolicy ftp = root.getFocusTraversalPolicy();
                    Component child = ftp.getComponentAfter(root, spinner);

                    if (child != null && SwingUtilities.isDescendingFrom(
                                                        child, spinner)) {
                        child.requestFocus();
                    }
                }
            }
        }

        public void focusGained(FocusEvent e) {
        }

        public void focusLost(FocusEvent e) {
            if (spinner == eventToSpinner(e)) {
                if (autoRepeatTimer.isRunning()) {
                    autoRepeatTimer.stop();
                }
                spinner = null;
                if (arrowButton != null) {
                    ButtonModel model = arrowButton.getModel();
                    model.setPressed(false);
                    model.setArmed(false);
                    arrowButton = null;
                }
            }
        }
    }


    private static class Handler implements LayoutManager,
            PropertyChangeListener, ChangeListener {
        //
        // LayoutManager
        //
        private Component nextButton = null;
        private Component previousButton = null;
        private Component editor = null;

        public void addLayoutComponent(String name, Component c) {
            if ("Next".equals(name)) {
                nextButton = c;
            }
            else if ("Previous".equals(name)) {
                previousButton = c;
            }
            else if ("Editor".equals(name)) {
                editor = c;
            }
        }

        public void removeLayoutComponent(Component c) {
            if (c == nextButton) {
                nextButton = null;
            }
            else if (c == previousButton) {
                previousButton = null;
            }
            else if (c == editor) {
                editor = null;
            }
        }

        private Dimension preferredSize(Component c) {
            return (c == null) ? zeroSize : c.getPreferredSize();
        }

        public Dimension preferredLayoutSize(Container parent) {
            Dimension nextD = preferredSize(nextButton);
            Dimension previousD = preferredSize(previousButton);
            Dimension editorD = preferredSize(editor);

            /* Force the editors height to be a multiple of 2
             */
            editorD.height = ((editorD.height + 1) / 2) * 2;

            Dimension size = new Dimension(editorD.width, editorD.height);
            size.width += Math.max(nextD.width, previousD.width);
            Insets insets = parent.getInsets();
            size.width += insets.left + insets.right;
            size.height += insets.top + insets.bottom;
            return size;
        }

        public Dimension minimumLayoutSize(Container parent) {
            return preferredLayoutSize(parent);
        }

        private void setBounds(Component c, int x, int y, int width, int height) {
            if (c != null) {
                c.setBounds(x, y, width, height);
            }
        }

        public void layoutContainer(Container parent) {
            int width  = parent.getWidth();
            int height = parent.getHeight();

            Insets insets = parent.getInsets();

            if (nextButton == null && previousButton == null) {
                setBounds(editor, insets.left,  insets.top, width - insets.left - insets.right,
                        height - insets.top - insets.bottom);

                return;
            }

            Dimension nextD = preferredSize(nextButton);
            Dimension previousD = preferredSize(previousButton);
            int buttonsWidth = Math.max(nextD.width, previousD.width);
            int editorHeight = height - (insets.top + insets.bottom);

            // The arrowButtonInsets value is used instead of the JSpinner's
            // insets if not null. Defining this to be (0, 0, 0, 0) causes the
            // buttons to be aligned with the outer edge of the spinner's
            // border, and leaving it as "null" places the buttons completely
            // inside the spinner's border.
            Insets buttonInsets = UIManager.getInsets("Spinner.arrowButtonInsets");
            if (buttonInsets == null) {
                buttonInsets = insets;
            }

            /* Deal with the spinner's componentOrientation property.
             */
            int editorX, editorWidth, buttonsX;
            if (parent.getComponentOrientation().isLeftToRight()) {
                editorX = insets.left;
                editorWidth = width - insets.left - buttonsWidth - buttonInsets.right;
                buttonsX = width - buttonsWidth - buttonInsets.right;
            } else {
                buttonsX = buttonInsets.left;
                editorX = buttonsX + buttonsWidth;
                editorWidth = width - buttonInsets.left - buttonsWidth - insets.right;
            }

            int nextY = buttonInsets.top;
            int nextHeight = (height / 2) + (height % 2) - nextY;
            int previousY = buttonInsets.top + nextHeight;
            int previousHeight = height - previousY - buttonInsets.bottom;

            setBounds(editor,         editorX,  insets.top, editorWidth, editorHeight);
            setBounds(nextButton,     buttonsX, nextY,      buttonsWidth, nextHeight);
            setBounds(previousButton, buttonsX, previousY,  buttonsWidth, previousHeight);
        }


        //
        // PropertyChangeListener
        //
        public void propertyChange(PropertyChangeEvent e)
        {
            String propertyName = e.getPropertyName();
            if (e.getSource() instanceof JSpinner) {
                JSpinner spinner = (JSpinner)(e.getSource());
                SpinnerUI spinnerUI = spinner.getUI();

                if (spinnerUI instanceof BasicSpinnerUI) {
                    BasicSpinnerUI ui = (BasicSpinnerUI)spinnerUI;

                    if ("editor".equals(propertyName)) {
                        JComponent oldEditor = (JComponent)e.getOldValue();
                        JComponent newEditor = (JComponent)e.getNewValue();
                        ui.replaceEditor(oldEditor, newEditor);
                        ui.updateEnabledState();
                        if (oldEditor instanceof JSpinner.DefaultEditor) {
                            JTextField tf =
                                ((JSpinner.DefaultEditor)oldEditor).getTextField();
                            if (tf != null) {
                                tf.removeFocusListener(nextButtonHandler);
                                tf.removeFocusListener(previousButtonHandler);
                            }
                        }
                        if (newEditor instanceof JSpinner.DefaultEditor) {
                            JTextField tf =
                                ((JSpinner.DefaultEditor)newEditor).getTextField();
                            if (tf != null) {
                                if (tf.getFont() instanceof UIResource) {
                                    tf.setFont(spinner.getFont());
                                }
                                tf.addFocusListener(nextButtonHandler);
                                tf.addFocusListener(previousButtonHandler);
                            }
                        }
                    }
                    else if ("enabled".equals(propertyName) ||
                             "model".equals(propertyName)) {
                        ui.updateEnabledState();
                    }
                else if ("font".equals(propertyName)) {
                    JComponent editor = spinner.getEditor();
                    if (editor!=null && editor instanceof JSpinner.DefaultEditor) {
                        JTextField tf =
                            ((JSpinner.DefaultEditor)editor).getTextField();
                        if (tf != null) {
                            if (tf.getFont() instanceof UIResource) {
                                tf.setFont(spinner.getFont());
                            }
                        }
                    }
                }
                else if (JComponent.TOOL_TIP_TEXT_KEY.equals(propertyName)) {
                    updateToolTipTextForChildren(spinner);
                }
                }
            } else if (e.getSource() instanceof JComponent) {
                JComponent c = (JComponent)e.getSource();
                if ((c.getParent() instanceof JPanel) &&
                    (c.getParent().getParent() instanceof JSpinner) &&
                    "border".equals(propertyName)) {

                    JSpinner spinner = (JSpinner)c.getParent().getParent();
                    SpinnerUI spinnerUI = spinner.getUI();
                    if (spinnerUI instanceof BasicSpinnerUI) {
                        BasicSpinnerUI ui = (BasicSpinnerUI)spinnerUI;
                        ui.maybeRemoveEditorBorder(c);
                    }
                }
            }
        }

        // Syncronizes the ToolTip text for the components within the spinner
        // to be the same value as the spinner ToolTip text.
        private void updateToolTipTextForChildren(JComponent spinner) {
            String toolTipText = spinner.getToolTipText();
            Component[] children = spinner.getComponents();
            for (int i = 0; i < children.length; i++) {
                if (children[i] instanceof JSpinner.DefaultEditor) {
                    JTextField tf = ((JSpinner.DefaultEditor)children[i]).getTextField();
                    if (tf != null) {
                        tf.setToolTipText(toolTipText);
                    }
                } else if (children[i] instanceof JComponent) {
                    ((JComponent)children[i]).setToolTipText( spinner.getToolTipText() );
                }
            }
        }

        public void stateChanged(ChangeEvent e) {
            if (e.getSource() instanceof JSpinner) {
                JSpinner spinner = (JSpinner)e.getSource();
                SpinnerUI spinnerUI = spinner.getUI();
                if (DefaultLookup.getBoolean(spinner, spinnerUI,
                    "Spinner.disableOnBoundaryValues", false) &&
                    spinnerUI instanceof BasicSpinnerUI) {
                    BasicSpinnerUI ui = (BasicSpinnerUI)spinnerUI;
                    ui.updateEnabledState();
                }
            }
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java BasicSpinnerUI.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.