Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/com/apple/laf/AquaComboBoxUI.java
38831 views
1
/*
2
* Copyright (c) 2011, 2013, 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 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 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
class AquaCustomComboTextField extends JTextField {
192
public AquaCustomComboTextField() {
193
final InputMap inputMap = getInputMap();
194
inputMap.put(KeyStroke.getKeyStroke("DOWN"), highlightNextAction);
195
inputMap.put(KeyStroke.getKeyStroke("KP_DOWN"), highlightNextAction);
196
inputMap.put(KeyStroke.getKeyStroke("UP"), highlightPreviousAction);
197
inputMap.put(KeyStroke.getKeyStroke("KP_UP"), highlightPreviousAction);
198
199
inputMap.put(KeyStroke.getKeyStroke("HOME"), highlightFirstAction);
200
inputMap.put(KeyStroke.getKeyStroke("END"), highlightLastAction);
201
inputMap.put(KeyStroke.getKeyStroke("PAGE_UP"), highlightPageUpAction);
202
inputMap.put(KeyStroke.getKeyStroke("PAGE_DOWN"), highlightPageDownAction);
203
204
final Action action = getActionMap().get(JTextField.notifyAction);
205
inputMap.put(KeyStroke.getKeyStroke("ENTER"), new AbstractAction() {
206
public void actionPerformed(final ActionEvent e) {
207
if (popup.isVisible()) {
208
triggerSelectionEvent(comboBox, e);
209
210
if (editor instanceof AquaCustomComboTextField) {
211
((AquaCustomComboTextField)editor).selectAll();
212
}
213
} else {
214
action.actionPerformed(e);
215
}
216
}
217
});
218
}
219
220
// workaround for 4530952
221
public void setText(final String s) {
222
if (getText().equals(s)) {
223
return;
224
}
225
super.setText(s);
226
}
227
}
228
229
/**
230
* This listener hides the popup when the focus is lost. It also repaints
231
* when focus is gained or lost.
232
*
233
* This override is necessary because the Basic L&F for the combo box is working
234
* around a Solaris-only bug that we don't have on Mac OS X. So, remove the lightweight
235
* popup check here. rdar://Problem/3518582
236
*/
237
protected FocusListener createFocusListener() {
238
return new BasicComboBoxUI.FocusHandler() {
239
@Override
240
public void focusGained(FocusEvent e) {
241
super.focusGained(e);
242
243
if (arrowButton != null) {
244
arrowButton.repaint();
245
}
246
}
247
248
@Override
249
public void focusLost(final FocusEvent e) {
250
hasFocus = false;
251
if (!e.isTemporary()) {
252
setPopupVisible(comboBox, false);
253
}
254
comboBox.repaint();
255
256
// Notify assistive technologies that the combo box lost focus
257
final AccessibleContext ac = ((Accessible)comboBox).getAccessibleContext();
258
if (ac != null) {
259
ac.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, AccessibleState.FOCUSED, null);
260
}
261
262
if (arrowButton != null) {
263
arrowButton.repaint();
264
}
265
}
266
};
267
}
268
269
protected void installKeyboardActions() {
270
super.installKeyboardActions();
271
272
ActionMap actionMap = new ActionMapUIResource();
273
274
actionMap.put("aquaSelectNext", highlightNextAction);
275
actionMap.put("aquaSelectPrevious", highlightPreviousAction);
276
actionMap.put("enterPressed", triggerSelectionAction);
277
actionMap.put("aquaSpacePressed", toggleSelectionAction);
278
279
actionMap.put("aquaSelectHome", highlightFirstAction);
280
actionMap.put("aquaSelectEnd", highlightLastAction);
281
actionMap.put("aquaSelectPageUp", highlightPageUpAction);
282
actionMap.put("aquaSelectPageDown", highlightPageDownAction);
283
284
actionMap.put("aquaHidePopup", hideAction);
285
286
SwingUtilities.replaceUIActionMap(comboBox, actionMap);
287
}
288
289
private abstract class ComboBoxAction extends AbstractAction {
290
public void actionPerformed(final ActionEvent e) {
291
if (!comboBox.isEnabled() || !comboBox.isShowing()) {
292
return;
293
}
294
295
if (comboBox.isPopupVisible()) {
296
final AquaComboBoxUI ui = (AquaComboBoxUI)comboBox.getUI();
297
performComboBoxAction(ui);
298
} else {
299
comboBox.setPopupVisible(true);
300
}
301
}
302
303
abstract void performComboBoxAction(final AquaComboBoxUI ui);
304
}
305
306
/**
307
* Hilight _but do not select_ the next item in the list.
308
*/
309
private Action highlightNextAction = new ComboBoxAction() {
310
@Override
311
public void performComboBoxAction(AquaComboBoxUI ui) {
312
final int si = listBox.getSelectedIndex();
313
314
if (si < comboBox.getModel().getSize() - 1) {
315
listBox.setSelectedIndex(si + 1);
316
listBox.ensureIndexIsVisible(si + 1);
317
}
318
comboBox.repaint();
319
}
320
};
321
322
/**
323
* Hilight _but do not select_ the previous item in the list.
324
*/
325
private Action highlightPreviousAction = new ComboBoxAction() {
326
@Override
327
void performComboBoxAction(final AquaComboBoxUI ui) {
328
final int si = listBox.getSelectedIndex();
329
if (si > 0) {
330
listBox.setSelectedIndex(si - 1);
331
listBox.ensureIndexIsVisible(si - 1);
332
}
333
comboBox.repaint();
334
}
335
};
336
337
private Action highlightFirstAction = new ComboBoxAction() {
338
@Override
339
void performComboBoxAction(final AquaComboBoxUI ui) {
340
listBox.setSelectedIndex(0);
341
listBox.ensureIndexIsVisible(0);
342
}
343
};
344
345
private Action highlightLastAction = new ComboBoxAction() {
346
@Override
347
void performComboBoxAction(final AquaComboBoxUI ui) {
348
final int size = listBox.getModel().getSize();
349
listBox.setSelectedIndex(size - 1);
350
listBox.ensureIndexIsVisible(size - 1);
351
}
352
};
353
354
private Action highlightPageUpAction = new ComboBoxAction() {
355
@Override
356
void performComboBoxAction(final AquaComboBoxUI ui) {
357
final int current = listBox.getSelectedIndex();
358
final int first = listBox.getFirstVisibleIndex();
359
360
if (current != first) {
361
listBox.setSelectedIndex(first);
362
return;
363
}
364
365
final int page = listBox.getVisibleRect().height / listBox.getCellBounds(0, 0).height;
366
int target = first - page;
367
if (target < 0) target = 0;
368
369
listBox.ensureIndexIsVisible(target);
370
listBox.setSelectedIndex(target);
371
}
372
};
373
374
private Action highlightPageDownAction = new ComboBoxAction() {
375
@Override
376
void performComboBoxAction(final AquaComboBoxUI ui) {
377
final int current = listBox.getSelectedIndex();
378
final int last = listBox.getLastVisibleIndex();
379
380
if (current != last) {
381
listBox.setSelectedIndex(last);
382
return;
383
}
384
385
final int page = listBox.getVisibleRect().height / listBox.getCellBounds(0, 0).height;
386
final int end = listBox.getModel().getSize() - 1;
387
int target = last + page;
388
if (target > end) target = end;
389
390
listBox.ensureIndexIsVisible(target);
391
listBox.setSelectedIndex(target);
392
}
393
};
394
395
// For <rdar://problem/3759984> Java 1.4.2_5: Serializing Swing components not working
396
// Inner classes were using a this reference and then trying to serialize the AquaComboBoxUI
397
// We shouldn't do that. But we need to be able to get the popup from other classes, so we need
398
// a public accessor.
399
public ComboPopup getPopup() {
400
return popup;
401
}
402
403
protected LayoutManager createLayoutManager() {
404
return new AquaComboBoxLayoutManager();
405
}
406
407
class AquaComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager {
408
public void layoutContainer(final Container parent) {
409
if (arrowButton != null && !comboBox.isEditable()) {
410
final Insets insets = comboBox.getInsets();
411
final int width = comboBox.getWidth();
412
final int height = comboBox.getHeight();
413
arrowButton.setBounds(insets.left, insets.top, width - (insets.left + insets.right), height - (insets.top + insets.bottom));
414
return;
415
}
416
417
final JComboBox cb = (JComboBox)parent;
418
final int width = cb.getWidth();
419
final int height = cb.getHeight();
420
421
final Insets insets = getInsets();
422
final int buttonHeight = height - (insets.top + insets.bottom);
423
final int buttonWidth = 20;
424
425
if (arrowButton != null) {
426
arrowButton.setBounds(width - (insets.right + buttonWidth), insets.top, buttonWidth, buttonHeight);
427
}
428
429
if (editor != null) {
430
final Rectangle editorRect = rectangleForCurrentValue();
431
editorRect.width += 4;
432
editorRect.height += 1;
433
editor.setBounds(editorRect);
434
}
435
}
436
}
437
438
// This is here because Sun can't use protected like they should!
439
protected static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
440
441
protected static boolean isTableCellEditor(final JComponent c) {
442
return Boolean.TRUE.equals(c.getClientProperty(AquaComboBoxUI.IS_TABLE_CELL_EDITOR));
443
}
444
445
protected static boolean isPopdown(final JComboBox c) {
446
return c.isEditable() || Boolean.TRUE.equals(c.getClientProperty(AquaComboBoxUI.POPDOWN_CLIENT_PROPERTY_KEY));
447
}
448
449
protected static void triggerSelectionEvent(final JComboBox comboBox, final ActionEvent e) {
450
if (!comboBox.isEnabled()) return;
451
452
final AquaComboBoxUI aquaUi = (AquaComboBoxUI)comboBox.getUI();
453
454
if (aquaUi.getPopup().getList().getSelectedIndex() < 0) {
455
comboBox.setPopupVisible(false);
456
}
457
458
if (isTableCellEditor(comboBox)) {
459
// Forces the selection of the list item if the combo box is in a JTable
460
comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex());
461
return;
462
}
463
464
if (comboBox.isPopupVisible()) {
465
comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex());
466
comboBox.setPopupVisible(false);
467
return;
468
}
469
470
// Call the default button binding.
471
// This is a pretty messy way of passing an event through to the root pane
472
final JRootPane root = SwingUtilities.getRootPane(comboBox);
473
if (root == null) return;
474
475
final InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
476
final ActionMap am = root.getActionMap();
477
if (im == null || am == null) return;
478
479
final Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
480
if (obj == null) return;
481
482
final Action action = am.get(obj);
483
if (action == null) return;
484
485
action.actionPerformed(new ActionEvent(root, e.getID(), e.getActionCommand(), e.getWhen(), e.getModifiers()));
486
}
487
488
// This is somewhat messy. The difference here from BasicComboBoxUI.EnterAction is that
489
// arrow up or down does not automatically select the
490
private final Action triggerSelectionAction = new AbstractAction() {
491
public void actionPerformed(final ActionEvent e) {
492
triggerSelectionEvent((JComboBox)e.getSource(), e);
493
}
494
495
@Override
496
public boolean isEnabled() {
497
return comboBox.isPopupVisible() && super.isEnabled();
498
}
499
};
500
501
private static final Action toggleSelectionAction = new AbstractAction() {
502
public void actionPerformed(final ActionEvent e) {
503
final JComboBox comboBox = (JComboBox)e.getSource();
504
if (!comboBox.isEnabled()) return;
505
if (comboBox.isEditable()) return;
506
507
final AquaComboBoxUI aquaUi = (AquaComboBoxUI)comboBox.getUI();
508
509
if (comboBox.isPopupVisible()) {
510
comboBox.setSelectedIndex(aquaUi.getPopup().getList().getSelectedIndex());
511
comboBox.setPopupVisible(false);
512
return;
513
}
514
515
comboBox.setPopupVisible(true);
516
}
517
};
518
519
private final Action hideAction = new AbstractAction() {
520
@Override
521
public void actionPerformed(final ActionEvent e) {
522
final JComboBox comboBox = (JComboBox)e.getSource();
523
comboBox.firePopupMenuCanceled();
524
comboBox.setPopupVisible(false);
525
}
526
527
@Override
528
public boolean isEnabled() {
529
return comboBox.isPopupVisible() && super.isEnabled();
530
}
531
};
532
533
public void applySizeFor(final JComponent c, final Size size) {
534
if (arrowButton == null) return;
535
final Border border = arrowButton.getBorder();
536
if (!(border instanceof AquaButtonBorder)) return;
537
final AquaButtonBorder aquaBorder = (AquaButtonBorder)border;
538
arrowButton.setBorder(aquaBorder.deriveBorderForSize(size));
539
}
540
541
public Dimension getMinimumSize(final JComponent c) {
542
if (!isMinimumSizeDirty) {
543
return new Dimension(cachedMinimumSize);
544
}
545
546
final boolean editable = comboBox.isEditable();
547
548
final Dimension size;
549
if (!editable && arrowButton != null && arrowButton instanceof AquaComboBoxButton) {
550
final AquaComboBoxButton button = (AquaComboBoxButton)arrowButton;
551
final Insets buttonInsets = button.getInsets();
552
// Insets insets = comboBox.getInsets();
553
final Insets insets = new Insets(0, 5, 0, 25);//comboBox.getInsets();
554
555
size = getDisplaySize();
556
size.width += insets.left + insets.right;
557
size.width += buttonInsets.left + buttonInsets.right;
558
size.width += buttonInsets.right + 10;
559
size.height += insets.top + insets.bottom;
560
size.height += buttonInsets.top + buttonInsets.bottom;
561
// Min height = Height of arrow button plus 2 pixels fuzz above plus 2 below. 23 + 2 + 2
562
size.height = Math.max(27, size.height);
563
} else if (editable && arrowButton != null && editor != null) {
564
size = super.getMinimumSize(c);
565
final Insets margin = arrowButton.getMargin();
566
size.height += margin.top + margin.bottom;
567
} else {
568
size = super.getMinimumSize(c);
569
}
570
571
final Border border = c.getBorder();
572
if (border != null) {
573
final Insets insets = border.getBorderInsets(c);
574
size.height += insets.top + insets.bottom;
575
size.width += insets.left + insets.right;
576
}
577
578
cachedMinimumSize.setSize(size.width, size.height);
579
isMinimumSizeDirty = false;
580
581
return new Dimension(cachedMinimumSize);
582
}
583
584
@SuppressWarnings("unchecked")
585
static final RecyclableSingleton<ClientPropertyApplicator<JComboBox, AquaComboBoxUI>> APPLICATOR = new RecyclableSingleton<ClientPropertyApplicator<JComboBox, AquaComboBoxUI>>() {
586
@Override
587
protected ClientPropertyApplicator<JComboBox, AquaComboBoxUI> getInstance() {
588
return new ClientPropertyApplicator<JComboBox, AquaComboBoxUI>(
589
new Property<AquaComboBoxUI>(AquaFocusHandler.FRAME_ACTIVE_PROPERTY) {
590
public void applyProperty(final AquaComboBoxUI target, final Object value) {
591
if (Boolean.FALSE.equals(value)) {
592
if (target.comboBox != null) target.comboBox.hidePopup();
593
}
594
if (target.listBox != null) target.listBox.repaint();
595
}
596
},
597
new Property<AquaComboBoxUI>("editable") {
598
public void applyProperty(final AquaComboBoxUI target, final Object value) {
599
if (target.comboBox == null) return;
600
target.comboBox.repaint();
601
}
602
},
603
new Property<AquaComboBoxUI>("background") {
604
public void applyProperty(final AquaComboBoxUI target, final Object value) {
605
final Color color = (Color)value;
606
if (target.arrowButton != null) target.arrowButton.setBackground(color);
607
if (target.listBox != null) target.listBox.setBackground(color);
608
}
609
},
610
new Property<AquaComboBoxUI>("foreground") {
611
public void applyProperty(final AquaComboBoxUI target, final Object value) {
612
final Color color = (Color)value;
613
if (target.arrowButton != null) target.arrowButton.setForeground(color);
614
if (target.listBox != null) target.listBox.setForeground(color);
615
}
616
},
617
new Property<AquaComboBoxUI>(POPDOWN_CLIENT_PROPERTY_KEY) {
618
public void applyProperty(final AquaComboBoxUI target, final Object value) {
619
if (!(target.arrowButton instanceof AquaComboBoxButton)) return;
620
((AquaComboBoxButton)target.arrowButton).setIsPopDown(Boolean.TRUE.equals(value));
621
}
622
},
623
new Property<AquaComboBoxUI>(ISSQUARE_CLIENT_PROPERTY_KEY) {
624
public void applyProperty(final AquaComboBoxUI target, final Object value) {
625
if (!(target.arrowButton instanceof AquaComboBoxButton)) return;
626
((AquaComboBoxButton)target.arrowButton).setIsSquare(Boolean.TRUE.equals(value));
627
}
628
}
629
) {
630
public AquaComboBoxUI convertJComponentToTarget(final JComboBox combo) {
631
final ComboBoxUI comboUI = combo.getUI();
632
if (comboUI instanceof AquaComboBoxUI) return (AquaComboBoxUI)comboUI;
633
return null;
634
}
635
};
636
}
637
};
638
static ClientPropertyApplicator<JComboBox, AquaComboBoxUI> getApplicator() {
639
return APPLICATOR.get();
640
}
641
}
642
643