1 /* 2 * Copyright (c) 1998, 2010, 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™ 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 class should be treated as a "protected" inner class. 203 * Instantiate it only within subclasses of {@code MetalComboBoxUI}. 204 */ 205 public class MetalPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler { 206 public void propertyChange(PropertyChangeEvent e) { 207 super.propertyChange( e ); 208 String propertyName = e.getPropertyName(); 209 210 if ( propertyName == "editable" ) { 211 if(arrowButton instanceof MetalComboBoxButton) { 212 MetalComboBoxButton button = (MetalComboBoxButton)arrowButton; 213 button.setIconOnly( comboBox.isEditable() || 214 MetalLookAndFeel.usingOcean() ); 215 } 216 comboBox.repaint(); 217 updateButtonForOcean(arrowButton); 218 } else if ( propertyName == "background" ) { 219 Color color = (Color)e.getNewValue(); 220 arrowButton.setBackground(color); 221 listBox.setBackground(color); 222 223 } else if ( propertyName == "foreground" ) { 224 Color color = (Color)e.getNewValue(); 225 arrowButton.setForeground(color); 226 listBox.setForeground(color); 227 } 228 } 229 } 230 231 /** 232 * As of Java 2 platform v1.4 this method is no longer used. Do not call or 233 * override. All the functionality of this method is in the 234 * MetalPropertyChangeListener. 235 * 236 * @deprecated As of Java 2 platform v1.4. 237 */ 238 @Deprecated 239 protected void editablePropertyChanged( PropertyChangeEvent e ) { } 240 241 protected LayoutManager createLayoutManager() { 242 return new MetalComboBoxLayoutManager(); 243 } 244 245 /** 246 * This class should be treated as a "protected" inner class. 247 * Instantiate it only within subclasses of {@code MetalComboBoxUI}. 248 */ 249 public class MetalComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager { 250 public void layoutContainer( Container parent ) { 251 layoutComboBox( parent, this ); 252 } 253 public void superLayout( Container parent ) { 254 super.layoutContainer( parent ); 255 } 256 } 257 258 // This is here because of a bug in the compiler. 259 // When a protected-inner-class-savvy compiler comes out we 260 // should move this into MetalComboBoxLayoutManager. 261 public void layoutComboBox( Container parent, MetalComboBoxLayoutManager manager ) { 262 if (comboBox.isEditable() && !MetalLookAndFeel.usingOcean()) { 263 manager.superLayout( parent ); 264 return; 265 } 266 267 if (arrowButton != null) { 268 if (MetalLookAndFeel.usingOcean() ) { 269 Insets insets = comboBox.getInsets(); 270 int buttonWidth = arrowButton.getMinimumSize().width; 271 arrowButton.setBounds(MetalUtils.isLeftToRight(comboBox) 272 ? (comboBox.getWidth() - insets.right - buttonWidth) 273 : insets.left, 274 insets.top, buttonWidth, 275 comboBox.getHeight() - insets.top - insets.bottom); 276 } 277 else { 278 Insets insets = comboBox.getInsets(); 279 int width = comboBox.getWidth(); 280 int height = comboBox.getHeight(); 281 arrowButton.setBounds( insets.left, insets.top, 282 width - (insets.left + insets.right), 283 height - (insets.top + insets.bottom) ); 284 } 285 } 286 287 if (editor != null && MetalLookAndFeel.usingOcean()) { 288 Rectangle cvb = rectangleForCurrentValue(); 289 editor.setBounds(cvb); 290 } 291 } 292 293 /** 294 * As of Java 2 platform v1.4 this method is no 295 * longer used. 296 * 297 * @deprecated As of Java 2 platform v1.4. 298 */ 299 @Deprecated 300 protected void removeListeners() { 301 if ( propertyChangeListener != null ) { 302 comboBox.removePropertyChangeListener( propertyChangeListener ); 303 } 304 } 305 306 // These two methods were overloaded and made public. This was probably a 307 // mistake in the implementation. The functionality that they used to 308 // provide is no longer necessary and should be removed. However, 309 // removing them will create an uncompatible API change. 310 311 public void configureEditor() { 312 super.configureEditor(); 313 } 314 315 public void unconfigureEditor() { 316 super.unconfigureEditor(); 317 } 318 319 public Dimension getMinimumSize( JComponent c ) { 320 if ( !isMinimumSizeDirty ) { 321 return new Dimension( cachedMinimumSize ); 322 } 323 324 Dimension size = null; 325 326 if ( !comboBox.isEditable() && 327 arrowButton != null) { 328 Insets buttonInsets = arrowButton.getInsets(); 329 Insets insets = comboBox.getInsets(); 330 331 size = getDisplaySize(); 332 size.width += insets.left + insets.right; 333 size.width += buttonInsets.right; 334 size.width += arrowButton.getMinimumSize().width; 335 size.height += insets.top + insets.bottom; 336 size.height += buttonInsets.top + buttonInsets.bottom; 337 } 338 else if ( comboBox.isEditable() && 339 arrowButton != null && 340 editor != null ) { 341 size = super.getMinimumSize( c ); 342 Insets margin = arrowButton.getMargin(); 343 size.height += margin.top + margin.bottom; 344 size.width += margin.left + margin.right; 345 } 346 else { 347 size = super.getMinimumSize( c ); 348 } 349 350 cachedMinimumSize.setSize( size.width, size.height ); 351 isMinimumSizeDirty = false; 352 353 return new Dimension( cachedMinimumSize ); 354 } 355 356 /** 357 * This class should be treated as a "protected" inner class. 358 * Instantiate it only within subclasses of {@code MetalComboBoxUI}. 359 * 360 * This class is now obsolete and doesn't do anything and 361 * is only included for backwards API compatibility. Do not call or 362 * override. 363 * 364 * @deprecated As of Java 2 platform v1.4. 365 */ 366 @Deprecated 367 public class MetalComboPopup extends BasicComboPopup { 368 369 public MetalComboPopup( JComboBox cBox) { 370 super( cBox ); 371 } 372 373 // This method was overloaded and made public. This was probably 374 // mistake in the implementation. The functionality that they used to 375 // provide is no longer necessary and should be removed. However, 376 // removing them will create an uncompatible API change. 377 378 public void delegateFocus(MouseEvent e) { 379 super.delegateFocus(e); 380 } 381 } 382 }