759 lines · 25.2 KB · Java
RawDownload
/* * Copyright (c) 2002, 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.synth; import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.plaf.*;import javax.swing.event.*;import javax.swing.plaf.basic.*;import java.beans.PropertyChangeListener;import java.beans.PropertyChangeEvent; /** * Provides the Synth L&F UI delegate for * {@link javax.swing.JComboBox}. * * @author Scott Violet * @since 1.7 */public class SynthComboBoxUI extends BasicComboBoxUI implements                              PropertyChangeListener, SynthUI {    private SynthStyle style;    private boolean useListColors;     /**     * Used to adjust the location and size of the popup. Very useful for     * situations such as we find in Nimbus where part of the border is used     * to paint the focus. In such cases, the border is empty space, and not     * part of the "visual" border, and in these cases, you'd like the popup     * to be adjusted such that it looks as if it were next to the visual border.     * You may want to use negative insets to get the right look.     */    Insets popupInsets;     /**     * This flag may be set via UIDefaults. By default, it is false, to     * preserve backwards compatibility. If true, then the combo will     * "act as a button" when it is not editable.     */    private boolean buttonWhenNotEditable;     /**     * A flag to indicate that the combo box and combo box button should     * remain in the PRESSED state while the combo popup is visible.     */    private boolean pressedWhenPopupVisible;     /**     * When buttonWhenNotEditable is true, this field is used to help make     * the combo box appear and function as a button when the combo box is     * not editable. In such a state, you can click anywhere on the button     * to get it to open the popup. Also, anywhere you hover over the combo     * will cause the entire combo to go into "rollover" state, and anywhere     * you press will go into "pressed" state. This also keeps in sync the     * state of the combo and the arrowButton.     */    private ButtonHandler buttonHandler;     /**     * Handler for repainting combo when editor component gains/looses focus     */    private EditorFocusHandler editorFocusHandler;     /**     * If true, then the cell renderer will be forced to be non-opaque when     * used for rendering the selected item in the combo box (not in the list),     * and forced to opaque after rendering the selected value.     */    private boolean forceOpaque = false;     /**     * Creates a new UI object for the given component.     *     * @param c component to create UI object for     * @return the UI object     */    public static ComponentUI createUI(JComponent c) {        return new SynthComboBoxUI();    }     /**     * @inheritDoc     *     * Overridden to ensure that ButtonHandler is created prior to any of     * the other installXXX methods, since several of them reference     * buttonHandler.     */    @Override    public void installUI(JComponent c) {        buttonHandler = new ButtonHandler();        super.installUI(c);    }     @Override    protected void installDefaults() {        updateStyle(comboBox);    }     private void updateStyle(JComboBox comboBox) {        SynthStyle oldStyle = style;        SynthContext context = getContext(comboBox, ENABLED);         style = SynthLookAndFeel.updateStyle(context, this);        if (style != oldStyle) {            padding = (Insets) style.get(context, "ComboBox.padding");            popupInsets = (Insets)style.get(context, "ComboBox.popupInsets");            useListColors = style.getBoolean(context,                    "ComboBox.rendererUseListColors", true);            buttonWhenNotEditable = style.getBoolean(context,                    "ComboBox.buttonWhenNotEditable", false);            pressedWhenPopupVisible = style.getBoolean(context,                    "ComboBox.pressedWhenPopupVisible", false);            squareButton = style.getBoolean(context,                    "ComboBox.squareButton", true);             if (oldStyle != null) {                uninstallKeyboardActions();                installKeyboardActions();            }            forceOpaque = style.getBoolean(context,                    "ComboBox.forceOpaque", false);        }        context.dispose();         if(listBox != null) {            SynthLookAndFeel.updateStyles(listBox);        }    }     /**     * @inheritDoc     */    @Override    protected void installListeners() {        comboBox.addPropertyChangeListener(this);        comboBox.addMouseListener(buttonHandler);        editorFocusHandler = new EditorFocusHandler(comboBox);        super.installListeners();    }     /**     * @inheritDoc     */    @Override    public void uninstallUI(JComponent c) {        if (popup instanceof SynthComboPopup) {            ((SynthComboPopup)popup).removePopupMenuListener(buttonHandler);        }        super.uninstallUI(c);        buttonHandler = null;    }     /**     * @inheritDoc     */    @Override    protected void uninstallDefaults() {        SynthContext context = getContext(comboBox, ENABLED);         style.uninstallDefaults(context);        context.dispose();        style = null;    }     /**     * @inheritDoc     */    @Override    protected void uninstallListeners() {        editorFocusHandler.unregister();        comboBox.removePropertyChangeListener(this);        comboBox.removeMouseListener(buttonHandler);        buttonHandler.pressed = false;        buttonHandler.over = false;        super.uninstallListeners();    }     /**     * @inheritDoc     */    @Override    public SynthContext getContext(JComponent c) {        return getContext(c, getComponentState(c));    }     private SynthContext getContext(JComponent c, int state) {        return SynthContext.getContext(SynthContext.class, c,                    SynthLookAndFeel.getRegion(c), style, state);    }     private int getComponentState(JComponent c) {        // currently we have a broken situation where if a developer        // takes the border from a JComboBox and sets it on a JTextField        // then the codepath will eventually lead back to this method        // but pass in a JTextField instead of JComboBox! In case this        // happens, we just return the normal synth state for the component        // instead of doing anything special        if (!(c instanceof JComboBox)) return SynthLookAndFeel.getComponentState(c);         JComboBox box = (JComboBox)c;        if (shouldActLikeButton()) {            int state = ENABLED;            if ((!c.isEnabled())) {                state = DISABLED;            }            if (buttonHandler.isPressed()) {                state |= PRESSED;            }            if (buttonHandler.isRollover()) {                state |= MOUSE_OVER;            }            if (box.isFocusOwner()) {                state |= FOCUSED;            }            return state;        } else {            // for editable combos the editor component has the focus not the            // combo box its self, so we should make the combo paint focused            // when its editor has focus            int basicState = SynthLookAndFeel.getComponentState(c);            if (box.isEditable() &&                     box.getEditor().getEditorComponent().isFocusOwner()) {                basicState |= FOCUSED;            }            return basicState;        }    }     /**     * @inheritDoc     */    @Override    protected ComboPopup createPopup() {        SynthComboPopup p = new SynthComboPopup(comboBox);        p.addPopupMenuListener(buttonHandler);        return p;    }     /**     * @inheritDoc     */    @Override    protected ListCellRenderer createRenderer() {        return new SynthComboBoxRenderer();    }     /**     * @inheritDoc     */    @Override    protected ComboBoxEditor createEditor() {        return new SynthComboBoxEditor();    }     //    // end UI Initialization    //======================     /**     * @inheritDoc     */    @Override    public void propertyChange(PropertyChangeEvent e) {        if (SynthLookAndFeel.shouldUpdateStyle(e)) {            updateStyle(comboBox);        }    }     /**     * @inheritDoc     */    @Override    protected JButton createArrowButton() {        SynthArrowButton button = new SynthArrowButton(SwingConstants.SOUTH);        button.setName("ComboBox.arrowButton");        button.setModel(buttonHandler);        return button;    }     //=================================    // begin ComponentUI Implementation     /**     * Notifies this UI delegate to repaint the specified component.     * This method paints the component background, then calls     * the {@link #paint(SynthContext,Graphics)} method.     *     * <p>In general, this method does not need to be overridden by subclasses.     * All Look and Feel rendering code should reside in the {@code paint} method.     *     * @param g the {@code Graphics} object used for painting     * @param c the component being painted     * @see #paint(SynthContext,Graphics)     */    @Override    public void update(Graphics g, JComponent c) {        SynthContext context = getContext(c);         SynthLookAndFeel.update(context, g);        context.getPainter().paintComboBoxBackground(context, g, 0, 0,                                                  c.getWidth(), c.getHeight());        paint(context, g);        context.dispose();    }     /**     * Paints the specified component according to the Look and Feel.     * <p>This method is not used by Synth Look and Feel.     * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.     *     * @param g the {@code Graphics} object used for painting     * @param c the component being painted     * @see #paint(SynthContext,Graphics)     */    @Override    public void paint(Graphics g, JComponent c) {        SynthContext context = getContext(c);         paint(context, g);        context.dispose();    }     /**     * Paints the specified component.     *     * @param context context for the component being painted     * @param g the {@code Graphics} object used for painting     * @see #update(Graphics,JComponent)     */    protected void paint(SynthContext context, Graphics g) {        hasFocus = comboBox.hasFocus();        if ( !comboBox.isEditable() ) {            Rectangle r = rectangleForCurrentValue();            paintCurrentValue(g,r,hasFocus);        }    }     /**     * @inheritDoc     */    @Override    public void paintBorder(SynthContext context, Graphics g, int x,                            int y, int w, int h) {        context.getPainter().paintComboBoxBorder(context, g, x, y, w, h);    }     /**     * Paints the currently selected item.     */    @Override    public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {        ListCellRenderer renderer = comboBox.getRenderer();        Component c;         c = renderer.getListCellRendererComponent(                listBox, comboBox.getSelectedItem(), -1, false, false );         // Fix for 4238829: should lay out the JPanel.        boolean shouldValidate = false;        if (c instanceof JPanel)  {            shouldValidate = true;        }         if (c instanceof UIResource) {            c.setName("ComboBox.renderer");        }         boolean force = forceOpaque && c instanceof JComponent;        if (force) {            ((JComponent)c).setOpaque(false);        }         int x = bounds.x, y = bounds.y, w = bounds.width, h = bounds.height;        if (padding != null) {            x = bounds.x + padding.left;            y = bounds.y + padding.top;            w = bounds.width - (padding.left + padding.right);            h = bounds.height - (padding.top + padding.bottom);        }         currentValuePane.paintComponent(g, c, comboBox, x, y, w, h, shouldValidate);         if (force) {            ((JComponent)c).setOpaque(true);        }    }     /**     * @return true if this combo box should act as one big button. Typically     * only happens when buttonWhenNotEditable is true, and comboBox.isEditable     * is false.     */    private boolean shouldActLikeButton() {        return buttonWhenNotEditable && !comboBox.isEditable();    }     /**     * Returns the default size of an empty display area of the combo box using     * the current renderer and font.     *     * This method was overridden to use SynthComboBoxRenderer instead of     * DefaultListCellRenderer as the default renderer when calculating the     * size of the combo box. This is used in the case of the combo not having     * any data.     *     * @return the size of an empty display area     * @see #getDisplaySize     */    @Override    protected Dimension getDefaultSize() {        SynthComboBoxRenderer r = new SynthComboBoxRenderer();        Dimension d = getSizeForComponent(r.getListCellRendererComponent(listBox, " ", -1, false, false));        return new Dimension(d.width, d.height);    }     /**     * From BasicComboBoxRenderer v 1.18.     *     * Be aware that SynthFileChooserUIImpl relies on the fact that the default     * renderer installed on a Synth combo box is a JLabel. If this is changed,     * then an assert will fail in SynthFileChooserUIImpl     */    private class SynthComboBoxRenderer extends JLabel implements ListCellRenderer, UIResource {        public SynthComboBoxRenderer() {            super();            setName("ComboBox.renderer");            setText(" ");        }         @Override        public Component getListCellRendererComponent(JList list, Object value,                         int index, boolean isSelected, boolean cellHasFocus) {            setName("ComboBox.listRenderer");            SynthLookAndFeel.resetSelectedUI();            if (isSelected) {                setBackground(list.getSelectionBackground());                setForeground(list.getSelectionForeground());                if (!useListColors) {                    SynthLookAndFeel.setSelectedUI(                         (SynthLabelUI)SynthLookAndFeel.getUIOfType(getUI(),                         SynthLabelUI.class), isSelected, cellHasFocus,                         list.isEnabled(), false);                }            } else {                setBackground(list.getBackground());                setForeground(list.getForeground());            }             setFont(list.getFont());             if (value instanceof Icon) {                setIcon((Icon)value);                setText("");            } else {                String text = (value == null) ? " " : value.toString();                 if ("".equals(text)) {                    text = " ";                }                setText(text);            }             // The renderer component should inherit the enabled and            // orientation state of its parent combobox.  This is            // especially needed for GTK comboboxes, where the            // ListCellRenderer's state determines the visual state            // of the combobox.            if (comboBox != null){                setEnabled(comboBox.isEnabled());                setComponentOrientation(comboBox.getComponentOrientation());            }             return this;        }         @Override

Lines 1–500 of 759.