1 /*
2 * Copyright 1998-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.swing.plaf.metal;
27
28 import java.awt;
29 import java.awt.event;
30 import javax.swing;
31 import javax.swing.plaf;
32 import javax.swing.border;
33 import javax.swing.plaf.basic;
34 import java.io.Serializable;
35 import java.beans;
36
37
38 /**
39 * Metal UI for JComboBox
40 * <p>
41 * <strong>Warning:</strong>
42 * Serialized objects of this class will not be compatible with
43 * future Swing releases. The current serialization support is
44 * appropriate for short term storage or RMI between applications running
45 * the same version of Swing. As of 1.4, support for long term storage
46 * of all JavaBeans<sup><font size="-2">TM</font></sup>
47 * has been added to the <code>java.beans</code> package.
48 * Please see {@link java.beans.XMLEncoder}.
49 *
50 * @see MetalComboBoxEditor
51 * @see MetalComboBoxButton
52 * @author Tom Santos
53 */
54 public class MetalComboBoxUI extends BasicComboBoxUI {
55
56 public static ComponentUI createUI(JComponent c) {
57 return new MetalComboBoxUI();
58 }
59
60 public void paint(Graphics g, JComponent c) {
61 if (MetalLookAndFeel.usingOcean()) {
62 super.paint(g, c);
63 }
64 }
65
66 /**
67 * If necessary paints the currently selected item.
68 *
69 * @param g Graphics to paint to
70 * @param bounds Region to paint current value to
71 * @param hasFocus whether or not the JComboBox has focus
72 * @throws NullPointerException if any of the arguments are null.
73 * @since 1.5
74 */
75 public void paintCurrentValue(Graphics g, Rectangle bounds,
76 boolean hasFocus) {
77 // This is really only called if we're using ocean.
78 if (MetalLookAndFeel.usingOcean()) {
79 bounds.x += 2;
80 bounds.width -= 3;
81 if (arrowButton != null) {
82 Insets buttonInsets = arrowButton.getInsets();
83 bounds.y += buttonInsets.top;
84 bounds.height -= (buttonInsets.top + buttonInsets.bottom);
85 }
86 else {
87 bounds.y += 2;
88 bounds.height -= 4;
89 }
90 super.paintCurrentValue(g, bounds, hasFocus);
91 }
92 else if (g == null || bounds == null) {
93 throw new NullPointerException(
94 "Must supply a non-null Graphics and Rectangle");
95 }
96 }
97
98 /**
99 * If necessary paints the background of the currently selected item.
100 *
101 * @param g Graphics to paint to
102 * @param bounds Region to paint background to
103 * @param hasFocus whether or not the JComboBox has focus
104 * @throws NullPointerException if any of the arguments are null.
105 * @since 1.5
106 */
107 public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
108 boolean hasFocus) {
109 // This is really only called if we're using ocean.
110 if (MetalLookAndFeel.usingOcean()) {
111 g.setColor(MetalLookAndFeel.getControlDarkShadow());
112 g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height - 1);
113 g.setColor(MetalLookAndFeel.getControlShadow());
114 g.drawRect(bounds.x + 1, bounds.y + 1, bounds.width - 2,
115 bounds.height - 3);
116 if (hasFocus && !isPopupVisible(comboBox) &&
117 arrowButton != null) {
118 g.setColor(listBox.getSelectionBackground());
119 Insets buttonInsets = arrowButton.getInsets();
120 if (buttonInsets.top > 2) {
121 g.fillRect(bounds.x + 2, bounds.y + 2, bounds.width - 3,
122 buttonInsets.top - 2);
123 }
124 if (buttonInsets.bottom > 2) {
125 g.fillRect(bounds.x + 2, bounds.y + bounds.height -
126 buttonInsets.bottom, bounds.width - 3,
127 buttonInsets.bottom - 2);
128 }
129 }
130 }
131 else if (g == null || bounds == null) {
132 throw new NullPointerException(
133 "Must supply a non-null Graphics and Rectangle");
134 }
135 }
136
137 /**
138 * Returns the baseline.
139 *
140 * @throws NullPointerException {@inheritDoc}
141 * @throws IllegalArgumentException {@inheritDoc}
142 * @see javax.swing.JComponent#getBaseline(int, int)
143 * @since 1.6
144 */
145 public int getBaseline(JComponent c, int width, int height) {
146 int baseline;
147 if (MetalLookAndFeel.usingOcean()) {
148 height -= 4;
149 baseline = super.getBaseline(c, width, height);
150 if (baseline >= 0) {
151 baseline += 2;
152 }
153 }
154 else {
155 baseline = super.getBaseline(c, width, height);
156 }
157 return baseline;
158 }
159
160 protected ComboBoxEditor createEditor() {
161 return new MetalComboBoxEditor.UIResource();
162 }
163
164 protected ComboPopup createPopup() {
165 return super.createPopup();
166 }
167
168 protected JButton createArrowButton() {
169 boolean iconOnly = (comboBox.isEditable() ||
170 MetalLookAndFeel.usingOcean());
171 JButton button = new MetalComboBoxButton( comboBox,
172 new MetalComboBoxIcon(),
173 iconOnly,
174 currentValuePane,
175 listBox );
176 button.setMargin( new Insets( 0, 1, 1, 3 ) );
177 if (MetalLookAndFeel.usingOcean()) {
178 // Disabled rollover effect.
179 button.putClientProperty(MetalBorders.NO_BUTTON_ROLLOVER,
180 Boolean.TRUE);
181 }
182 updateButtonForOcean(button);
183 return button;
184 }
185
186 /**
187 * Resets the necessary state on the ComboBoxButton for ocean.
188 */
189 private void updateButtonForOcean(JButton button) {
190 if (MetalLookAndFeel.usingOcean()) {
191 // Ocean renders the focus in a different way, this
192 // would be redundant.
193 button.setFocusPainted(comboBox.isEditable());
194 }
195 }
196
197 public PropertyChangeListener createPropertyChangeListener() {
198 return new MetalPropertyChangeListener();
199 }
200
201 /**
202 * This inner class is marked "public" due to a compiler bug.
203 * This class should be treated as a "protected" inner class.
204 * Instantiate it only within subclasses of <FooUI>.
205 */
206 public class MetalPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler {
207 public void propertyChange(PropertyChangeEvent e) {
208 super.propertyChange( e );
209 String propertyName = e.getPropertyName();
210
211 if ( propertyName == "editable" ) {
212 if(arrowButton instanceof MetalComboBoxButton) {
213 MetalComboBoxButton button = (MetalComboBoxButton)arrowButton;
214 button.setIconOnly( comboBox.isEditable() ||
215 MetalLookAndFeel.usingOcean() );
216 }
217 comboBox.repaint();
218 updateButtonForOcean(arrowButton);
219 } else if ( propertyName == "background" ) {
220 Color color = (Color)e.getNewValue();
221 arrowButton.setBackground(color);
222 listBox.setBackground(color);
223
224 } else if ( propertyName == "foreground" ) {
225 Color color = (Color)e.getNewValue();
226 arrowButton.setForeground(color);
227 listBox.setForeground(color);
228 }
229 }
230 }
231
232 /**
233 * As of Java 2 platform v1.4 this method is no longer used. Do not call or
234 * override. All the functionality of this method is in the
235 * MetalPropertyChangeListener.
236 *
237 * @deprecated As of Java 2 platform v1.4.
238 */
239 @Deprecated
240 protected void editablePropertyChanged( PropertyChangeEvent e ) { }
241
242 protected LayoutManager createLayoutManager() {
243 return new MetalComboBoxLayoutManager();
244 }
245
246 /**
247 * This inner class is marked "public" due to a compiler bug.
248 * This class should be treated as a "protected" inner class.
249 * Instantiate it only within subclasses of <FooUI>.
250 */
251 public class MetalComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager {
252 public void layoutContainer( Container parent ) {
253 layoutComboBox( parent, this );
254 }
255 public void superLayout( Container parent ) {
256 super.layoutContainer( parent );
257 }
258 }
259
260 // This is here because of a bug in the compiler.
261 // When a protected-inner-class-savvy compiler comes out we
262 // should move this into MetalComboBoxLayoutManager.
263 public void layoutComboBox( Container parent, MetalComboBoxLayoutManager manager ) {
264 if (comboBox.isEditable() && !MetalLookAndFeel.usingOcean()) {
265 manager.superLayout( parent );
266 return;
267 }
268
269 if (arrowButton != null) {
270 if (MetalLookAndFeel.usingOcean() ) {
271 Insets insets = comboBox.getInsets();
272 int buttonWidth = arrowButton.getMinimumSize().width;
273 arrowButton.setBounds(MetalUtils.isLeftToRight(comboBox)
274 ? (comboBox.getWidth() - insets.right - buttonWidth)
275 : insets.left,
276 insets.top, buttonWidth,
277 comboBox.getHeight() - insets.top - insets.bottom);
278 }
279 else {
280 Insets insets = comboBox.getInsets();
281 int width = comboBox.getWidth();
282 int height = comboBox.getHeight();
283 arrowButton.setBounds( insets.left, insets.top,
284 width - (insets.left + insets.right),
285 height - (insets.top + insets.bottom) );
286 }
287 }
288
289 if (editor != null && MetalLookAndFeel.usingOcean()) {
290 Rectangle cvb = rectangleForCurrentValue();
291 editor.setBounds(cvb);
292 }
293 }
294
295 /**
296 * As of Java 2 platform v1.4 this method is no
297 * longer used.
298 *
299 * @deprecated As of Java 2 platform v1.4.
300 */
301 @Deprecated
302 protected void removeListeners() {
303 if ( propertyChangeListener != null ) {
304 comboBox.removePropertyChangeListener( propertyChangeListener );
305 }
306 }
307
308 // These two methods were overloaded and made public. This was probably a
309 // mistake in the implementation. The functionality that they used to
310 // provide is no longer necessary and should be removed. However,
311 // removing them will create an uncompatible API change.
312
313 public void configureEditor() {
314 super.configureEditor();
315 }
316
317 public void unconfigureEditor() {
318 super.unconfigureEditor();
319 }
320
321 public Dimension getMinimumSize( JComponent c ) {
322 if ( !isMinimumSizeDirty ) {
323 return new Dimension( cachedMinimumSize );
324 }
325
326 Dimension size = null;
327
328 if ( !comboBox.isEditable() &&
329 arrowButton != null) {
330 Insets buttonInsets = arrowButton.getInsets();
331 Insets insets = comboBox.getInsets();
332
333 size = getDisplaySize();
334 size.width += insets.left + insets.right;
335 size.width += buttonInsets.right;
336 size.width += arrowButton.getMinimumSize().width;
337 size.height += insets.top + insets.bottom;
338 size.height += buttonInsets.top + buttonInsets.bottom;
339 }
340 else if ( comboBox.isEditable() &&
341 arrowButton != null &&
342 editor != null ) {
343 size = super.getMinimumSize( c );
344 Insets margin = arrowButton.getMargin();
345 size.height += margin.top + margin.bottom;
346 size.width += margin.left + margin.right;
347 }
348 else {
349 size = super.getMinimumSize( c );
350 }
351
352 cachedMinimumSize.setSize( size.width, size.height );
353 isMinimumSizeDirty = false;
354
355 return new Dimension( cachedMinimumSize );
356 }
357
358 /**
359 * This inner class is marked "public" due to a compiler bug.
360 * This class should be treated as a "protected" inner class.
361 * Instantiate it only within subclasses of <FooUI>.
362 *
363 * This class is now obsolete and doesn't do anything and
364 * is only included for backwards API compatibility. Do not call or
365 * override.
366 *
367 * @deprecated As of Java 2 platform v1.4.
368 */
369 @Deprecated
370 public class MetalComboPopup extends BasicComboPopup {
371
372 public MetalComboPopup( JComboBox cBox) {
373 super( cBox );
374 }
375
376 // This method was overloaded and made public. This was probably
377 // mistake in the implementation. The functionality that they used to
378 // provide is no longer necessary and should be removed. However,
379 // removing them will create an uncompatible API change.
380
381 public void delegateFocus(MouseEvent e) {
382 super.delegateFocus(e);
383 }
384 }
385 }