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