1/*
2 * Copyright (c) 1998, 2014, 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
26package javax.swing.plaf.metal;
27
28import java.awt.*;
29import java.awt.event.*;
30import javax.swing.plaf.basic.*;
31import javax.swing.*;
32import javax.swing.plaf.*;
33import javax.swing.border.*;
34import java.io.Serializable;
35
36/**
37 * JButton subclass to help out MetalComboBoxUI
38 * <p>
39 * <strong>Warning:</strong>
40 * Serialized objects of this class will not be compatible with
41 * future Swing releases. The current serialization support is
42 * appropriate for short term storage or RMI between applications running
43 * the same version of Swing.  As of 1.4, support for long term storage
44 * of all JavaBeans&trade;
45 * has been added to the <code>java.beans</code> package.
46 * Please see {@link java.beans.XMLEncoder}.
47 *
48 * @see MetalComboBoxButton
49 * @author Tom Santos
50 */
51@SuppressWarnings("serial") // Same-version serialization only
52public class MetalComboBoxButton extends JButton {
53
54    /**
55     * The instance of {@code JComboBox}.
56     */
57    protected JComboBox<Object> comboBox;
58
59    /**
60     * The instance of {@code JList}.
61     */
62    protected JList<Object> listBox;
63
64    /**
65     * The instance of {@code CellRendererPane}.
66     */
67    protected CellRendererPane rendererPane;
68
69    /**
70     * The icon.
71     */
72    protected Icon comboIcon;
73
74    /**
75     * The {@code iconOnly} value.
76     */
77    protected boolean iconOnly = false;
78
79    /**
80     * Returns the {@code JComboBox}.
81     *
82     * @return the {@code JComboBox}
83     */
84    public final JComboBox<Object> getComboBox() { return comboBox;}
85
86    /**
87     * Sets the {@code JComboBox}.
88     *
89     * @param cb the {@code JComboBox}
90     */
91    public final void setComboBox( JComboBox<Object> cb ) { comboBox = cb;}
92
93    /**
94     * Returns the icon of the {@code JComboBox}.
95     *
96     * @return the icon of the {@code JComboBox}
97     */
98    public final Icon getComboIcon() { return comboIcon;}
99
100    /**
101     * Sets the icon of the {@code JComboBox}.
102     *
103     * @param i the icon of the {@code JComboBox}
104     */
105    public final void setComboIcon( Icon i ) { comboIcon = i;}
106
107    /**
108     * Returns the {@code isIconOnly} value.
109     *
110     * @return the {@code isIconOnly} value
111     */
112    public final boolean isIconOnly() { return iconOnly;}
113
114    /**
115     * If {@code isIconOnly} is {@code true} then only icon is painted.
116     *
117     * @param isIconOnly if {@code true} then only icon is painted
118     */
119    public final void setIconOnly( boolean isIconOnly ) { iconOnly = isIconOnly;}
120
121    MetalComboBoxButton() {
122        super( "" );
123        DefaultButtonModel model = new DefaultButtonModel() {
124            public void setArmed( boolean armed ) {
125                super.setArmed( isPressed() ? true : armed );
126            }
127        };
128        setModel( model );
129    }
130
131    /**
132     * Constructs a new instance of {@code MetalComboBoxButton}.
133     *
134     * @param cb an instance of {@code JComboBox}
135     * @param i an icon
136     * @param pane an instance of {@code CellRendererPane}
137     * @param list an instance of {@code JList}
138     */
139    public MetalComboBoxButton( JComboBox<Object> cb, Icon i,
140                                CellRendererPane pane, JList<Object> list ) {
141        this();
142        comboBox = cb;
143        comboIcon = i;
144        rendererPane = pane;
145        listBox = list;
146        setEnabled( comboBox.isEnabled() );
147    }
148
149    /**
150     * Constructs a new instance of {@code MetalComboBoxButton}.
151     *
152     * @param cb an instance of {@code JComboBox}
153     * @param i an icon
154     * @param onlyIcon if {@code true} only icon is painted
155     * @param pane an instance of {@code CellRendererPane}
156     * @param list an instance of {@code JList}
157     */
158    public MetalComboBoxButton( JComboBox<Object> cb, Icon i, boolean onlyIcon,
159                                CellRendererPane pane, JList<Object> list ) {
160        this( cb, i, pane, list );
161        iconOnly = onlyIcon;
162    }
163
164    @SuppressWarnings("deprecation")
165    public boolean isFocusTraversable() {
166        return false;
167    }
168
169    public void setEnabled(boolean enabled) {
170        super.setEnabled(enabled);
171
172        // Set the background and foreground to the combobox colors.
173        if (enabled) {
174            setBackground(comboBox.getBackground());
175            setForeground(comboBox.getForeground());
176        } else {
177            setBackground(UIManager.getColor("ComboBox.disabledBackground"));
178            setForeground(UIManager.getColor("ComboBox.disabledForeground"));
179        }
180    }
181
182    public void paintComponent( Graphics g ) {
183        boolean leftToRight = MetalUtils.isLeftToRight(comboBox);
184
185        // Paint the button as usual
186        super.paintComponent( g );
187
188        Insets insets = getInsets();
189
190        int width = getWidth() - (insets.left + insets.right);
191        int height = getHeight() - (insets.top + insets.bottom);
192
193        if ( height <= 0 || width <= 0 ) {
194            return;
195        }
196
197        int left = insets.left;
198        int top = insets.top;
199        int right = left + (width - 1);
200        int bottom = top + (height - 1);
201
202        int iconWidth = 0;
203        int iconLeft = (leftToRight) ? right : left;
204
205        // Paint the icon
206        if ( comboIcon != null ) {
207            iconWidth = comboIcon.getIconWidth();
208            int iconHeight = comboIcon.getIconHeight();
209            int iconTop = 0;
210
211            if ( iconOnly ) {
212                iconLeft = (getWidth() / 2) - (iconWidth / 2);
213                iconTop = (getHeight() / 2) - (iconHeight / 2);
214            }
215            else {
216                if (leftToRight) {
217                    iconLeft = (left + (width - 1)) - iconWidth;
218                }
219                else {
220                    iconLeft = left;
221                }
222                iconTop = (top + ((bottom - top) / 2)) - (iconHeight / 2);
223            }
224
225            comboIcon.paintIcon( this, g, iconLeft, iconTop );
226
227            // Paint the focus
228            if ( comboBox.hasFocus() && (!MetalLookAndFeel.usingOcean() ||
229                                         comboBox.isEditable())) {
230                g.setColor( MetalLookAndFeel.getFocusColor() );
231                g.drawRect( left - 1, top - 1, width + 3, height + 1 );
232            }
233        }
234
235        if (MetalLookAndFeel.usingOcean()) {
236            // With Ocean the button only paints the arrow, bail.
237            return;
238        }
239
240        // Let the renderer paint
241        if ( ! iconOnly && comboBox != null ) {
242             ListCellRenderer<Object> renderer = comboBox.getRenderer();
243            Component c;
244            boolean renderPressed = getModel().isPressed();
245            c = renderer.getListCellRendererComponent(listBox,
246                                                      comboBox.getSelectedItem(),
247                                                      -1,
248                                                      renderPressed,
249                                                      false);
250            c.setFont(rendererPane.getFont());
251
252            if ( model.isArmed() && model.isPressed() ) {
253                if ( isOpaque() ) {
254                    c.setBackground(UIManager.getColor("Button.select"));
255                }
256                c.setForeground(comboBox.getForeground());
257            }
258            else if ( !comboBox.isEnabled() ) {
259                if ( isOpaque() ) {
260                    c.setBackground(UIManager.getColor("ComboBox.disabledBackground"));
261                }
262                c.setForeground(UIManager.getColor("ComboBox.disabledForeground"));
263            }
264            else {
265                c.setForeground(comboBox.getForeground());
266                c.setBackground(comboBox.getBackground());
267            }
268
269
270            int cWidth = width - (insets.right + iconWidth);
271
272            // Fix for 4238829: should lay out the JPanel.
273            boolean shouldValidate = false;
274            if (c instanceof JPanel)  {
275                shouldValidate = true;
276            }
277
278            if (leftToRight) {
279                rendererPane.paintComponent( g, c, this,
280                                             left, top, cWidth, height, shouldValidate );
281            }
282            else {
283                rendererPane.paintComponent( g, c, this,
284                                             left + iconWidth, top, cWidth, height, shouldValidate );
285            }
286        }
287    }
288
289    public Dimension getMinimumSize() {
290        Dimension ret = new Dimension();
291        Insets insets = getInsets();
292        ret.width = insets.left + getComboIcon().getIconWidth() + insets.right;
293        ret.height = insets.bottom + getComboIcon().getIconHeight() + insets.top;
294        return ret;
295    }
296}
297