1/* 2 * Copyright (c) 2011, 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 26package com.apple.laf; 27 28import java.awt.*; 29import java.awt.event.*; 30 31import javax.accessibility.*; 32import javax.swing.*; 33import javax.swing.border.Border; 34import javax.swing.event.*; 35import javax.swing.plaf.*; 36import javax.swing.plaf.basic.*; 37import com.apple.laf.ClientPropertyApplicator.Property; 38import apple.laf.JRSUIConstants.Size; 39 40import com.apple.laf.AquaUtilControlSize.Sizeable; 41import com.apple.laf.AquaUtils.RecyclableSingleton; 42 43// Inspired by MetalComboBoxUI, which also has a combined text-and-arrow button for noneditables 44public class AquaComboBoxUI extends BasicComboBoxUI implements Sizeable { 45 static final String POPDOWN_CLIENT_PROPERTY_KEY = "JComboBox.isPopDown"; 46 static final String ISSQUARE_CLIENT_PROPERTY_KEY = "JComboBox.isSquare"; 47 48 public static ComponentUI createUI(final JComponent c) { 49 return new AquaComboBoxUI(); 50 } 51 52 private boolean wasOpaque; 53 public void installUI(final JComponent c) { 54 super.installUI(c); 55 56 // this doesn't work right now, because the JComboBox.init() method calls 57 // .setOpaque(false) directly, and doesn't allow the LaF to decided. Bad Sun! 58 LookAndFeel.installProperty(c, "opaque", Boolean.FALSE); 59 60 wasOpaque = c.isOpaque(); 61 c.setOpaque(false); 62 } 63 64 public void uninstallUI(final JComponent c) { 65 c.setOpaque(wasOpaque); 66 super.uninstallUI(c); 67 } 68 69 protected void installListeners() { 70 super.installListeners(); 71 AquaUtilControlSize.addSizePropertyListener(comboBox); 72 } 73 74 protected void uninstallListeners() { 75 AquaUtilControlSize.removeSizePropertyListener(comboBox); 76 super.uninstallListeners(); 77 } 78 79 protected void installComponents() { 80 super.installComponents(); 81 82 // client properties must be applied after the components have been installed, 83 // because isSquare and isPopdown are applied to the installed button 84 getApplicator().attachAndApplyClientProperties(comboBox); 85 } 86 87 protected void uninstallComponents() { 88 getApplicator().removeFrom(comboBox); 89 super.uninstallComponents(); 90 } 91 92 protected ItemListener createItemListener() { 93 return new ItemListener() { 94 long lastBlink = 0L; 95 public void itemStateChanged(final ItemEvent e) { 96 if (e.getStateChange() != ItemEvent.SELECTED) return; 97 if (!popup.isVisible()) return; 98 99 // sometimes, multiple selection changes can occur while the popup is up, 100 // and blinking more than "once" (in a second) is not desirable 101 final long now = System.currentTimeMillis(); 102 if (now - 1000 < lastBlink) return; 103 lastBlink = now; 104 105 final JList<Object> itemList = popup.getList(); 106 final ListUI listUI = itemList.getUI(); 107 if (!(listUI instanceof AquaListUI)) return; 108 final AquaListUI aquaListUI = (AquaListUI)listUI; 109 110 final int selectedIndex = comboBox.getSelectedIndex(); 111 final ListModel<Object> dataModel = itemList.getModel(); 112 if (dataModel == null) return; 113 114 final Object value = dataModel.getElementAt(selectedIndex); 115 AquaUtils.blinkMenu(new AquaUtils.Selectable() { 116 public void paintSelected(final boolean selected) { 117 aquaListUI.repaintCell(value, selectedIndex, selected); 118 } 119 }); 120 } 121 }; 122 } 123 124 public void paint(final Graphics g, final JComponent c) { 125 // this space intentionally left blank 126 } 127 128 protected ListCellRenderer<Object> createRenderer() { 129 return new AquaComboBoxRenderer(comboBox); 130 } 131 132 protected ComboPopup createPopup() { 133 return new AquaComboBoxPopup(comboBox); 134 } 135 136 protected JButton createArrowButton() { 137 return new AquaComboBoxButton(this, comboBox, currentValuePane, listBox); 138 } 139 140 protected ComboBoxEditor createEditor() { 141 return new AquaComboBoxEditor(); 142 } 143 144 final class AquaComboBoxEditor extends BasicComboBoxEditor 145 implements UIResource, DocumentListener { 146 147 AquaComboBoxEditor() { 148 super(); 149 editor = new AquaCustomComboTextField(); 150 editor.addFocusListener(this); 151 editor.getDocument().addDocumentListener(this); 152 } 153 154 @Override 155 public void changedUpdate(final DocumentEvent e) { 156 editorTextChanged(); 157 } 158 159 @Override 160 public void insertUpdate(final DocumentEvent e) { 161 editorTextChanged(); 162 } 163 164 @Override 165 public void removeUpdate(final DocumentEvent e) { 166 editorTextChanged(); 167 } 168 169 private void editorTextChanged() { 170 if (!popup.isVisible()) return; 171 172 final Object text = editor.getText(); 173 174 final ListModel<Object> model = listBox.getModel(); 175 final int items = model.getSize(); 176 for (int i = 0; i < items; i++) { 177 final Object element = model.getElementAt(i); 178 if (element == null) continue; 179 180 final String asString = element.toString(); 181 if (asString == null || !asString.equals(text)) continue; 182 183 popup.getList().setSelectedIndex(i); 184 return; 185 } 186 187 popup.getList().clearSelection(); 188 } 189 } 190 191 @SuppressWarnings("serial") // Superclass is not serializable across versions 192 class AquaCustomComboTextField extends JTextField { 193 @SuppressWarnings("serial") // anonymous class 194 public AquaCustomComboTextField() { 195 final InputMap inputMap = getInputMap(); 196 inputMap.put(KeyStroke.getKeyStroke("DOWN"), highlightNextAction); 197 inputMap.put(KeyStroke.getKeyStroke("KP_DOWN"), highlightNextAction); 198 inputMap.put(KeyStroke.getKeyStroke("UP"), highlightPreviousAction); 199 inputMap.put(KeyStroke.getKeyStroke("KP_UP"), highlightPreviousAction); 200 201 inputMap.put(KeyStroke.getKeyStroke("HOME"), highlightFirstAction); 202 inputMap.put(KeyStroke.getKeyStroke("END"), highlightLastAction); 203 inputMap.put(KeyStroke.getKeyStroke("PAGE_UP"), highlightPageUpAction); 204 inputMap.put(KeyStroke.getKeyStroke("PAGE_DOWN"), highlightPageDownAction); 205 206 final Action action = getActionMap().get(JTextField.notifyAction); 207 inputMap.put(KeyStroke.getKeyStroke("ENTER"), new AbstractAction() { 208 public void actionPerformed(final ActionEvent e) { 209 if (popup.isVisible()) { 210 triggerSelectionEvent(comboBox, e); 211 212 if (editor instanceof AquaCustomComboTextField) { 213 ((AquaCustomComboTextField)editor).selectAll(); 214 } 215 } else { 216 action.actionPerformed(e); 217 } 218 } 219 }); 220 } 221 222 // workaround for 4530952 223 public void setText(final String s) { 224 if (getText().equals(s)) { 225 return; 226 } 227 super.setText(s); 228 } 229 } 230 231 /** 232 * This listener hides the popup when the focus is lost. It also repaints 233 * when focus is gained or lost. 234 * 235 * This override is necessary because the Basic L&F for the combo box is working 236 * around a Solaris-only bug that we don't have on Mac OS X. So, remove the lightweight 237 * popup check here. rdar://Problem/3518582 238 */ 239 protected FocusListener createFocusListener() { 240 return new BasicComboBoxUI.FocusHandler() { 241 @Override 242 public void focusGained(FocusEvent e) { 243 super.focusGained(e); 244 245 if (arrowButton != null) { 246 arrowButton.repaint(); 247 } 248 } 249 250 @Override 251 public void focusLost(final FocusEvent e) { 252 hasFocus = false; 253 if (!e.isTemporary()) { 254 setPopupVisible(comboBox, false); 255 } 256 comboBox.repaint(); 257 258 // Notify assistive technologies that the combo box lost focus 259 final AccessibleContext ac = ((Accessible)comboBox).getAccessibleContext(); 260 if (ac != null) { 261 ac.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.FOCUSED, null); 262 } 263 264 if (arrowButton != null) { 265 arrowButton.repaint(); 266 } 267 } 268 }; 269 } 270 271 protected void installKeyboardActions() { 272 super.installKeyboardActions(); 273 274 ActionMap actionMap = new ActionMapUIResource(); 275 276 actionMap.put("aquaSelectNext", highlightNextAction); 277 actionMap.put("aquaSelectPrevious", highlightPreviousAction); 278 actionMap.put("enterPressed", triggerSelectionAction); 279 actionMap.put("aquaSpacePressed", toggleSelectionAction); 280 281 actionMap.put("aquaSelectHome", highlightFirstAction); 282 actionMap.put("aquaSelectEnd", highlightLastAction); 283 actionMap.put("aquaSelectPageUp", highlightPageUpAction); 284 actionMap.put("aquaSelectPageDown", highlightPageDownAction); 285 286 actionMap.put("aquaHidePopup", hideAction); 287 288 SwingUtilities.replaceUIActionMap(comboBox, actionMap); 289 } 290 291 @SuppressWarnings("serial") // Superclass is not serializable across versions 292 private abstract class ComboBoxAction extends AbstractAction { 293 public void actionPerformed(final ActionEvent e) { 294 if (!comboBox.isEnabled() || !comboBox.isShowing()) { 295 return; 296 } 297 298 if (comboBox.isPopupVisible()) { 299 final AquaComboBoxUI ui = (AquaComboBoxUI)comboBox.getUI(); 300 performComboBoxAction(ui); 301 } else { 302 comboBox.setPopupVisible(true); 303 } 304 } 305 306 abstract void performComboBoxAction(final AquaComboBoxUI ui); 307 } 308 309 /** 310 * Hilight _but do not select_ the next item in the list. 311 */ 312 @SuppressWarnings("serial") // anonymous class 313 private Action highlightNextAction = new ComboBoxAction() { 314 @Override 315 public void performComboBoxAction(AquaComboBoxUI ui) { 316 final int si = listBox.getSelectedIndex(); 317 318 if (si < comboBox.getModel().getSize() - 1) { 319 listBox.setSelectedIndex(si + 1); 320 listBox.ensureIndexIsVisible(si + 1); 321 } 322 comboBox.repaint(); 323 } 324 }; 325 326 /** 327 * Hilight _but do not select_ the previous item in the list. 328 */ 329 @SuppressWarnings("serial") // anonymous class 330 private Action highlightPreviousAction = new ComboBoxAction() { 331 @Override 332 void performComboBoxAction(final AquaComboBoxUI ui) { 333 final int si = listBox.getSelectedIndex(); 334 if (si > 0) { 335 listBox.setSelectedIndex(si - 1); 336 listBox.ensureIndexIsVisible(si - 1); 337 } 338 comboBox.repaint(); 339 } 340 }; 341 342 @SuppressWarnings("serial") // anonymous class 343 private Action highlightFirstAction = new ComboBoxAction() { 344 @Override 345 void performComboBoxAction(final AquaComboBoxUI ui) { 346 listBox.setSelectedIndex(0); 347 listBox.ensureIndexIsVisible(0); 348 } 349 }; 350 351 @SuppressWarnings("serial") // anonymous class 352 private Action highlightLastAction = new ComboBoxAction() { 353 @Override 354 void performComboBoxAction(final AquaComboBoxUI ui) { 355 final int size = listBox.getModel().getSize(); 356 listBox.setSelectedIndex(size - 1); 357 listBox.ensureIndexIsVisible(size - 1); 358 } 359 }; 360 361 @SuppressWarnings("serial") // anonymous class 362 private Action highlightPageUpAction = new ComboBoxAction() { 363 @Override 364 void performComboBoxAction(final AquaComboBoxUI ui) { 365 final int current = listBox.getSelectedIndex(); 366 final int first = listBox.getFirstVisibleIndex(); 367 368 if (current != first) { 369 listBox.setSelectedIndex(first); 370 return; 371 } 372 373 final int page = listBox.getVisibleRect().height / listBox.getCellBounds(0, 0).height; 374 int target = first - page; 375 if (target < 0) target = 0; 376 377 listBox.ensureIndexIsVisible(target); 378 listBox.setSelectedIndex(target); 379 } 380 }; 381 382 @SuppressWarnings("serial") // anonymous class 383 private Action highlightPageDownAction = new ComboBoxAction() { 384 @Override 385 void performComboBoxAction(final AquaComboBoxUI ui) { 386 final int current = listBox.getSelectedIndex(); 387 final int last = listBox.getLastVisibleIndex(); 388 389 if (current != last) { 390 listBox.setSelectedIndex(last); 391 return; 392 } 393 394 final int page = listBox.getVisibleRect().height / listBox.getCellBounds(0, 0).height; 395 final int end = listBox.getModel().getSize() - 1; 396 int target = last + page; 397 if (target > end) target = end; 398 399 listBox.ensureIndexIsVisible(target); 400 listBox.setSelectedIndex(target); 401 } 402 }; 403 404 // For <rdar://problem/3759984> Java 1.4.2_5: Serializing Swing components not working 405 // Inner classes were using a this reference and then trying to serialize the AquaComboBoxUI 406 // We shouldn't do that. But we need to be able to get the popup from other classes, so we need 407 // a public accessor. 408 public ComboPopup getPopup() { 409 return popup; 410 } 411 412 protected LayoutManager createLayoutManager() { 413 return new AquaComboBoxLayoutManager(); 414 } 415 416 class AquaComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager { 417 public void layoutContainer(final Container parent) { 418 if (arrowButton != null && !comboBox.isEditable()) { 419 final Insets insets = comboBox.getInsets(); 420 final int width = comboBox.getWidth(); 421 final int height = comboBox.getHeight(); 422 arrowButton.setBounds(insets.left, insets.top, width - (insets.left + insets.right), height - (insets.top + insets.bottom)); 423 return; 424 } 425 426 final JComboBox<?> cb = (JComboBox<?>) parent; 427 final int width = cb.getWidth(); 428 final int height = cb.getHeight(); 429 430 final Insets insets = getInsets(); 431 final int buttonHeight = height - (insets.top + insets.bottom); 432 final int buttonWidth = 20; 433 434 if (arrowButton != null) { 435 arrowButton.setBounds(width - (insets.right + buttonWidth), insets.top, buttonWidth, buttonHeight); 436 } 437 438 if (editor != null) { 439 final Rectangle editorRect = rectangleForCurrentValue(); 440 editorRect.width += 4; 441 editorRect.height += 1; 442 editor.setBounds(editorRect); 443 } 444 } 445 } 446 447 // This is here because Sun can't use protected like they should! 448 protected static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor"; 449 450 protected static boolean isTableCellEditor(final JComponent c) { 451 return Boolean.TRUE.equals(c.getClientProperty(AquaComboBoxUI.IS_TABLE_CELL_EDITOR)); 452 } 453 454 protected static boolean isPopdown(final JComboBox<?> c) { 455 return c.isEditable() || Boolean.TRUE.equals(c.getClientProperty(AquaComboBoxUI.POPDOWN_CLIENT_PROPERTY_KEY)); 456 } 457 458 protected static void triggerSelectionEvent(final JComboBox<?> comboBox, final ActionEvent e) { 459 if (!comboBox.isEnabled()) return; 460 461 final AquaComboBoxUI aquaUi = (AquaComboBoxUI)comboBox.getUI(); 462 463 if (aquaUi.getPopup().getList().getSelectedIndex() < 0) { 464 comboBox.setPopupVisible(false); 465 } 466 467 if (isTableCellEditor(comboBox)) { 468 // Forces the selection of the list item if the combo box is in a JTable 469 comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex()); 470 return; 471 } 472 473 if (comboBox.isPopupVisible()) { 474 comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex()); 475 comboBox.setPopupVisible(false); 476 return; 477 } 478 479 // Call the default button binding. 480 // This is a pretty messy way of passing an event through to the root pane 481 final JRootPane root = SwingUtilities.getRootPane(comboBox); 482 if (root == null) return; 483 484 final InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); 485 final ActionMap am = root.getActionMap(); 486 if (im == null || am == null) return; 487 488 final Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); 489 if (obj == null) return; 490 491 final Action action = am.get(obj); 492 if (action == null) return; 493 494 action.actionPerformed(new ActionEvent(root, e.getID(), e.getActionCommand(), e.getWhen(), e.getModifiers())); 495 } 496 497 // This is somewhat messy. The difference here from BasicComboBoxUI.EnterAction is that 498 // arrow up or down does not automatically select the 499 @SuppressWarnings("serial") // anonymous class 500 private final Action triggerSelectionAction = new AbstractAction() { 501 public void actionPerformed(final ActionEvent e) { 502 triggerSelectionEvent((JComboBox)e.getSource(), e); 503 } 504 505 @Override 506 public boolean isEnabled() { 507 return comboBox.isPopupVisible() && super.isEnabled(); 508 } 509 }; 510 511 @SuppressWarnings("serial") // anonymous class 512 private static final Action toggleSelectionAction = new AbstractAction() { 513 public void actionPerformed(final ActionEvent e) { 514 final JComboBox<?> comboBox = (JComboBox<?>) e.getSource(); 515 if (!comboBox.isEnabled()) return; 516 if (comboBox.isEditable()) return; 517 518 final AquaComboBoxUI aquaUi = (AquaComboBoxUI)comboBox.getUI(); 519 520 if (comboBox.isPopupVisible()) { 521 comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex()); 522 comboBox.setPopupVisible(false); 523 return; 524 } 525 526 comboBox.setPopupVisible(true); 527 } 528 }; 529 530 @SuppressWarnings("serial") // anonymous class 531 private final Action hideAction = new AbstractAction() { 532 @Override 533 public void actionPerformed(final ActionEvent e) { 534 final JComboBox<?> comboBox = (JComboBox<?>) e.getSource(); 535 comboBox.firePopupMenuCanceled(); 536 comboBox.setPopupVisible(false); 537 } 538 539 @Override 540 public boolean isEnabled() { 541 return comboBox.isPopupVisible() && super.isEnabled(); 542 } 543 }; 544 545 public void applySizeFor(final JComponent c, final Size size) { 546 if (arrowButton == null) return; 547 final Border border = arrowButton.getBorder(); 548 if (!(border instanceof AquaButtonBorder)) return; 549 final AquaButtonBorder aquaBorder = (AquaButtonBorder)border; 550 arrowButton.setBorder(aquaBorder.deriveBorderForSize(size)); 551 } 552 553 public Dimension getMinimumSize(final JComponent c) { 554 if (!isMinimumSizeDirty) { 555 return new Dimension(cachedMinimumSize); 556 } 557 558 final boolean editable = comboBox.isEditable(); 559 560 final Dimension size; 561 if (!editable && arrowButton != null && arrowButton instanceof AquaComboBoxButton) { 562 final AquaComboBoxButton button = (AquaComboBoxButton)arrowButton; 563 final Insets buttonInsets = button.getInsets(); 564 // Insets insets = comboBox.getInsets(); 565 final Insets insets = new Insets(0, 5, 0, 25);//comboBox.getInsets(); 566 567 size = getDisplaySize(); 568 size.width += insets.left + insets.right; 569 size.width += buttonInsets.left + buttonInsets.right; 570 size.width += buttonInsets.right + 10; 571 size.height += insets.top + insets.bottom; 572 size.height += buttonInsets.top + buttonInsets.bottom; 573 // Min height = Height of arrow button plus 2 pixels fuzz above plus 2 below. 23 + 2 + 2 574 size.height = Math.max(27, size.height); 575 } else if (editable && arrowButton != null && editor != null) { 576 size = super.getMinimumSize(c); 577 final Insets margin = arrowButton.getMargin(); 578 size.height += margin.top + margin.bottom; 579 } else { 580 size = super.getMinimumSize(c); 581 } 582 583 final Border border = c.getBorder(); 584 if (border != null) { 585 final Insets insets = border.getBorderInsets(c); 586 size.height += insets.top + insets.bottom; 587 size.width += insets.left + insets.right; 588 } 589 590 cachedMinimumSize.setSize(size.width, size.height); 591 isMinimumSizeDirty = false; 592 593 return new Dimension(cachedMinimumSize); 594 } 595 596 @SuppressWarnings("unchecked") 597 private static final RecyclableSingleton<ClientPropertyApplicator<JComboBox<?>, AquaComboBoxUI>> APPLICATOR = new 598 RecyclableSingleton<ClientPropertyApplicator<JComboBox<?>, AquaComboBoxUI>>() { 599 @Override 600 protected ClientPropertyApplicator<JComboBox<?>, AquaComboBoxUI> getInstance() { 601 return new ClientPropertyApplicator<JComboBox<?>, AquaComboBoxUI>( 602 new Property<AquaComboBoxUI>(AquaFocusHandler.FRAME_ACTIVE_PROPERTY) { 603 public void applyProperty(final AquaComboBoxUI target, final Object value) { 604 if (Boolean.FALSE.equals(value)) { 605 if (target.comboBox != null) target.comboBox.hidePopup(); 606 } 607 if (target.listBox != null) target.listBox.repaint(); 608 } 609 }, 610 new Property<AquaComboBoxUI>("editable") { 611 public void applyProperty(final AquaComboBoxUI target, final Object value) { 612 if (target.comboBox == null) return; 613 target.comboBox.repaint(); 614 } 615 }, 616 new Property<AquaComboBoxUI>("background") { 617 public void applyProperty(final AquaComboBoxUI target, final Object value) { 618 final Color color = (Color)value; 619 if (target.arrowButton != null) target.arrowButton.setBackground(color); 620 if (target.listBox != null) target.listBox.setBackground(color); 621 } 622 }, 623 new Property<AquaComboBoxUI>("foreground") { 624 public void applyProperty(final AquaComboBoxUI target, final Object value) { 625 final Color color = (Color)value; 626 if (target.arrowButton != null) target.arrowButton.setForeground(color); 627 if (target.listBox != null) target.listBox.setForeground(color); 628 } 629 }, 630 new Property<AquaComboBoxUI>(POPDOWN_CLIENT_PROPERTY_KEY) { 631 public void applyProperty(final AquaComboBoxUI target, final Object value) { 632 if (!(target.arrowButton instanceof AquaComboBoxButton)) return; 633 ((AquaComboBoxButton)target.arrowButton).setIsPopDown(Boolean.TRUE.equals(value)); 634 } 635 }, 636 new Property<AquaComboBoxUI>(ISSQUARE_CLIENT_PROPERTY_KEY) { 637 public void applyProperty(final AquaComboBoxUI target, final Object value) { 638 if (!(target.arrowButton instanceof AquaComboBoxButton)) return; 639 ((AquaComboBoxButton)target.arrowButton).setIsSquare(Boolean.TRUE.equals(value)); 640 } 641 } 642 ) { 643 public AquaComboBoxUI convertJComponentToTarget(final JComboBox<?> combo) { 644 final ComboBoxUI comboUI = combo.getUI(); 645 if (comboUI instanceof AquaComboBoxUI) return (AquaComboBoxUI)comboUI; 646 return null; 647 } 648 }; 649 } 650 }; 651 static ClientPropertyApplicator<JComboBox<?>, AquaComboBoxUI> getApplicator() { 652 return APPLICATOR.get(); 653 } 654} 655