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 26 package com.apple.laf; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 31 import javax.accessibility.*; 32 import javax.swing.*; 33 import javax.swing.border.Border; 34 import javax.swing.event.*; 35 import javax.swing.plaf.*; 36 import javax.swing.plaf.basic.*; 37 import com.apple.laf.ClientPropertyApplicator.Property; 38 import apple.laf.JRSUIConstants.Size; 39 40 import com.apple.laf.AquaUtilControlSize.Sizeable; 41 import com.apple.laf.AquaUtils.RecyclableSingleton; 42 43 // Inspired by MetalComboBoxUI, which also has a combined text-and-arrow button for noneditables 44 public 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 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 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 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 focusGained(final FocusEvent e) { 156 if (arrowButton != null) { 157 arrowButton.repaint(); 158 } 159 } 160 161 @Override 162 public void focusLost(final FocusEvent e) { 163 if (arrowButton != null) { 164 arrowButton.repaint(); 165 } 166 } 167 168 @Override 169 public void changedUpdate(final DocumentEvent e) { 170 editorTextChanged(); 171 } 172 173 @Override 174 public void insertUpdate(final DocumentEvent e) { 175 editorTextChanged(); 176 } 177 178 @Override 179 public void removeUpdate(final DocumentEvent e) { 180 editorTextChanged(); 181 } 182 183 private void editorTextChanged() { 184 if (!popup.isVisible()) return; 185 186 final Object text = editor.getText(); 187 188 final ListModel model = listBox.getModel(); 189 final int items = model.getSize(); 190 for (int i = 0; i < items; i++) { 191 final Object element = model.getElementAt(i); 192 if (element == null) continue; 193 194 final String asString = element.toString(); 195 if (asString == null || !asString.equals(text)) continue; 196 197 popup.getList().setSelectedIndex(i); 198 return; 199 } 200 201 popup.getList().clearSelection(); 202 } 203 } 204 205 @SuppressWarnings("serial") // Superclass is not serializable across versions 206 class AquaCustomComboTextField extends JTextField { 207 @SuppressWarnings("serial") // anonymous class 208 public AquaCustomComboTextField() { 209 final InputMap inputMap = getInputMap(); 210 inputMap.put(KeyStroke.getKeyStroke("DOWN"), highlightNextAction); 211 inputMap.put(KeyStroke.getKeyStroke("KP_DOWN"), highlightNextAction); 212 inputMap.put(KeyStroke.getKeyStroke("UP"), highlightPreviousAction); 213 inputMap.put(KeyStroke.getKeyStroke("KP_UP"), highlightPreviousAction); 214 215 inputMap.put(KeyStroke.getKeyStroke("HOME"), highlightFirstAction); 216 inputMap.put(KeyStroke.getKeyStroke("END"), highlightLastAction); 217 inputMap.put(KeyStroke.getKeyStroke("PAGE_UP"), highlightPageUpAction); 218 inputMap.put(KeyStroke.getKeyStroke("PAGE_DOWN"), highlightPageDownAction); 219 220 final Action action = getActionMap().get(JTextField.notifyAction); 221 inputMap.put(KeyStroke.getKeyStroke("ENTER"), new AbstractAction() { 222 public void actionPerformed(final ActionEvent e) { 223 if (popup.isVisible()) { 224 triggerSelectionEvent(comboBox, e); 225 226 if (editor instanceof AquaCustomComboTextField) { 227 ((AquaCustomComboTextField)editor).selectAll(); 228 } 229 } else { 230 action.actionPerformed(e); 231 } 232 } 233 }); 234 } 235 236 // workaround for 4530952 237 public void setText(final String s) { 238 if (getText().equals(s)) { 239 return; 240 } 241 super.setText(s); 242 } 243 } 244 245 /** 246 * This listener hides the popup when the focus is lost. It also repaints 247 * when focus is gained or lost. 248 * 249 * This override is necessary because the Basic L&F for the combo box is working 250 * around a Solaris-only bug that we don't have on Mac OS X. So, remove the lightweight 251 * popup check here. rdar://Problem/3518582 252 */ 253 protected FocusListener createFocusListener() { 254 return new BasicComboBoxUI.FocusHandler() { 255 public void focusLost(final FocusEvent e) { 256 hasFocus = false; 257 if (!e.isTemporary()) { 258 setPopupVisible(comboBox, false); 259 } 260 comboBox.repaint(); 261 262 // Notify assistive technologies that the combo box lost focus 263 final AccessibleContext ac = ((Accessible)comboBox).getAccessibleContext(); 264 if (ac != null) { 265 ac.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.FOCUSED, null); 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("aquaEnterPressed", 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 editor.setBounds(editorRect); 442 } 443 } 444 } 445 446 // This is here because Sun can't use protected like they should! 447 protected static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor"; 448 449 protected static boolean isTableCellEditor(final JComponent c) { 450 return Boolean.TRUE.equals(c.getClientProperty(AquaComboBoxUI.IS_TABLE_CELL_EDITOR)); 451 } 452 453 protected static boolean isPopdown(final JComboBox c) { 454 return c.isEditable() || Boolean.TRUE.equals(c.getClientProperty(AquaComboBoxUI.POPDOWN_CLIENT_PROPERTY_KEY)); 455 } 456 457 protected static void triggerSelectionEvent(final JComboBox comboBox, final ActionEvent e) { 458 if (!comboBox.isEnabled()) return; 459 460 final AquaComboBoxUI aquaUi = (AquaComboBoxUI)comboBox.getUI(); 461 462 if (aquaUi.getPopup().getList().getSelectedIndex() < 0) { 463 comboBox.setPopupVisible(false); 464 } 465 466 if (isTableCellEditor(comboBox)) { 467 // Forces the selection of the list item if the combo box is in a JTable 468 comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex()); 469 return; 470 } 471 472 if (comboBox.isPopupVisible()) { 473 comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex()); 474 comboBox.setPopupVisible(false); 475 return; 476 } 477 478 // Call the default button binding. 479 // This is a pretty messy way of passing an event through to the root pane 480 final JRootPane root = SwingUtilities.getRootPane(comboBox); 481 if (root == null) return; 482 483 final InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); 484 final ActionMap am = root.getActionMap(); 485 if (im == null || am == null) return; 486 487 final Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); 488 if (obj == null) return; 489 490 final Action action = am.get(obj); 491 if (action == null) return; 492 493 action.actionPerformed(new ActionEvent(root, e.getID(), e.getActionCommand(), e.getWhen(), e.getModifiers())); 494 } 495 496 // This is somewhat messy. The difference here from BasicComboBoxUI.EnterAction is that 497 // arrow up or down does not automatically select the 498 @SuppressWarnings("serial") // anonymous class 499 private static final Action triggerSelectionAction = new AbstractAction() { 500 public void actionPerformed(final ActionEvent e) { 501 triggerSelectionEvent((JComboBox)e.getSource(), e); 502 } 503 }; 504 505 @SuppressWarnings("serial") // anonymous class 506 private static final Action toggleSelectionAction = new AbstractAction() { 507 public void actionPerformed(final ActionEvent e) { 508 final JComboBox comboBox = (JComboBox)e.getSource(); 509 if (!comboBox.isEnabled()) return; 510 if (comboBox.isEditable()) return; 511 512 final AquaComboBoxUI aquaUi = (AquaComboBoxUI)comboBox.getUI(); 513 514 if (comboBox.isPopupVisible()) { 515 comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex()); 516 comboBox.setPopupVisible(false); 517 return; 518 } 519 520 comboBox.setPopupVisible(true); 521 } 522 }; 523 524 @SuppressWarnings("serial") // anonymous class 525 private static Action hideAction = new AbstractAction() { 526 @Override 527 public void actionPerformed(final ActionEvent e) { 528 final JComboBox comboBox = (JComboBox)e.getSource(); 529 530 if (comboBox.isPopupVisible()) { 531 comboBox.firePopupMenuCanceled(); 532 comboBox.setPopupVisible(false); 533 } 534 } 535 }; 536 537 public void applySizeFor(final JComponent c, final Size size) { 538 if (arrowButton == null) return; 539 final Border border = arrowButton.getBorder(); 540 if (!(border instanceof AquaButtonBorder)) return; 541 final AquaButtonBorder aquaBorder = (AquaButtonBorder)border; 542 arrowButton.setBorder(aquaBorder.deriveBorderForSize(size)); 543 } 544 545 public Dimension getMinimumSize(final JComponent c) { 546 if (!isMinimumSizeDirty) { 547 return new Dimension(cachedMinimumSize); 548 } 549 550 final boolean editable = comboBox.isEditable(); 551 552 final Dimension size; 553 if (!editable && arrowButton != null && arrowButton instanceof AquaComboBoxButton) { 554 final AquaComboBoxButton button = (AquaComboBoxButton)arrowButton; 555 final Insets buttonInsets = button.getInsets(); 556 // Insets insets = comboBox.getInsets(); 557 final Insets insets = new Insets(0, 5, 0, 25);//comboBox.getInsets(); 558 559 size = getDisplaySize(); 560 size.width += insets.left + insets.right; 561 size.width += buttonInsets.left + buttonInsets.right; 562 size.width += buttonInsets.right + 10; 563 size.height += insets.top + insets.bottom; 564 size.height += buttonInsets.top + buttonInsets.bottom; 565 // Min height = Height of arrow button plus 2 pixels fuzz above plus 2 below. 23 + 2 + 2 566 size.height = Math.max(27, size.height); 567 } else if (editable && arrowButton != null && editor != null) { 568 size = super.getMinimumSize(c); 569 final Insets margin = arrowButton.getMargin(); 570 size.height += margin.top + margin.bottom; 571 } else { 572 size = super.getMinimumSize(c); 573 } 574 575 final Border border = c.getBorder(); 576 if (border != null) { 577 final Insets insets = border.getBorderInsets(c); 578 size.height += insets.top + insets.bottom; 579 size.width += insets.left + insets.right; 580 } 581 582 cachedMinimumSize.setSize(size.width, size.height); 583 isMinimumSizeDirty = false; 584 585 return new Dimension(cachedMinimumSize); 586 } 587 588 @SuppressWarnings("unchecked") 589 static final RecyclableSingleton<ClientPropertyApplicator<JComboBox, AquaComboBoxUI>> APPLICATOR = new RecyclableSingleton<ClientPropertyApplicator<JComboBox, AquaComboBoxUI>>() { 590 @Override 591 protected ClientPropertyApplicator<JComboBox, AquaComboBoxUI> getInstance() { 592 return new ClientPropertyApplicator<JComboBox, AquaComboBoxUI>( 593 new Property<AquaComboBoxUI>(AquaFocusHandler.FRAME_ACTIVE_PROPERTY) { 594 public void applyProperty(final AquaComboBoxUI target, final Object value) { 595 if (Boolean.FALSE.equals(value)) { 596 if (target.comboBox != null) target.comboBox.hidePopup(); 597 } 598 if (target.listBox != null) target.listBox.repaint(); 599 } 600 }, 601 new Property<AquaComboBoxUI>("editable") { 602 public void applyProperty(final AquaComboBoxUI target, final Object value) { 603 if (target.comboBox == null) return; 604 target.comboBox.repaint(); 605 } 606 }, 607 new Property<AquaComboBoxUI>("background") { 608 public void applyProperty(final AquaComboBoxUI target, final Object value) { 609 final Color color = (Color)value; 610 if (target.arrowButton != null) target.arrowButton.setBackground(color); 611 if (target.listBox != null) target.listBox.setBackground(color); 612 } 613 }, 614 new Property<AquaComboBoxUI>("foreground") { 615 public void applyProperty(final AquaComboBoxUI target, final Object value) { 616 final Color color = (Color)value; 617 if (target.arrowButton != null) target.arrowButton.setForeground(color); 618 if (target.listBox != null) target.listBox.setForeground(color); 619 } 620 }, 621 new Property<AquaComboBoxUI>(POPDOWN_CLIENT_PROPERTY_KEY) { 622 public void applyProperty(final AquaComboBoxUI target, final Object value) { 623 if (!(target.arrowButton instanceof AquaComboBoxButton)) return; 624 ((AquaComboBoxButton)target.arrowButton).setIsPopDown(Boolean.TRUE.equals(value)); 625 } 626 }, 627 new Property<AquaComboBoxUI>(ISSQUARE_CLIENT_PROPERTY_KEY) { 628 public void applyProperty(final AquaComboBoxUI target, final Object value) { 629 if (!(target.arrowButton instanceof AquaComboBoxButton)) return; 630 ((AquaComboBoxButton)target.arrowButton).setIsSquare(Boolean.TRUE.equals(value)); 631 } 632 } 633 ) { 634 public AquaComboBoxUI convertJComponentToTarget(final JComboBox combo) { 635 final ComboBoxUI comboUI = combo.getUI(); 636 if (comboUI instanceof AquaComboBoxUI) return (AquaComboBoxUI)comboUI; 637 return null; 638 } 639 }; 640 } 641 }; 642 static ClientPropertyApplicator<JComboBox, AquaComboBoxUI> getApplicator() { 643 return APPLICATOR.get(); 644 } 645 }