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