1. /*
  2. * @(#)MetalComboBoxUI.java 1.26 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.metal;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import javax.swing.*;
  11. import javax.swing.plaf.*;
  12. import javax.swing.border.*;
  13. import javax.swing.plaf.basic.*;
  14. import java.io.Serializable;
  15. import java.beans.*;
  16. /**
  17. * Metal UI for JComboBox
  18. * <p>
  19. * <strong>Warning:</strong>
  20. * Serialized objects of this class will not be compatible with
  21. * future Swing releases. The current serialization support is appropriate
  22. * for short term storage or RMI between applications running the same
  23. * version of Swing. A future release of Swing will provide support for
  24. * long term persistence.
  25. *
  26. * @see MetalComboBoxListCellRenderer
  27. * @see MetalPopupMenuBorder
  28. * @version 1.17 07/09/98
  29. * @author Tom Santos
  30. */
  31. public class MetalComboBoxUI extends BasicComboBoxUI {
  32. FocusListener focusDelegator;
  33. public static ComponentUI createUI(JComponent c) {
  34. return new MetalComboBoxUI();
  35. }
  36. public void installUI(JComponent c) {
  37. super.installUI(c);
  38. comboBox.setRequestFocusEnabled( true );
  39. }
  40. public void uninstallUI( JComponent c ) {
  41. super.uninstallUI( c );
  42. }
  43. public void paint(Graphics g, JComponent c) {
  44. }
  45. protected ComboBoxEditor createEditor() {
  46. return new MetalComboBoxEditor.UIResource();
  47. }
  48. protected ComboPopup createPopup() {
  49. return new MetalComboPopup( comboBox );
  50. }
  51. protected JButton createArrowButton() {
  52. JButton button = new MetalComboBoxButton( comboBox,
  53. new MetalComboBoxIcon(),
  54. comboBox.isEditable() ? true : false,
  55. currentValuePane,
  56. listBox );
  57. button.setMargin( new Insets( 0, 1, 1, 3 ) );
  58. return button;
  59. }
  60. FocusListener createFocusDelegator() {
  61. return new FocusDelegator();
  62. }
  63. class FocusDelegator extends FocusAdapter {
  64. public void focusGained( FocusEvent e ) {
  65. if ( metalGetComboBox().isEditable() ) {
  66. metalGetEditor().requestFocus();
  67. }
  68. else {
  69. metalGetArrowButton().requestFocus();
  70. }
  71. }
  72. }
  73. public PropertyChangeListener createPropertyChangeListener() {
  74. return new MetalPropertyChangeListener();
  75. }
  76. /**
  77. * This inner class is marked "public" due to a compiler bug.
  78. * This class should be treated as a "protected" inner class.
  79. * Instantiate it only within subclasses of <FooUI>.
  80. */
  81. public class MetalPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler {
  82. public void propertyChange(PropertyChangeEvent e) {
  83. super.propertyChange( e );
  84. metalGetComboBox().setRequestFocusEnabled( true );
  85. String propertyName = e.getPropertyName();
  86. if ( propertyName.equals( "editable" ) ) {
  87. editablePropertyChanged( e );
  88. }
  89. else if ( propertyName.equals( "enabled" ) ) {
  90. enabledPropertyChanged( e );
  91. }
  92. }
  93. }
  94. protected void editablePropertyChanged( PropertyChangeEvent e ) {
  95. if ( arrowButton instanceof MetalComboBoxButton ) {
  96. MetalComboBoxButton button = (MetalComboBoxButton)arrowButton;
  97. button.setIconOnly( comboBox.isEditable() );
  98. button.setRequestFocusEnabled( (!comboBox.isEditable()) && comboBox.isEnabled() );
  99. comboBox.repaint();
  100. }
  101. }
  102. void enabledPropertyChanged( PropertyChangeEvent e ) {
  103. if ( arrowButton instanceof MetalComboBoxButton ) {
  104. arrowButton.setRequestFocusEnabled( (!comboBox.isEditable()) && comboBox.isEnabled() );
  105. comboBox.repaint();
  106. }
  107. }
  108. protected LayoutManager createLayoutManager() {
  109. return new MetalComboBoxLayoutManager();
  110. }
  111. /**
  112. * This inner class is marked "public" due to a compiler bug.
  113. * This class should be treated as a "protected" inner class.
  114. * Instantiate it only within subclasses of <FooUI>.
  115. */
  116. public class MetalComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager {
  117. public void layoutContainer( Container parent ) {
  118. layoutComboBox( parent, this );
  119. }
  120. public void superLayout( Container parent ) {
  121. super.layoutContainer( parent );
  122. }
  123. }
  124. // This is here because of a bug in the compiler. When a protected-inner-class-savvy compiler comes out we
  125. // should move this into MetalComboBoxLayoutManager.
  126. public void layoutComboBox( Container parent, MetalComboBoxLayoutManager manager ) {
  127. if ( comboBox.isEditable() ) {
  128. manager.superLayout( parent );
  129. }
  130. else {
  131. if ( arrowButton != null ) {
  132. Insets insets = comboBox.getInsets();
  133. int width = comboBox.getWidth();
  134. int height = comboBox.getHeight();
  135. arrowButton.setBounds( insets.left, insets.top,
  136. width - (insets.left + insets.right),
  137. height - (insets.top + insets.bottom) );
  138. }
  139. }
  140. }
  141. public boolean isFocusTraversable( JComboBox c ) {
  142. return false;
  143. }
  144. protected void installListeners() {
  145. if ( (itemListener = createItemListener()) != null ) {
  146. comboBox.addItemListener( itemListener );
  147. }
  148. if ( (propertyChangeListener = createPropertyChangeListener()) != null ) {
  149. comboBox.addPropertyChangeListener( propertyChangeListener );
  150. }
  151. keyListener = createKeyListener();
  152. focusListener = createFocusListener();
  153. popupKeyListener = popup.getKeyListener();
  154. popupMouseListener = popup.getMouseListener();
  155. popupMouseMotionListener = popup.getMouseMotionListener();
  156. if ( comboBox.getModel() != null ) {
  157. if ( (listDataListener = createListDataListener()) != null ) {
  158. comboBox.getModel().addListDataListener( listDataListener );
  159. }
  160. }
  161. if ( (focusDelegator = createFocusDelegator()) != null ) {
  162. comboBox.addFocusListener( focusDelegator );
  163. }
  164. }
  165. protected void uninstallListeners() {
  166. if ( itemListener != null ) {
  167. comboBox.removeItemListener( itemListener );
  168. }
  169. if ( propertyChangeListener != null ) {
  170. comboBox.removePropertyChangeListener( propertyChangeListener );
  171. }
  172. if ( comboBox.getModel() != null ) {
  173. if ( listDataListener != null ) {
  174. comboBox.getModel().removeListDataListener( listDataListener );
  175. }
  176. }
  177. if ( focusDelegator != null ) {
  178. comboBox.removeFocusListener( focusDelegator );
  179. }
  180. }
  181. protected void removeListeners() {
  182. if ( itemListener != null ) {
  183. comboBox.removeItemListener( itemListener );
  184. }
  185. if ( propertyChangeListener != null ) {
  186. comboBox.removePropertyChangeListener( propertyChangeListener );
  187. }
  188. }
  189. public void configureEditor() {
  190. super.configureEditor();
  191. if ( popupKeyListener != null ) {
  192. editor.removeKeyListener( popupKeyListener );
  193. }
  194. if ( focusListener != null ) {
  195. editor.addFocusListener( focusListener );
  196. }
  197. }
  198. public void unconfigureEditor() {
  199. super.unconfigureEditor();
  200. if ( focusListener != null ) {
  201. editor.removeFocusListener( focusListener );
  202. }
  203. }
  204. public void configureArrowButton() {
  205. if ( arrowButton != null ) {
  206. arrowButton.setRequestFocusEnabled( (!comboBox.isEditable()) && comboBox.isEnabled() );
  207. if ( keyListener != null ) {
  208. arrowButton.addKeyListener( keyListener );
  209. }
  210. if ( popupKeyListener != null ) {
  211. arrowButton.addKeyListener( popupKeyListener );
  212. }
  213. if ( focusListener != null ) {
  214. arrowButton.addFocusListener( focusListener );
  215. }
  216. if ( popupMouseListener != null ) {
  217. arrowButton.addMouseListener( popupMouseListener );
  218. }
  219. if ( popupMouseMotionListener != null ) {
  220. arrowButton.addMouseMotionListener( popupMouseMotionListener );
  221. }
  222. }
  223. }
  224. public void unconfigureArrowButton() {
  225. if ( arrowButton != null ) {
  226. super.unconfigureArrowButton();
  227. if ( keyListener != null ) {
  228. arrowButton.removeKeyListener( keyListener );
  229. }
  230. if ( popupKeyListener != null ) {
  231. arrowButton.removeKeyListener( popupKeyListener );
  232. }
  233. if ( focusListener != null ) {
  234. arrowButton.removeFocusListener( focusListener );
  235. }
  236. }
  237. }
  238. public Dimension getMinimumSize( JComponent c ) {
  239. if ( !isMinimumSizeDirty ) {
  240. return new Dimension( cachedMinimumSize );
  241. }
  242. Dimension size = null;
  243. if ( !comboBox.isEditable() &&
  244. arrowButton != null &&
  245. arrowButton instanceof MetalComboBoxButton ) {
  246. MetalComboBoxButton button = (MetalComboBoxButton)arrowButton;
  247. Insets buttonInsets = button.getInsets();
  248. Insets insets = comboBox.getInsets();
  249. size = getDisplaySize();
  250. size.width += insets.left + insets.right;
  251. size.width += buttonInsets.left + buttonInsets.right;
  252. size.width += buttonInsets.right + button.getComboIcon().getIconWidth();
  253. size.height += insets.top + insets.bottom;
  254. size.height += buttonInsets.top + buttonInsets.bottom;
  255. }
  256. else if ( comboBox.isEditable() &&
  257. arrowButton != null &&
  258. editor != null ) {
  259. size = super.getMinimumSize( c );
  260. Insets margin = arrowButton.getMargin();
  261. Insets insets = comboBox.getInsets();
  262. if ( editor instanceof JComponent ) {
  263. Insets editorInsets = ((JComponent)editor).getInsets();
  264. size.height += editorInsets.top + editorInsets.bottom;
  265. }
  266. size.height += margin.top + margin.bottom;
  267. size.height += insets.top + insets.bottom;
  268. }
  269. else {
  270. size = super.getMinimumSize( c );
  271. }
  272. cachedMinimumSize.setSize( size.width, size.height );
  273. isMinimumSizeDirty = false;
  274. return new Dimension( cachedMinimumSize );
  275. }
  276. protected void selectNextPossibleValue() { super.selectNextPossibleValue();}
  277. protected void selectPreviousPossibleValue() { super.selectPreviousPossibleValue();}
  278. /**
  279. * This method is here as a workaround for a bug in the javac compiler.
  280. */
  281. JComboBox metalGetComboBox() {
  282. return comboBox;
  283. }
  284. /**
  285. * This method is here as a workaround for a bug in the javac compiler.
  286. */
  287. JButton getArrowButton() {
  288. return arrowButton;
  289. }
  290. boolean isPopupVisible() {
  291. return super.isPopupVisible( comboBox );
  292. }
  293. void togglePopup() {
  294. toggleOpenClose();
  295. }
  296. protected void installKeyboardActions() {
  297. super.installKeyboardActions();
  298. ActionListener downAction = new ActionListener() {
  299. public void actionPerformed(ActionEvent e) {
  300. if ( metalGetComboBox().isEnabled() ) {
  301. if ( isPopupVisible() ) {
  302. selectNextPossibleValue();
  303. }
  304. else {
  305. setPopupVisible( metalGetComboBox(), true );
  306. }
  307. }
  308. }
  309. };
  310. metalGetComboBox().registerKeyboardAction( downAction,
  311. KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),
  312. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  313. metalGetComboBox().registerKeyboardAction( downAction,
  314. KeyStroke.getKeyStroke("KP_DOWN"),
  315. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  316. ActionListener altAction = new ActionListener() {
  317. public void actionPerformed(ActionEvent e) {
  318. if ( metalGetComboBox().isEnabled() && isPopupVisible() ) {
  319. togglePopup();
  320. }
  321. }
  322. };
  323. metalGetComboBox().registerKeyboardAction( altAction,
  324. KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.ALT_MASK),
  325. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  326. metalGetComboBox().registerKeyboardAction( altAction,
  327. KeyStroke.getKeyStroke("alt KP_DOWN"),
  328. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  329. metalGetComboBox().registerKeyboardAction( altAction,
  330. KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.ALT_MASK),
  331. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  332. metalGetComboBox().registerKeyboardAction( altAction,
  333. KeyStroke.getKeyStroke("alt KP_UP"),
  334. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  335. ActionListener upAction = new ActionListener() {
  336. public void actionPerformed(ActionEvent e) {
  337. if ( metalGetComboBox().isEnabled() && isPopupVisible() ) {
  338. selectPreviousPossibleValue();
  339. }
  340. }
  341. };
  342. metalGetComboBox().registerKeyboardAction( upAction,
  343. KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),
  344. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  345. metalGetComboBox().registerKeyboardAction( upAction,
  346. KeyStroke.getKeyStroke("KP_UP"),
  347. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  348. }
  349. protected void uninstallKeyboardActions() {
  350. super.uninstallKeyboardActions();
  351. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0));
  352. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke("KP_DOWN"));
  353. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.ALT_MASK));
  354. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke("alt KP_DOWN"));
  355. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0));
  356. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke("KP_UP"));
  357. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.ALT_MASK));
  358. comboBox.unregisterKeyboardAction(KeyStroke.getKeyStroke("alt KP_UP"));
  359. }
  360. Component metalGetEditor() {
  361. return editor;
  362. }
  363. JButton metalGetArrowButton() {
  364. return arrowButton;
  365. }
  366. /**
  367. * This inner class is marked "public" due to a compiler bug.
  368. * This class should be treated as a "protected" inner class.
  369. * Instantiate it only within subclasses of <FooUI>.
  370. */
  371. public class MetalComboPopup extends BasicComboPopup {
  372. public MetalComboPopup( JComboBox cBox ) {
  373. super( cBox );
  374. }
  375. public void delegateFocus( MouseEvent e ) {
  376. if ( metalGetComboBox().isEditable() ) {
  377. metalGetEditor().requestFocus();
  378. }
  379. }
  380. }
  381. }