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 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 @SuppressWarnings("serial") // Same-version serialization only 55 public class MetalComboBoxUI extends BasicComboBoxUI { 56 57 public static ComponentUI createUI(JComponent c) { 58 return new MetalComboBoxUI(); 59 } 60 61 public void paint(Graphics g, JComponent c) { 62 if (MetalLookAndFeel.usingOcean()) { 63 super.paint(g, c); 64 } 65 } 66 67 /** 68 * If necessary paints the currently selected item. 69 * 70 * @param g Graphics to paint to 71 * @param bounds Region to paint current value to 72 * @param hasFocus whether or not the JComboBox has focus 73 * @throws NullPointerException if any of the arguments are null. 74 * @since 1.5 75 */ 76 public void paintCurrentValue(Graphics g, Rectangle bounds, 77 boolean hasFocus) { 78 // This is really only called if we're using ocean. 79 if (MetalLookAndFeel.usingOcean()) { 80 bounds.x += 2; 81 bounds.width -= 3; 82 if (arrowButton != null) { 83 Insets buttonInsets = arrowButton.getInsets(); 84 bounds.y += buttonInsets.top; 85 bounds.height -= (buttonInsets.top + buttonInsets.bottom); 86 } 87 else { 88 bounds.y += 2; 89 bounds.height -= 4; 90 } 91 super.paintCurrentValue(g, bounds, hasFocus); 92 } 93 else if (g == null || bounds == null) { 94 throw new NullPointerException( 95 "Must supply a non-null Graphics and Rectangle"); 96 } 97 } 98 99 /** 100 * If necessary paints the background of the currently selected item. 101 * 102 * @param g Graphics to paint to 103 * @param bounds Region to paint background to 104 * @param hasFocus whether or not the JComboBox has focus 105 * @throws NullPointerException if any of the arguments are null. 106 * @since 1.5 107 */ 108 public void paintCurrentValueBackground(Graphics g, Rectangle bounds, 109 boolean hasFocus) { 110 // This is really only called if we're using ocean. 111 if (MetalLookAndFeel.usingOcean()) { 112 g.setColor(MetalLookAndFeel.getControlDarkShadow()); 113 g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height - 1); 114 g.setColor(MetalLookAndFeel.getControlShadow()); 115 g.drawRect(bounds.x + 1, bounds.y + 1, bounds.width - 2, 116 bounds.height - 3); 117 if (hasFocus && !isPopupVisible(comboBox) && 118 arrowButton != null) { 119 g.setColor(listBox.getSelectionBackground()); 120 Insets buttonInsets = arrowButton.getInsets(); 121 if (buttonInsets.top > 2) { 122 g.fillRect(bounds.x + 2, bounds.y + 2, bounds.width - 3, 123 buttonInsets.top - 2); 124 } 125 if (buttonInsets.bottom > 2) { 126 g.fillRect(bounds.x + 2, bounds.y + bounds.height - 127 buttonInsets.bottom, bounds.width - 3, 128 buttonInsets.bottom - 2); 129 } 130 } 131 } 132 else if (g == null || bounds == null) { 133 throw new NullPointerException( 134 "Must supply a non-null Graphics and Rectangle"); 135 } 136 } 137 138 /** 139 * Returns the baseline. 140 * 141 * @throws NullPointerException {@inheritDoc} 142 * @throws IllegalArgumentException {@inheritDoc} 143 * @see javax.swing.JComponent#getBaseline(int, int) 144 * @since 1.6 145 */ 146 public int getBaseline(JComponent c, int width, int height) { 147 int baseline; 148 if (MetalLookAndFeel.usingOcean() && height >= 4) { 149 height -= 4; 150 baseline = super.getBaseline(c, width, height); 151 if (baseline >= 0) { 152 baseline += 2; 153 } 154 } 155 else { 156 baseline = super.getBaseline(c, width, height); 157 } 158 return baseline; 159 } 160 161 protected ComboBoxEditor createEditor() { 162 return new MetalComboBoxEditor.UIResource(); 163 } 164 165 protected ComboPopup createPopup() { 166 return super.createPopup(); 167 } 168 169 protected JButton createArrowButton() { 170 boolean iconOnly = (comboBox.isEditable() || 171 MetalLookAndFeel.usingOcean()); 172 JButton button = new MetalComboBoxButton( comboBox, 173 new MetalComboBoxIcon(), 174 iconOnly, 175 currentValuePane, 176 listBox ); 177 button.setMargin( new Insets( 0, 1, 1, 3 ) ); 178 if (MetalLookAndFeel.usingOcean()) { 179 // Disabled rollover effect. 180 button.putClientProperty(MetalBorders.NO_BUTTON_ROLLOVER, 181 Boolean.TRUE); 182 } 183 updateButtonForOcean(button); 184 return button; 185 } 186 187 /** 188 * Resets the necessary state on the ComboBoxButton for ocean. 189 */ 190 private void updateButtonForOcean(JButton button) { 191 if (MetalLookAndFeel.usingOcean()) { 192 // Ocean renders the focus in a different way, this 193 // would be redundant. 194 button.setFocusPainted(comboBox.isEditable()); 195 } 196 } 197 198 public PropertyChangeListener createPropertyChangeListener() { 199 return new MetalPropertyChangeListener(); 200 } 201 202 /** 203 * This class should be treated as a "protected" inner class. 204 * Instantiate it only within subclasses of {@code MetalComboBoxUI}. 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 class should be treated as a "protected" inner class. 248 * Instantiate it only within subclasses of {@code MetalComboBoxUI}. 249 */ 250 public class MetalComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager { 251 public void layoutContainer( Container parent ) { 252 layoutComboBox( parent, this ); 253 } 254 public void superLayout( Container parent ) { 255 super.layoutContainer( parent ); 256 } 257 } 258 259 // This is here because of a bug in the compiler. 260 // When a protected-inner-class-savvy compiler comes out we 261 // should move this into MetalComboBoxLayoutManager. 262 public void layoutComboBox( Container parent, MetalComboBoxLayoutManager manager ) { 263 if (comboBox.isEditable() && !MetalLookAndFeel.usingOcean()) { 264 manager.superLayout( parent ); 265 return; 266 } 267 268 if (arrowButton != null) { 269 if (MetalLookAndFeel.usingOcean() ) { 270 Insets insets = comboBox.getInsets(); 271 int buttonWidth = arrowButton.getMinimumSize().width; 272 arrowButton.setBounds(MetalUtils.isLeftToRight(comboBox) 273 ? (comboBox.getWidth() - insets.right - buttonWidth) 274 : insets.left, 275 insets.top, buttonWidth, 276 comboBox.getHeight() - insets.top - insets.bottom); 277 } 278 else { 279 Insets insets = comboBox.getInsets(); 280 int width = comboBox.getWidth(); 281 int height = comboBox.getHeight(); 282 arrowButton.setBounds( insets.left, insets.top, 283 width - (insets.left + insets.right), 284 height - (insets.top + insets.bottom) ); 285 } 286 } 287 288 if (editor != null && MetalLookAndFeel.usingOcean()) { 289 Rectangle cvb = rectangleForCurrentValue(); 290 editor.setBounds(cvb); 291 } 292 } 293 294 /** 295 * As of Java 2 platform v1.4 this method is no 296 * longer used. 297 * 298 * @deprecated As of Java 2 platform v1.4. 299 */ 300 @Deprecated 301 protected void removeListeners() { 302 if ( propertyChangeListener != null ) { 303 comboBox.removePropertyChangeListener( propertyChangeListener ); 304 } 305 } 306 307 // These two methods were overloaded and made public. This was probably a 308 // mistake in the implementation. The functionality that they used to 309 // provide is no longer necessary and should be removed. However, 310 // removing them will create an uncompatible API change. 311 312 public void configureEditor() { 313 super.configureEditor(); 314 } 315 316 public void unconfigureEditor() { 317 super.unconfigureEditor(); 318 } 319 320 public Dimension getMinimumSize( JComponent c ) { 321 if ( !isMinimumSizeDirty ) { 322 return new Dimension( cachedMinimumSize ); 323 } 324 325 Dimension size = null; 326 327 if ( !comboBox.isEditable() && 328 arrowButton != null) { 329 Insets buttonInsets = arrowButton.getInsets(); 330 Insets insets = comboBox.getInsets(); 331 332 size = getDisplaySize(); 333 size.width += insets.left + insets.right; 334 size.width += buttonInsets.right; 335 size.width += arrowButton.getMinimumSize().width; 336 size.height += insets.top + insets.bottom; 337 size.height += buttonInsets.top + buttonInsets.bottom; 338 } 339 else if ( comboBox.isEditable() && 340 arrowButton != null && 341 editor != null ) { 342 size = super.getMinimumSize( c ); 343 Insets margin = arrowButton.getMargin(); 344 size.height += margin.top + margin.bottom; 345 size.width += margin.left + margin.right; 346 } 347 else { 348 size = super.getMinimumSize( c ); 349 } 350 351 cachedMinimumSize.setSize( size.width, size.height ); 352 isMinimumSizeDirty = false; 353 354 return new Dimension( cachedMinimumSize ); 355 } 356 357 /** 358 * This class should be treated as a "protected" inner class. 359 * Instantiate it only within subclasses of {@code MetalComboBoxUI}. 360 * 361 * This class is now obsolete and doesn't do anything and 362 * is only included for backwards API compatibility. Do not call or 363 * override. 364 * 365 * @deprecated As of Java 2 platform v1.4. 366 */ 367 @Deprecated 368 public class MetalComboPopup extends BasicComboPopup { 369 370 public MetalComboPopup( JComboBox cBox) { 371 super( cBox ); 372 } 373 374 // This method was overloaded and made public. This was probably 375 // mistake in the implementation. The functionality that they used to 376 // provide is no longer necessary and should be removed. However, 377 // removing them will create an uncompatible API change. 378 379 public void delegateFocus(MouseEvent e) { 380 super.delegateFocus(e); 381 } 382 } 383 }