1 /* 2 * Copyright (c) 1998, 2005, 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.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() && height >= 4) { 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 }