43 |
import java.awt.Container; |
import java.awt.Container; |
44 |
import java.awt.Dimension; |
import java.awt.Dimension; |
45 |
import java.awt.Font; |
import java.awt.Font; |
46 |
|
import java.awt.FontMetrics; |
47 |
import java.awt.Graphics; |
import java.awt.Graphics; |
48 |
import java.awt.Insets; |
import java.awt.Insets; |
49 |
import java.awt.LayoutManager; |
import java.awt.LayoutManager; |
71 |
import javax.swing.JComponent; |
import javax.swing.JComponent; |
72 |
import javax.swing.JList; |
import javax.swing.JList; |
73 |
import javax.swing.ListCellRenderer; |
import javax.swing.ListCellRenderer; |
74 |
|
import javax.swing.LookAndFeel; |
75 |
import javax.swing.SwingUtilities; |
import javax.swing.SwingUtilities; |
|
import javax.swing.UIDefaults; |
|
76 |
import javax.swing.UIManager; |
import javax.swing.UIManager; |
77 |
import javax.swing.event.ListDataEvent; |
import javax.swing.event.ListDataEvent; |
78 |
import javax.swing.event.ListDataListener; |
import javax.swing.event.ListDataListener; |
158 |
*/ |
*/ |
159 |
protected PropertyChangeListener propertyChangeListener; |
protected PropertyChangeListener propertyChangeListener; |
160 |
|
|
161 |
|
/** |
162 |
|
* The button background. |
163 |
|
* @see #installDefaults() |
164 |
|
*/ |
165 |
|
private Color buttonBackground; |
166 |
|
|
167 |
|
/** |
168 |
|
* The button shadow. |
169 |
|
* @see #installDefaults() |
170 |
|
*/ |
171 |
|
private Color buttonShadow; |
172 |
|
|
173 |
/** |
/** |
174 |
* Colors that are used to render selected item in the combo box. |
* The button dark shadow. |
175 |
|
* @see #installDefaults() |
176 |
|
*/ |
177 |
|
private Color buttonDarkShadow; |
178 |
|
|
179 |
|
/** |
180 |
|
* The button highlight. |
181 |
|
* @see #installDefaults() |
182 |
*/ |
*/ |
183 |
private Color shadow; |
private Color buttonHighlight; |
|
private Color darkShadow; |
|
|
private Color highlight; |
|
184 |
|
|
185 |
/* Size of the largest item in the comboBox |
/* Size of the largest item in the comboBox |
186 |
* This is package-private to avoid an accessor method. |
* This is package-private to avoid an accessor method. |
197 |
*/ |
*/ |
198 |
public BasicComboBoxUI() |
public BasicComboBoxUI() |
199 |
{ |
{ |
200 |
|
// Nothing to do here. |
201 |
} |
} |
202 |
|
|
203 |
/** |
/** |
226 |
|
|
227 |
if (c instanceof JComboBox) |
if (c instanceof JComboBox) |
228 |
{ |
{ |
229 |
comboBox = (JComboBox) c; |
comboBox = (JComboBox) c; |
230 |
comboBox.setOpaque(true); |
comboBox.setOpaque(true); |
231 |
comboBox.setLayout(createLayoutManager()); |
comboBox.setLayout(createLayoutManager()); |
232 |
installDefaults(); |
installDefaults(); |
233 |
installComponents(); |
installComponents(); |
234 |
installListeners(); |
installListeners(); |
235 |
installKeyboardActions(); |
installKeyboardActions(); |
236 |
} |
} |
237 |
} |
} |
238 |
|
|
260 |
*/ |
*/ |
261 |
protected void installDefaults() |
protected void installDefaults() |
262 |
{ |
{ |
263 |
UIDefaults defaults = UIManager.getLookAndFeelDefaults(); |
LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background", |
264 |
|
"ComboBox.foreground", "ComboBox.font"); |
|
if (comboBox.getFont() instanceof UIResource) |
|
|
comboBox.setFont(defaults.getFont("ComboBox.font")); |
|
265 |
|
|
|
if (comboBox.getForeground() instanceof UIResource) |
|
|
comboBox.setForeground(defaults.getColor("ComboBox.foreground")); |
|
|
|
|
|
if (comboBox.getBackground() instanceof UIResource) |
|
|
comboBox.setBackground(defaults.getColor("ComboBox.background")); |
|
|
|
|
266 |
// fetch the button color scheme |
// fetch the button color scheme |
267 |
shadow = defaults.getColor("ComboBox.buttonShadow"); |
buttonBackground = UIManager.getColor("ComboBox.buttonBackground"); |
268 |
darkShadow = defaults.getColor("ComboBox.buttonDarkShadow"); |
buttonShadow = UIManager.getColor("ComboBox.buttonShadow"); |
269 |
highlight = defaults.getColor("ComboBox.buttonHighlight"); |
buttonDarkShadow = UIManager.getColor("ComboBox.buttonDarkShadow"); |
270 |
|
buttonHighlight = UIManager.getColor("ComboBox.buttonHighlight"); |
271 |
} |
} |
272 |
|
|
273 |
/** |
/** |
315 |
if (comboBox.getBackground() instanceof UIResource) |
if (comboBox.getBackground() instanceof UIResource) |
316 |
comboBox.setBackground(null); |
comboBox.setBackground(null); |
317 |
|
|
318 |
shadow = null; |
buttonBackground = null; |
319 |
darkShadow = null; |
buttonShadow = null; |
320 |
highlight = null; |
buttonDarkShadow = null; |
321 |
|
buttonHighlight = null; |
322 |
} |
} |
323 |
|
|
324 |
/** |
/** |
453 |
*/ |
*/ |
454 |
protected ComboBoxEditor createEditor() |
protected ComboBoxEditor createEditor() |
455 |
{ |
{ |
456 |
return new BasicComboBoxEditor(); |
return new BasicComboBoxEditor.UIResource(); |
457 |
} |
} |
458 |
|
|
459 |
/** |
/** |
463 |
*/ |
*/ |
464 |
protected void installComponents() |
protected void installComponents() |
465 |
{ |
{ |
466 |
// create and install arrow button |
// create drop down list of items |
467 |
arrowButton = createArrowButton(); |
popup = createPopup(); |
468 |
configureArrowButton(); |
listBox = popup.getList(); |
|
comboBox.add(arrowButton); |
|
|
|
|
|
// Set list that will be used by BasicComboBoxRender |
|
|
// in order to determine the right colors when rendering |
|
|
listBox = new JList(); |
|
469 |
|
|
470 |
// set editor and renderer for the combo box. Editor is used |
// set editor and renderer for the combo box. Editor is used |
471 |
// only if combo box becomes editable, otherwise renderer is used |
// only if combo box becomes editable, otherwise renderer is used |
472 |
// to paint the selected item; combobox is not editable by default. |
// to paint the selected item; combobox is not editable by default. |
473 |
comboBox.setRenderer(createRenderer()); |
comboBox.setRenderer(createRenderer()); |
474 |
|
|
475 |
comboBox.setEditor(createEditor()); |
// create and install arrow button |
476 |
editor = comboBox.getEditor().getEditorComponent(); |
arrowButton = createArrowButton(); |
477 |
|
configureArrowButton(); |
478 |
|
comboBox.add(arrowButton); |
479 |
|
|
480 |
// create drop down list of items |
ComboBoxEditor currentEditor = comboBox.getEditor(); |
481 |
popup = createPopup(); |
if (currentEditor == null || currentEditor instanceof UIResource) |
482 |
|
{ |
483 |
|
comboBox.setEditor(createEditor()); |
484 |
|
editor = comboBox.getEditor().getEditorComponent(); |
485 |
|
} |
486 |
|
|
487 |
comboBox.revalidate(); |
comboBox.revalidate(); |
488 |
} |
} |
504 |
|
|
505 |
comboBox.setRenderer(null); |
comboBox.setRenderer(null); |
506 |
|
|
507 |
comboBox.setEditor(null); |
// if the editor is not an instanceof UIResource, it was not set by the |
508 |
editor = null; |
// UI delegate, so don't clear it... |
509 |
|
ComboBoxEditor currentEditor = comboBox.getEditor(); |
510 |
|
if (currentEditor instanceof UIResource) |
511 |
|
{ |
512 |
|
comboBox.setEditor(null); |
513 |
|
editor = null; |
514 |
|
} |
515 |
} |
} |
516 |
|
|
517 |
/** |
/** |
557 |
{ |
{ |
558 |
arrowButton.setEnabled(comboBox.isEnabled()); |
arrowButton.setEnabled(comboBox.isEnabled()); |
559 |
arrowButton.setFont(comboBox.getFont()); |
arrowButton.setFont(comboBox.getFont()); |
|
arrowButton.setMargin(new Insets(0, 0, 0, 0)); |
|
560 |
} |
} |
561 |
|
|
562 |
/** |
/** |
563 |
* Unconfigures the arrow button. |
* Unconfigures the arrow button. |
564 |
* |
* |
565 |
* @see #configureArrowButton() |
* @see #configureArrowButton() |
566 |
|
* |
567 |
|
* @specnote The specification says this method is implementation specific |
568 |
|
* and should not be used or overridden. |
569 |
*/ |
*/ |
570 |
public void unconfigureArrowButton() |
public void unconfigureArrowButton() |
571 |
{ |
{ |
572 |
|
// Nothing to do here yet. |
573 |
} |
} |
574 |
|
|
575 |
/** |
/** |
581 |
*/ |
*/ |
582 |
protected JButton createArrowButton() |
protected JButton createArrowButton() |
583 |
{ |
{ |
584 |
return new BasicArrowButton(BasicArrowButton.SOUTH); |
return new BasicArrowButton(BasicArrowButton.SOUTH, buttonBackground, |
585 |
|
buttonShadow, buttonDarkShadow, buttonHighlight); |
586 |
} |
} |
587 |
|
|
588 |
/** |
/** |
651 |
*/ |
*/ |
652 |
public Dimension getPreferredSize(JComponent c) |
public Dimension getPreferredSize(JComponent c) |
653 |
{ |
{ |
654 |
|
// note: overriding getMinimumSize() (for example in the MetalComboBoxUI |
655 |
|
// class) affects the getPreferredSize() result, so it seems logical that |
656 |
|
// this method is implemented by delegating to the getMinimumSize() method |
657 |
return getMinimumSize(c); |
return getMinimumSize(c); |
658 |
} |
} |
659 |
|
|
668 |
public Dimension getMinimumSize(JComponent c) |
public Dimension getMinimumSize(JComponent c) |
669 |
{ |
{ |
670 |
Dimension d = getDisplaySize(); |
Dimension d = getDisplaySize(); |
671 |
Dimension arrowDim = arrowButton.getPreferredSize(); |
int arrowButtonWidth = d.height; |
672 |
Dimension result = new Dimension(d.width + arrowDim.width, |
Dimension result = new Dimension(d.width + arrowButtonWidth, d.height); |
|
Math.max(d.height, arrowDim.height)); |
|
673 |
return result; |
return result; |
674 |
} |
} |
675 |
|
|
682 |
* |
* |
683 |
* @param c The {@link JComponent} to find the maximum size for |
* @param c The {@link JComponent} to find the maximum size for |
684 |
* |
* |
685 |
* @return The dimensions of the minimum size. |
* @return The maximum size (<code>Dimension(32767, 32767)</code>). |
686 |
*/ |
*/ |
687 |
public Dimension getMaximumSize(JComponent c) |
public Dimension getMaximumSize(JComponent c) |
688 |
{ |
{ |
830 |
} |
} |
831 |
|
|
832 |
/** |
/** |
833 |
* Returns default size for the combo box that doesn't contain any elements |
* Returns the default size for the display area of a combo box that does |
834 |
* in it |
* not contain any elements. This method returns the width and height of |
835 |
|
* a single space in the current font, plus a margin of 1 pixel. |
836 |
* |
* |
837 |
* @return Default size of the combo box with no elements in it. |
* @return The default display size. |
838 |
|
* |
839 |
|
* @see #getDisplaySize() |
840 |
*/ |
*/ |
841 |
protected Dimension getDefaultSize() |
protected Dimension getDefaultSize() |
842 |
{ |
{ |
843 |
// FIXME: Not implemented properly. |
// There is nothing in the spec to say how this method should be |
844 |
return new Dimension(100, 5); |
// implemented...so I've done some guessing, written some Mauve tests, |
845 |
|
// and written something that gives dimensions that are close to the |
846 |
|
// reference implementation. |
847 |
|
FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); |
848 |
|
int w = fm.charWidth(' ') + 2; |
849 |
|
int h = fm.getHeight() + 2; |
850 |
|
return new Dimension(w, h); |
851 |
} |
} |
852 |
|
|
853 |
/** |
/** |
858 |
*/ |
*/ |
859 |
protected Dimension getDisplaySize() |
protected Dimension getDisplaySize() |
860 |
{ |
{ |
861 |
ComboBoxModel model = comboBox.getModel(); |
Object prototype = comboBox.getPrototypeDisplayValue(); |
862 |
int numItems = model.getSize(); |
if (prototype != null) |
|
|
|
|
// if combo box doesn't have any items then simply |
|
|
// return its default size |
|
|
if (numItems == 0) |
|
863 |
{ |
{ |
864 |
displaySize = getDefaultSize(); |
// calculate result based on prototype |
865 |
return displaySize; |
ListCellRenderer renderer = comboBox.getRenderer(); |
866 |
|
Component comp = renderer.getListCellRendererComponent(listBox, |
867 |
|
prototype, -1, false, false); |
868 |
|
Dimension compSize = comp.getPreferredSize(); |
869 |
|
compSize.width += 2; // add 1 pixel margin around area |
870 |
|
compSize.height += 2; |
871 |
|
return compSize; |
872 |
} |
} |
873 |
|
else |
874 |
|
{ |
875 |
|
ComboBoxModel model = comboBox.getModel(); |
876 |
|
int numItems = model.getSize(); |
877 |
|
|
878 |
|
// if combo box doesn't have any items then simply |
879 |
|
// return its default size |
880 |
|
if (numItems == 0) |
881 |
|
{ |
882 |
|
displaySize = getDefaultSize(); |
883 |
|
return displaySize; |
884 |
|
} |
885 |
|
|
886 |
Dimension size = new Dimension(0, 0); |
Dimension size = new Dimension(0, 0); |
887 |
|
|
888 |
// ComboBox's display size should be equal to the |
// ComboBox's display size should be equal to the |
889 |
// size of the largest item in the combo box. |
// size of the largest item in the combo box. |
890 |
ListCellRenderer renderer = comboBox.getRenderer(); |
ListCellRenderer renderer = comboBox.getRenderer(); |
|
|
|
|
// FIXME: use the JComboBox.getPrototypeDisplayValue() if there is |
|
|
// one |
|
|
for (int i = 0; i < numItems; i++) |
|
|
{ |
|
|
Object item = model.getElementAt(i); |
|
|
String s = item.toString(); |
|
|
Component comp = renderer.getListCellRendererComponent(listBox, item, |
|
|
-1, false, false); |
|
891 |
|
|
892 |
Dimension compSize = comp.getPreferredSize(); |
for (int i = 0; i < numItems; i++) |
893 |
if (compSize.width > size.width) |
{ |
894 |
size.width = compSize.width; |
Object item = model.getElementAt(i); |
895 |
if (compSize.height > size.height) |
Component comp = renderer.getListCellRendererComponent(listBox, |
896 |
size.height = compSize.height; |
item, -1, false, false); |
897 |
|
|
898 |
|
Dimension compSize = comp.getPreferredSize(); |
899 |
|
if (compSize.width + 2 > size.width) |
900 |
|
size.width = compSize.width + 2; |
901 |
|
if (compSize.height + 2 > size.height) |
902 |
|
size.height = compSize.height + 2; |
903 |
|
} |
904 |
|
displaySize = size; |
905 |
|
return displaySize; |
906 |
} |
} |
|
displaySize = size; |
|
|
return displaySize; |
|
907 |
} |
} |
908 |
|
|
909 |
/** |
/** |
937 |
*/ |
*/ |
938 |
public ComboBoxLayoutManager() |
public ComboBoxLayoutManager() |
939 |
{ |
{ |
940 |
|
// Nothing to do here. |
941 |
} |
} |
942 |
|
|
943 |
/** |
/** |
1024 |
*/ |
*/ |
1025 |
public FocusHandler() |
public FocusHandler() |
1026 |
{ |
{ |
1027 |
|
// Nothing to do here. |
1028 |
} |
} |
1029 |
|
|
1030 |
/** |
/** |
1048 |
public void focusLost(FocusEvent e) |
public void focusLost(FocusEvent e) |
1049 |
{ |
{ |
1050 |
hasFocus = false; |
hasFocus = false; |
1051 |
|
setPopupVisible(comboBox, false); |
1052 |
comboBox.repaint(); |
comboBox.repaint(); |
1053 |
} |
} |
1054 |
} |
} |
1064 |
*/ |
*/ |
1065 |
public ItemHandler() |
public ItemHandler() |
1066 |
{ |
{ |
1067 |
|
// Nothing to do here. |
1068 |
} |
} |
1069 |
|
|
1070 |
/** |
/** |
1075 |
*/ |
*/ |
1076 |
public void itemStateChanged(ItemEvent e) |
public void itemStateChanged(ItemEvent e) |
1077 |
{ |
{ |
1078 |
|
if (e.getStateChange() == ItemEvent.SELECTED && comboBox.isEditable()) |
1079 |
|
comboBox.getEditor().setItem(e.getItem()); |
1080 |
comboBox.repaint(); |
comboBox.repaint(); |
1081 |
} |
} |
1082 |
} |
} |
1088 |
{ |
{ |
1089 |
public KeyHandler() |
public KeyHandler() |
1090 |
{ |
{ |
1091 |
|
// Nothing to do here. |
1092 |
} |
} |
1093 |
|
|
1094 |
/** |
/** |
1111 |
*/ |
*/ |
1112 |
public ListDataHandler() |
public ListDataHandler() |
1113 |
{ |
{ |
1114 |
|
// Nothing to do here. |
1115 |
} |
} |
1116 |
|
|
1117 |
/** |
/** |
1169 |
*/ |
*/ |
1170 |
public PropertyChangeHandler() |
public PropertyChangeHandler() |
1171 |
{ |
{ |
1172 |
|
// Nothing to do here. |
1173 |
} |
} |
1174 |
|
|
1175 |
/** |
/** |