1. /*
  2. * @(#)BasicComboBoxUI.java 1.155 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import javax.swing.*;
  11. import javax.accessibility.*;
  12. import javax.swing.FocusManager;
  13. import javax.swing.plaf.*;
  14. import javax.swing.border.*;
  15. import javax.swing.text.*;
  16. import javax.swing.event.*;
  17. import java.beans.PropertyChangeListener;
  18. import java.beans.PropertyChangeEvent;
  19. import sun.awt.AppContext;
  20. /**
  21. * Basic UI implementation for JComboBox.
  22. * <p>
  23. * The combo box is a compound component which means that it is an agregate of
  24. * many simpler components. This class creates and manages the listeners
  25. * on the combo box and the combo box model. These listeners update the user
  26. * interface in response to changes in the properties and state of the combo box.
  27. * <p>
  28. * All event handling is handled by listener classes created with the
  29. * <code>createxxxListener()</code> methods and internal classes.
  30. * You can change the behavior of this class by overriding the
  31. * <code>createxxxListener()</code> methods and supplying your own
  32. * event listeners or subclassing from the ones supplied in this class.
  33. * <p>
  34. * For adding specific actions,
  35. * overide <code>installKeyboardActions</code> to add actions in response to
  36. * KeyStroke bindings. See the article <a href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">Keyboard Bindings in Swing</a>
  37. * at <a href="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
  38. *
  39. * @version 1.155 01/23/03
  40. * @author Arnaud Weber
  41. * @author Tom Santos
  42. * @author Mark Davidson
  43. */
  44. public class BasicComboBoxUI extends ComboBoxUI {
  45. protected JComboBox comboBox;
  46. /**
  47. * This protected field is implementation specific. Do not access directly
  48. * or override.
  49. */
  50. protected boolean hasFocus = false;
  51. // Control the selection behavior of the JComboBox when it is used
  52. // in the JTable DefaultCellEditor.
  53. private boolean isTableCellEditor = false;
  54. private static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
  55. // This list is for drawing the current item in the combo box.
  56. protected JList listBox;
  57. // Used to render the currently selected item in the combo box.
  58. // It doesn't have anything to do with the popup's rendering.
  59. protected CellRendererPane currentValuePane = new CellRendererPane();
  60. // The implementation of ComboPopup that is used to show the popup.
  61. protected ComboPopup popup;
  62. // The Component that the ComboBoxEditor uses for editing
  63. protected Component editor;
  64. // The arrow button that invokes the popup.
  65. protected JButton arrowButton;
  66. // Listeners that are attached to the JComboBox
  67. /**
  68. * This protected field is implementation specific. Do not access directly
  69. * or override. Override the listener construction method instead.
  70. *
  71. * @see #createKeyListener
  72. */
  73. protected KeyListener keyListener;
  74. /**
  75. * This protected field is implementation specific. Do not access directly
  76. * or override. Override the listener construction method instead.
  77. *
  78. * @see #createFocusListener
  79. */
  80. protected FocusListener focusListener;
  81. /**
  82. * This protected field is implementation specific. Do not access directly
  83. * or override. Override the listener construction method instead.
  84. *
  85. * @see #createPropertyChangeListener
  86. */
  87. protected PropertyChangeListener propertyChangeListener;
  88. private FocusListener editorFocusListener;
  89. private ActionListener editorActionListener;
  90. /**
  91. * This protected field is implementation specific. Do not access directly
  92. * or override. Override the listener construction method instead.
  93. *
  94. * @see #createItemListener
  95. */
  96. protected ItemListener itemListener;
  97. // Listeners that the ComboPopup produces.
  98. protected MouseListener popupMouseListener;
  99. protected MouseMotionListener popupMouseMotionListener;
  100. protected KeyListener popupKeyListener;
  101. // This is used for knowing when to cache the minimum preferred size.
  102. // If the data in the list changes, the cached value get marked for recalc.
  103. // Added to the current JComboBox model
  104. /**
  105. * This protected field is implementation specific. Do not access directly
  106. * or override. Override the listener construction method instead.
  107. *
  108. * @see #createListDataListener
  109. */
  110. protected ListDataListener listDataListener;
  111. // Flag for recalculating the minimum preferred size.
  112. protected boolean isMinimumSizeDirty = true;
  113. // Cached minimum preferred size.
  114. protected Dimension cachedMinimumSize = new Dimension( 0, 0 );
  115. // Flag for calculating the display size
  116. private boolean isDisplaySizeDirty = true;
  117. // Cached the size that the display needs to render the largest item
  118. private Dimension cachedDisplaySize = new Dimension( 0, 0 );
  119. // Key used for lookup of the DefaultListCellRenderer in the AppContext.
  120. private static final Object COMBO_UI_LIST_CELL_RENDERER_KEY =
  121. new StringBuffer("DefaultListCellRendererKey");
  122. // Used for calculating the default size.
  123. private static ListCellRenderer getDefaultListCellRenderer() {
  124. ListCellRenderer renderer = (ListCellRenderer)AppContext.
  125. getAppContext().get(COMBO_UI_LIST_CELL_RENDERER_KEY);
  126. if (renderer == null) {
  127. renderer = new DefaultListCellRenderer();
  128. AppContext.getAppContext().put(COMBO_UI_LIST_CELL_RENDERER_KEY,
  129. new DefaultListCellRenderer());
  130. }
  131. return renderer;
  132. }
  133. //========================
  134. // begin UI Initialization
  135. //
  136. public static ComponentUI createUI(JComponent c) {
  137. return new BasicComboBoxUI();
  138. }
  139. public void installUI( JComponent c ) {
  140. isMinimumSizeDirty = true;
  141. comboBox = (JComboBox)c;
  142. installDefaults();
  143. popup = createPopup();
  144. listBox = popup.getList();
  145. // Is this combo box a cell editor?
  146. Boolean inTable = (Boolean)c.getClientProperty(IS_TABLE_CELL_EDITOR );
  147. if (inTable != null) {
  148. isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
  149. }
  150. if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
  151. comboBox.setRenderer( createRenderer() );
  152. }
  153. if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
  154. comboBox.setEditor( createEditor() );
  155. }
  156. installListeners();
  157. installComponents();
  158. comboBox.setLayout( createLayoutManager() );
  159. comboBox.setRequestFocusEnabled( true );
  160. installKeyboardActions();
  161. }
  162. public void uninstallUI( JComponent c ) {
  163. setPopupVisible( comboBox, false);
  164. popup.uninstallingUI();
  165. uninstallKeyboardActions();
  166. comboBox.setLayout( null );
  167. uninstallComponents();
  168. uninstallListeners();
  169. uninstallDefaults();
  170. if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
  171. comboBox.setRenderer( null );
  172. }
  173. if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
  174. comboBox.setEditor( null );
  175. }
  176. keyListener = null;
  177. focusListener = null;
  178. listDataListener = null;
  179. propertyChangeListener = null;
  180. editorActionListener = null;
  181. editorFocusListener = null;
  182. popup = null;
  183. listBox = null;
  184. comboBox = null;
  185. }
  186. /**
  187. * Installs the default colors, default font, default renderer, and default
  188. * editor into the JComboBox.
  189. */
  190. protected void installDefaults() {
  191. LookAndFeel.installColorsAndFont( comboBox,
  192. "ComboBox.background",
  193. "ComboBox.foreground",
  194. "ComboBox.font" );
  195. LookAndFeel.installBorder( comboBox, "ComboBox.border" );
  196. }
  197. /**
  198. * Create and install the listeners for the combo box and its model.
  199. * This method is called when the UI is installed.
  200. */
  201. protected void installListeners() {
  202. if ( (itemListener = createItemListener()) != null) {
  203. comboBox.addItemListener( itemListener );
  204. }
  205. if ( (propertyChangeListener = createPropertyChangeListener()) != null ) {
  206. comboBox.addPropertyChangeListener( propertyChangeListener );
  207. }
  208. if ( (keyListener = createKeyListener()) != null ) {
  209. comboBox.addKeyListener( keyListener );
  210. }
  211. if ( (focusListener = createFocusListener()) != null ) {
  212. comboBox.addFocusListener( focusListener );
  213. }
  214. if ((popupMouseListener = popup.getMouseListener()) != null) {
  215. comboBox.addMouseListener( popupMouseListener );
  216. }
  217. if ((popupMouseMotionListener = popup.getMouseMotionListener()) != null) {
  218. comboBox.addMouseMotionListener( popupMouseMotionListener );
  219. }
  220. if ((popupKeyListener = popup.getKeyListener()) != null) {
  221. comboBox.addKeyListener(popupKeyListener);
  222. }
  223. if ( comboBox.getModel() != null ) {
  224. if ( (listDataListener = createListDataListener()) != null ) {
  225. comboBox.getModel().addListDataListener( listDataListener );
  226. }
  227. }
  228. }
  229. /**
  230. * Uninstalls the default colors, default font, default renderer, and default
  231. * editor into the JComboBox.
  232. */
  233. protected void uninstallDefaults() {
  234. LookAndFeel.installColorsAndFont( comboBox,
  235. "ComboBox.background",
  236. "ComboBox.foreground",
  237. "ComboBox.font" );
  238. LookAndFeel.uninstallBorder( comboBox );
  239. }
  240. /**
  241. * Remove the installed listeners from the combo box and its model.
  242. * The number and types of listeners removed and in this method should be
  243. * the same that was added in <code>installListeners</code>
  244. */
  245. protected void uninstallListeners() {
  246. if ( keyListener != null ) {
  247. comboBox.removeKeyListener( keyListener );
  248. }
  249. if ( itemListener != null) {
  250. comboBox.removeItemListener( itemListener );
  251. }
  252. if ( propertyChangeListener != null ) {
  253. comboBox.removePropertyChangeListener( propertyChangeListener );
  254. }
  255. if ( focusListener != null) {
  256. comboBox.removeFocusListener( focusListener );
  257. }
  258. if ( popupMouseListener != null) {
  259. comboBox.removeMouseListener( popupMouseListener );
  260. }
  261. if ( popupMouseMotionListener != null) {
  262. comboBox.removeMouseMotionListener( popupMouseMotionListener );
  263. }
  264. if (popupKeyListener != null) {
  265. comboBox.removeKeyListener(popupKeyListener);
  266. }
  267. if ( comboBox.getModel() != null ) {
  268. if ( listDataListener != null ) {
  269. comboBox.getModel().removeListDataListener( listDataListener );
  270. }
  271. }
  272. }
  273. /**
  274. * Creates the popup portion of the combo box.
  275. *
  276. * @return an instance of <code>ComboPopup</code>
  277. * @see ComboPopup
  278. */
  279. protected ComboPopup createPopup() {
  280. BasicComboPopup popup = new BasicComboPopup( comboBox );
  281. popup.getAccessibleContext().setAccessibleParent(comboBox);
  282. return popup;
  283. }
  284. /**
  285. * Creates a <code>KeyListener</code> which will be added to the
  286. * combo box. If this method returns null then it will not be added
  287. * to the combo box.
  288. *
  289. * @return an instance <code>KeyListener</code> or null
  290. */
  291. protected KeyListener createKeyListener() {
  292. return new KeyHandler();
  293. }
  294. /**
  295. * Creates a <code>FocusListener</code> which will be added to the combo box.
  296. * If this method returns null then it will not be added to the combo box.
  297. *
  298. * @return an instance of a <code>FocusListener</code> or null
  299. */
  300. protected FocusListener createFocusListener() {
  301. return new FocusHandler();
  302. }
  303. /**
  304. * Creates a list data listener which will be added to the
  305. * <code>ComboBoxModel</code>. If this method returns null then
  306. * it will not be added to the combo box model.
  307. *
  308. * @return an instance of a <code>ListDataListener</code> or null
  309. */
  310. protected ListDataListener createListDataListener() {
  311. return new ListDataHandler();
  312. }
  313. /**
  314. * Creates an <code>ItemListener</code> which will be added to the
  315. * combo box. If this method returns null then it will not
  316. * be added to the combo box.
  317. * <p>
  318. * Subclasses may override this method to return instances of their own
  319. * ItemEvent handlers.
  320. *
  321. * @return an instance of an <code>ItemListener</code> or null
  322. */
  323. protected ItemListener createItemListener() {
  324. return null;
  325. }
  326. /**
  327. * Creates a <code>PropertyChangeListener</code> which will be added to
  328. * the combo box. If this method returns null then it will not
  329. * be added to the combo box.
  330. *
  331. * @return an instance of a <code>PropertyChangeListener</code> or null
  332. */
  333. protected PropertyChangeListener createPropertyChangeListener() {
  334. return new PropertyChangeHandler();
  335. }
  336. /**
  337. * Creates a layout manager for managing the components which make up the
  338. * combo box.
  339. *
  340. * @return an instance of a layout manager
  341. */
  342. protected LayoutManager createLayoutManager() {
  343. return new ComboBoxLayoutManager();
  344. }
  345. /**
  346. * Creates the default renderer that will be used in a non-editiable combo
  347. * box. A default renderer will used only if a renderer has not been
  348. * explicitly set with <code>setRenderer</code>.
  349. *
  350. * @return a <code>ListCellRender</code> used for the combo box
  351. * @see javax.swing.JComboBox#setRenderer
  352. */
  353. protected ListCellRenderer createRenderer() {
  354. return new BasicComboBoxRenderer.UIResource();
  355. }
  356. /**
  357. * Creates the default editor that will be used in editable combo boxes.
  358. * A default editor will be used only if an editor has not been
  359. * explicitly set with <code>setEditor</code>.
  360. *
  361. * @return a <code>ComboBoxEditor</code> used for the combo box
  362. * @see javax.swing.JComboBox#setEditor
  363. */
  364. protected ComboBoxEditor createEditor() {
  365. return new BasicComboBoxEditor.UIResource();
  366. }
  367. //
  368. // end UI Initialization
  369. //======================
  370. //======================
  371. // begin Inner classes
  372. //
  373. /**
  374. * This listener checks to see if the key event isn't a navigation key. If
  375. * it finds a key event that wasn't a navigation key it dispatches it to
  376. * JComboBox.selectWithKeyChar() so that it can do type-ahead.
  377. *
  378. * This public inner class should be treated as protected.
  379. * Instantiate it only within subclasses of
  380. * <code>BasicComboBoxUI</code>.
  381. */
  382. public class KeyHandler extends KeyAdapter {
  383. public void keyPressed( KeyEvent e ) {
  384. if ( comboBox.isEnabled() &&
  385. !isNavigationKey( e.getKeyCode() ) &&
  386. isTypeAheadKey( e ) ) {
  387. if ( comboBox.selectWithKeyChar(e.getKeyChar()) ) {
  388. e.consume();
  389. }
  390. }
  391. }
  392. boolean isTypeAheadKey( KeyEvent e ) {
  393. return !e.isAltDown() && !e.isControlDown() && !e.isMetaDown();
  394. }
  395. }
  396. /**
  397. * This listener hides the popup when the focus is lost. It also repaints
  398. * when focus is gained or lost.
  399. *
  400. * This public inner class should be treated as protected.
  401. * Instantiate it only within subclasses of
  402. * <code>BasicComboBoxUI</code>.
  403. */
  404. public class FocusHandler implements FocusListener {
  405. public void focusGained( FocusEvent e ) {
  406. hasFocus = true;
  407. comboBox.repaint();
  408. if (comboBox.isEditable() && editor != null) {
  409. editor.requestFocus();
  410. }
  411. // Notify assistive technologies that the combo box
  412. // gained focus.
  413. if (comboBox instanceof Accessible) {
  414. AccessibleContext ac =
  415. ((Accessible)comboBox).getAccessibleContext();
  416. if (ac != null) {
  417. ac.firePropertyChange(
  418. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  419. null, AccessibleState.FOCUSED);
  420. }
  421. }
  422. }
  423. public void focusLost( FocusEvent e ) {
  424. hasFocus = false;
  425. // GES, 980818:
  426. // Note that the second check here is a workaround to bug
  427. // 4168483. There is a bogus focusLost sent to the
  428. // ComboBox with isTemporary false when a mediumweight menu
  429. // is popped up. Until this is fixed in AWT, we make the
  430. // tradeoff of not popping down mediumweight popups when
  431. // the combobox loses focus. Although this means that the
  432. // combobox does not remove such menus when you tab out,
  433. // it is seen as more desirable than the alternative which
  434. // is that mediumweight combobox menus dissappear immediately
  435. // on popup, rendering them completely unusable.
  436. if ( !e.isTemporary() && comboBox.isLightWeightPopupEnabled()) {
  437. setPopupVisible(comboBox, false);
  438. }
  439. comboBox.repaint();
  440. // Notify assistive technologies that the combo box
  441. // lost focus.
  442. if (comboBox instanceof Accessible) {
  443. AccessibleContext ac =
  444. ((Accessible)comboBox).getAccessibleContext();
  445. if (ac != null) {
  446. ac.firePropertyChange(
  447. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  448. AccessibleState.FOCUSED, null);
  449. }
  450. }
  451. }
  452. }
  453. /**
  454. * This listener watches for changes in the
  455. * <code>ComboBoxModel</code>.
  456. * <p>
  457. * This public inner class should be treated as protected.
  458. * Instantiate it only within subclasses of
  459. * <code>BasicComboBoxUI</code>.
  460. *
  461. * @see #createListDataListener
  462. */
  463. public class ListDataHandler implements ListDataListener {
  464. public void contentsChanged( ListDataEvent e ) {
  465. if ( !(e.getIndex0() == -1 && e.getIndex1() == -1) ) {
  466. isMinimumSizeDirty = true;
  467. comboBox.revalidate();
  468. }
  469. // set the editor with the selected item since this
  470. // is the event handler for a selected item change.
  471. if (comboBox.isEditable() && editor != null) {
  472. comboBox.configureEditor( comboBox.getEditor(),
  473. comboBox.getSelectedItem() );
  474. }
  475. comboBox.repaint();
  476. }
  477. public void intervalAdded( ListDataEvent e ) {
  478. isDisplaySizeDirty = true;
  479. contentsChanged( e );
  480. }
  481. public void intervalRemoved( ListDataEvent e ) {
  482. isDisplaySizeDirty = true;
  483. contentsChanged( e );
  484. }
  485. }
  486. /**
  487. * This listener watches for changes to the selection in the
  488. * combo box.
  489. * <p>
  490. * This public inner class should be treated as protected.
  491. * Instantiate it only within subclasses of
  492. * <code>BasicComboBoxUI</code>.
  493. *
  494. * @see #createItemListener
  495. */
  496. public class ItemHandler implements ItemListener {
  497. // This class used to implement behavior which is now redundant.
  498. public void itemStateChanged(ItemEvent e) {}
  499. }
  500. /**
  501. * This listener watches for bound properties that have changed in the
  502. * combo box.
  503. * <p>
  504. * Subclasses which wish to listen to combo box property changes should
  505. * call the superclass methods to ensure that the combo box ui correctly
  506. * handles property changes.
  507. * <p>
  508. * This public inner class should be treated as protected.
  509. * Instantiate it only within subclasses of
  510. * <code>BasicComboBoxUI</code>.
  511. *
  512. * @see #createPropertyChangeListener
  513. */
  514. public class PropertyChangeHandler implements PropertyChangeListener {
  515. public void propertyChange(PropertyChangeEvent e) {
  516. String propertyName = e.getPropertyName();
  517. JComboBox comboBox = (JComboBox)e.getSource();
  518. if ( propertyName.equals( "model" ) ) {
  519. ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
  520. ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
  521. if ( oldModel != null && listDataListener != null ) {
  522. oldModel.removeListDataListener( listDataListener );
  523. }
  524. if ( newModel != null && listDataListener != null ) {
  525. newModel.addListDataListener( listDataListener );
  526. }
  527. if ( editor != null ) {
  528. comboBox.configureEditor( comboBox.getEditor(), comboBox.getSelectedItem() );
  529. }
  530. isMinimumSizeDirty = true;
  531. isDisplaySizeDirty = true;
  532. comboBox.revalidate();
  533. comboBox.repaint();
  534. }
  535. else if ( propertyName.equals( "editor" ) && comboBox.isEditable() ) {
  536. addEditor();
  537. comboBox.revalidate();
  538. }
  539. else if ( propertyName.equals( "editable" ) ) {
  540. if ( comboBox.isEditable() ) {
  541. comboBox.setRequestFocusEnabled( false );
  542. addEditor();
  543. } else {
  544. comboBox.setRequestFocusEnabled( true );
  545. removeEditor();
  546. }
  547. updateToolTipTextForChildren();
  548. comboBox.revalidate();
  549. }
  550. else if ( propertyName.equals( "enabled" ) ) {
  551. boolean enabled = comboBox.isEnabled();
  552. if ( editor != null )
  553. editor.setEnabled(enabled);
  554. if ( arrowButton != null )
  555. arrowButton.setEnabled(enabled);
  556. comboBox.repaint();
  557. }
  558. else if ( propertyName.equals( "maximumRowCount" ) ) {
  559. if ( isPopupVisible( comboBox ) ) {
  560. setPopupVisible(comboBox, false);
  561. setPopupVisible(comboBox, true);
  562. }
  563. }
  564. else if ( propertyName.equals( "font" ) ) {
  565. listBox.setFont( comboBox.getFont() );
  566. if ( editor != null ) {
  567. editor.setFont( comboBox.getFont() );
  568. }
  569. isMinimumSizeDirty = true;
  570. comboBox.validate();
  571. }
  572. else if ( propertyName.equals( JComponent.TOOL_TIP_TEXT_KEY ) ) {
  573. updateToolTipTextForChildren();
  574. }
  575. else if ( propertyName.equals( BasicComboBoxUI.IS_TABLE_CELL_EDITOR ) ) {
  576. Boolean inTable = (Boolean)e.getNewValue();
  577. isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
  578. }
  579. else if (propertyName.equals("prototypeDisplayValue")) {
  580. isMinimumSizeDirty = true;
  581. isDisplaySizeDirty = true;
  582. comboBox.revalidate();
  583. }
  584. else if (propertyName.equals("renderer")) {
  585. isMinimumSizeDirty = true;
  586. isDisplaySizeDirty = true;
  587. comboBox.revalidate();
  588. }
  589. }
  590. }
  591. // Syncronizes the ToolTip text for the components within the combo box to be the
  592. // same value as the combo box ToolTip text.
  593. private void updateToolTipTextForChildren() {
  594. Component[] children = comboBox.getComponents();
  595. for ( int i = 0; i < children.length; ++i ) {
  596. if ( children[i] instanceof JComponent ) {
  597. ((JComponent)children[i]).setToolTipText( comboBox.getToolTipText() );
  598. }
  599. }
  600. }
  601. /**
  602. * This layout manager handles the 'standard' layout of combo boxes. It puts
  603. * the arrow button to the right and the editor to the left. If there is no
  604. * editor it still keeps the arrow button to the right.
  605. *
  606. * This public inner class should be treated as protected.
  607. * Instantiate it only within subclasses of
  608. * <code>BasicComboBoxUI</code>.
  609. */
  610. public class ComboBoxLayoutManager implements LayoutManager {
  611. public void addLayoutComponent(String name, Component comp) {}
  612. public void removeLayoutComponent(Component comp) {}
  613. public Dimension preferredLayoutSize(Container parent) {
  614. JComboBox cb = (JComboBox)parent;
  615. return parent.getPreferredSize();
  616. }
  617. public Dimension minimumLayoutSize(Container parent) {
  618. JComboBox cb = (JComboBox)parent;
  619. return parent.getMinimumSize();
  620. }
  621. public void layoutContainer(Container parent) {
  622. JComboBox cb = (JComboBox)parent;
  623. int width = cb.getWidth();
  624. int height = cb.getHeight();
  625. Insets insets = getInsets();
  626. int buttonSize = height - (insets.top + insets.bottom);
  627. Rectangle cvb;
  628. if ( arrowButton != null ) {
  629. if(BasicGraphicsUtils.isLeftToRight(cb)) {
  630. arrowButton.setBounds( width - (insets.right + buttonSize),
  631. insets.top,
  632. buttonSize, buttonSize);
  633. }
  634. else {
  635. arrowButton.setBounds( insets.left, insets.top,
  636. buttonSize, buttonSize);
  637. }
  638. }
  639. if ( editor != null ) {
  640. cvb = rectangleForCurrentValue();
  641. editor.setBounds(cvb);
  642. }
  643. }
  644. }
  645. //
  646. // end Inner classes
  647. //====================
  648. //===============================
  649. // begin Sub-Component Management
  650. //
  651. /**
  652. * Creates and initializes the components which make up the
  653. * aggregate combo box. This method is called as part of the UI
  654. * installation process.
  655. */
  656. protected void installComponents() {
  657. arrowButton = createArrowButton();
  658. comboBox.add( arrowButton );
  659. if (arrowButton != null) {
  660. configureArrowButton();
  661. }
  662. if ( comboBox.isEditable() ) {
  663. addEditor();
  664. }
  665. comboBox.add( currentValuePane );
  666. }
  667. /**
  668. * The aggregate components which compise the combo box are
  669. * unregistered and uninitialized. This method is called as part of the
  670. * UI uninstallation process.
  671. */
  672. protected void uninstallComponents() {
  673. if ( arrowButton != null ) {
  674. unconfigureArrowButton();
  675. }
  676. if ( editor != null ) {
  677. unconfigureEditor();
  678. }
  679. comboBox.removeAll(); // Just to be safe.
  680. arrowButton = null;
  681. }
  682. /**
  683. * This public method is implementation specific and should be private.
  684. * do not call or override. To implement a specific editor create a
  685. * custom <code>ComboBoxEditor</code>
  686. *
  687. * @see #createEditor
  688. * @see javax.swing.JComboBox#setEditor
  689. * @see javax.swing.ComboBoxEditor
  690. */
  691. public void addEditor() {
  692. removeEditor();
  693. editor = comboBox.getEditor().getEditorComponent();
  694. if ( editor != null ) {
  695. configureEditor();
  696. comboBox.add(editor);
  697. }
  698. }
  699. /**
  700. * This public method is implementation specific and should be private.
  701. * do not call or override.
  702. *
  703. * @see #addEditor
  704. */
  705. public void removeEditor() {
  706. if ( editor != null ) {
  707. unconfigureEditor();
  708. comboBox.remove( editor );
  709. editor = null;
  710. }
  711. }
  712. /**
  713. * This protected method is implementation specific and should be private.
  714. * do not call or override.
  715. *
  716. * @see #addEditor
  717. */
  718. protected void configureEditor() {
  719. // Should be in the same state as the combobox
  720. editor.setEnabled(comboBox.isEnabled());
  721. editor.setFont( comboBox.getFont() );
  722. if ( editor instanceof Accessible ) {
  723. AccessibleContext ac = ((Accessible) editor).getAccessibleContext();
  724. if ( ac != null ) {
  725. ac.setAccessibleParent(comboBox);
  726. }
  727. }
  728. if (focusListener != null) {
  729. editor.addFocusListener(focusListener);
  730. }
  731. if (editorFocusListener == null) {
  732. editorFocusListener = new EditorFocusListener();
  733. }
  734. editor.addFocusListener( editorFocusListener );
  735. if (editorActionListener == null) {
  736. editorActionListener = new EditorActionListener();
  737. }
  738. comboBox.getEditor().addActionListener(editorActionListener);
  739. comboBox.configureEditor(comboBox.getEditor(),comboBox.getSelectedItem());
  740. }
  741. /**
  742. * This protected method is implementation specific and should be private.
  743. * Do not call or override.
  744. *
  745. * @see #addEditor
  746. */
  747. protected void unconfigureEditor() {
  748. if (focusListener != null) {
  749. editor.removeFocusListener(focusListener);
  750. }
  751. if ( editorFocusListener != null ) {
  752. editor.removeFocusListener( editorFocusListener );
  753. }
  754. if ( editorActionListener != null) {
  755. comboBox.getEditor().removeActionListener(editorActionListener);
  756. }
  757. }
  758. /**
  759. * This public method is implementation specific and should be private. Do
  760. * not call or override.
  761. *
  762. * @see #createArrowButton
  763. */
  764. public void configureArrowButton() {
  765. if ( arrowButton != null ) {
  766. arrowButton.setEnabled( comboBox.isEnabled() );
  767. arrowButton.setRequestFocusEnabled(false);
  768. arrowButton.addMouseListener( popup.getMouseListener() );
  769. arrowButton.addMouseMotionListener( popup.getMouseMotionListener() );
  770. arrowButton.resetKeyboardActions();
  771. }
  772. }
  773. /**
  774. * This public method is implementation specific and should be private. Do
  775. * not call or override.
  776. *
  777. * @see #createArrowButton
  778. */
  779. public void unconfigureArrowButton() {
  780. if ( arrowButton != null ) {
  781. arrowButton.removeMouseListener( popup.getMouseListener() );
  782. arrowButton.removeMouseMotionListener( popup.getMouseMotionListener() );
  783. }
  784. }
  785. /**
  786. * Creates an button which will be used as the control to show or hide
  787. * the popup portion of the combo box.
  788. *
  789. * @return a button which represents the popup control
  790. */
  791. protected JButton createArrowButton() {
  792. return new BasicArrowButton(BasicArrowButton.SOUTH,
  793. UIManager.getColor("ComboBox.buttonBackground"),
  794. UIManager.getColor("ComboBox.buttonShadow"),
  795. UIManager.getColor("ComboBox.buttonDarkShadow"),
  796. UIManager.getColor("ComboBox.buttonHighlight"));
  797. }
  798. //
  799. // end Sub-Component Management
  800. //===============================
  801. //================================
  802. // begin ComboBoxUI Implementation
  803. //
  804. /**
  805. * Tells if the popup is visible or not.
  806. */
  807. public boolean isPopupVisible( JComboBox c ) {
  808. return popup.isVisible();
  809. }
  810. /**
  811. * Hides the popup.
  812. */
  813. public void setPopupVisible( JComboBox c, boolean v ) {
  814. if ( v ) {
  815. popup.show();
  816. } else {
  817. popup.hide();
  818. }
  819. }
  820. /**
  821. * Determines if the JComboBox is focus traversable. If the JComboBox is editable
  822. * this returns false, otherwise it returns true.
  823. */
  824. public boolean isFocusTraversable( JComboBox c ) {
  825. return !comboBox.isEditable();
  826. }
  827. //
  828. // end ComboBoxUI Implementation
  829. //==============================
  830. //=================================
  831. // begin ComponentUI Implementation
  832. public void paint( Graphics g, JComponent c ) {
  833. hasFocus = comboBox.hasFocus();
  834. if ( !comboBox.isEditable() ) {
  835. Rectangle r = rectangleForCurrentValue();
  836. paintCurrentValueBackground(g,r,hasFocus);
  837. paintCurrentValue(g,r,hasFocus);
  838. }
  839. }
  840. public Dimension getPreferredSize( JComponent c ) {
  841. return getMinimumSize(c);
  842. }
  843. /**
  844. * The minumum size is the size of the display area plus insets plus the button.
  845. */
  846. public Dimension getMinimumSize( JComponent c ) {
  847. if ( !isMinimumSizeDirty ) {
  848. return new Dimension(cachedMinimumSize);
  849. }
  850. Dimension size = getDisplaySize();
  851. Insets insets = getInsets();
  852. size.height += insets.top + insets.bottom;
  853. int buttonSize = size.height - (insets.top + insets.bottom);
  854. size.width += insets.left + insets.right + buttonSize;
  855. cachedMinimumSize.setSize( size.width, size.height );
  856. isMinimumSizeDirty = false;
  857. return new Dimension(size);
  858. }
  859. public Dimension getMaximumSize( JComponent c ) {
  860. return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
  861. }
  862. // This is currently hacky...
  863. public int getAccessibleChildrenCount(JComponent c) {
  864. if ( comboBox.isEditable() ) {
  865. return 2;
  866. }
  867. else {
  868. return 1;
  869. }
  870. }
  871. // This is currently hacky...
  872. public Accessible getAccessibleChild(JComponent c, int i) {
  873. // 0 = the popup
  874. // 1 = the editor
  875. switch ( i ) {
  876. case 0:
  877. if ( popup instanceof Accessible ) {
  878. AccessibleContext ac = ((Accessible) popup).getAccessibleContext();
  879. ac.setAccessibleParent(comboBox);
  880. return(Accessible) popup;
  881. }
  882. break;
  883. case 1:
  884. if ( comboBox.isEditable()
  885. && (editor instanceof Accessible) ) {
  886. AccessibleContext ac = ((Accessible) editor).getAccessibleContext();
  887. ac.setAccessibleParent(comboBox);
  888. return(Accessible) editor;
  889. }
  890. break;
  891. }
  892. return null;
  893. }
  894. //
  895. // end ComponentUI Implementation
  896. //===============================
  897. //======================
  898. // begin Utility Methods
  899. //
  900. /**
  901. * Returns whether or not the supplied keyCode maps to a key that is used for
  902. * navigation. This is used for optimizing key input by only passing non-
  903. * navigation keys to the type-ahead mechanism. Subclasses should override this
  904. * if they change the navigation keys.
  905. */
  906. protected boolean isNavigationKey( int keyCode ) {
  907. return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN ||
  908. // This is horrible, but necessary since these aren't
  909. // supported until JDK 1.2
  910. keyCode == KeyStroke.getKeyStroke("KP_UP").getKeyCode() ||
  911. keyCode == KeyStroke.getKeyStroke("KP_DOWN").getKeyCode();
  912. }
  913. /**
  914. * Selects the next item in the list. It won't change the selection if the
  915. * currently selected item is already the last item.
  916. */
  917. protected void selectNextPossibleValue() {
  918. int si;
  919. if ( isTableCellEditor ) {
  920. si = listBox.getSelectedIndex();
  921. }
  922. else {
  923. si = comboBox.getSelectedIndex();
  924. }
  925. if ( si < comboBox.getModel().getSize() - 1 ) {
  926. if ( isTableCellEditor ) {
  927. listBox.setSelectedIndex( si + 1 );
  928. listBox.ensureIndexIsVisible( si + 1 );
  929. }
  930. else {
  931. comboBox.setSelectedIndex(si+1);
  932. }
  933. comboBox.repaint();
  934. }
  935. }
  936. /**
  937. * Selects the previous item in the list. It won't change the selection if the
  938. * currently selected item is already the first item.
  939. */
  940. protected void selectPreviousPossibleValue() {
  941. int si;
  942. if ( isTableCellEditor ) {
  943. si = listBox.getSelectedIndex();
  944. }
  945. else {
  946. si = comboBox.getSelectedIndex();
  947. }
  948. if ( si > 0 ) {
  949. if ( isTableCellEditor ) {
  950. listBox.setSelectedIndex( si - 1 );
  951. listBox.ensureIndexIsVisible( si - 1 );
  952. }
  953. else {
  954. comboBox.setSelectedIndex(si-1);
  955. }
  956. comboBox.repaint();
  957. }
  958. }
  959. /**
  960. * Hides the popup if it is showing and shows the popup if it is hidden.
  961. */
  962. protected void toggleOpenClose() {
  963. setPopupVisible(comboBox, !isPopupVisible(comboBox));
  964. }
  965. /**
  966. * Returns the area that is reserved for drawing the currently selected item.
  967. */
  968. protected Rectangle rectangleForCurrentValue() {
  969. int width = comboBox.getWidth();
  970. int height = comboBox.getHeight();
  971. Insets insets = getInsets();
  972. int buttonSize = height - (insets.top + insets.bottom);
  973. if ( arrowButton != null ) {
  974. buttonSize = arrowButton.getWidth();
  975. }
  976. if(BasicGraphicsUtils.isLeftToRight(comboBox)) {
  977. return new Rectangle(insets.left, insets.top,
  978. width - (insets.left + insets.right + buttonSize),
  979. height - (insets.top + insets.bottom));
  980. }
  981. else {
  982. return new Rectangle(insets.left + buttonSize, insets.top,
  983. width - (insets.left + insets.right + buttonSize),
  984. height - (insets.top + insets.bottom));
  985. }
  986. }
  987. /**
  988. * Gets the insets from the JComboBox.
  989. */
  990. protected Insets getInsets() {
  991. return comboBox.getInsets();
  992. }
  993. //
  994. // end Utility Methods
  995. //====================
  996. //===============================
  997. // begin Painting Utility Methods
  998. //
  999. /**
  1000. * Paints the currently selected item.
  1001. */
  1002. public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
  1003. ListCellRenderer renderer = comboBox.getRenderer();
  1004. Component c;
  1005. if ( hasFocus && !isPopupVisible(comboBox) ) {
  1006. c = renderer.getListCellRendererComponent( listBox,
  1007. comboBox.getSelectedItem(),
  1008. -1,
  1009. true,
  1010. false );
  1011. }
  1012. else {
  1013. c = renderer.getListCellRendererComponent( listBox,
  1014. comboBox.getSelectedItem(),
  1015. -1,
  1016. false,
  1017. false );
  1018. c.setBackground(UIManager.getColor("ComboBox.background"));
  1019. }
  1020. c.setFont(comboBox.getFont());
  1021. if ( hasFocus && !isPopupVisible(comboBox) ) {
  1022. c.setForeground(listBox.getSelectionForeground());
  1023. c.setBackground(listBox.getSelectionBackground());
  1024. }
  1025. else {
  1026. if ( comboBox.isEnabled() ) {
  1027. c.setForeground(comboBox.getForeground());
  1028. c.setBackground(comboBox.getBackground());
  1029. }
  1030. else {
  1031. c.setForeground(UIManager.getColor("ComboBox.disabledForeground"));
  1032. c.setBackground(UIManager.getColor("ComboBox.disabledBackground"));
  1033. }
  1034. }
  1035. // Fix for 4238829: should lay out the JPanel.
  1036. boolean shouldValidate = false;
  1037. if (c instanceof JPanel) {
  1038. shouldValidate = true;
  1039. }
  1040. currentValuePane.paintComponent(g,c,comboBox,bounds.x,bounds.y,
  1041. bounds.width,bounds.height, shouldValidate);
  1042. }
  1043. /**
  1044. * Paints the background of the currently selected item.
  1045. */
  1046. public void paintCurrentValueBackground(Graphics g,Rectangle bounds,boolean hasFocus) {
  1047. Color t = g.getColor();
  1048. if ( comboBox.isEnabled() )
  1049. g.setColor(UIManager.getColor("ComboBox.background"));
  1050. else
  1051. g.setColor(UIManager.getColor("ComboBox.disabledBackground"));
  1052. g.fillRect(bounds.x,bounds.y,bounds.width,bounds.height);
  1053. g.setColor(t);
  1054. }
  1055. /**
  1056. * Repaint the currently selected item.
  1057. */
  1058. void repaintCurrentValue() {
  1059. Rectangle r = rectangleForCurrentValue();
  1060. comboBox.repaint(r.x,r.y,r.width,r.height);
  1061. }
  1062. //
  1063. // end Painting Utility Methods
  1064. //=============================
  1065. //===============================
  1066. // begin Size Utility Methods
  1067. //
  1068. /**
  1069. * Return the default size of an empty display area of the combo box using
  1070. * the current renderer and font.
  1071. *
  1072. * @return the size of an empty display area
  1073. * @see #getDisplaySize
  1074. */
  1075. protected Dimension getDefaultSize() {
  1076. // Calculates the height and width using the default text renderer
  1077. Dimension d = getSizeForComponent(getDefaultListCellRenderer().getListCellRendererComponent(listBox, " ", -1, false, false));
  1078. return new Dimension(d.width, d.height);
  1079. }
  1080. /**
  1081. * Returns the calculated size of the display area. The display area is the
  1082. * portion of the combo box in which the selected item is displayed. This
  1083. * method will use the prototype display value if it has been set.
  1084. * <p>
  1085. * For combo boxes with a non trivial number of items, it is recommended to
  1086. * use a prototype display value to significantly speed up the display
  1087. * size calculation.
  1088. *
  1089. * @return the size of the display area calculated from the combo box items
  1090. * @see javax.swing.JComboBox#setPrototypeDisplayValue
  1091. */
  1092. protected Dimension getDisplaySize() {
  1093. if (!isDisplaySizeDirty) {
  1094. return new Dimension(cachedDisplaySize);
  1095. }
  1096. Dimension result = new Dimension();
  1097. ListCellRenderer renderer = comboBox.getRenderer();
  1098. if (renderer == null) {
  1099. renderer = new DefaultListCellRenderer();
  1100. }
  1101. Object prototypeValue = comboBox.getPrototypeDisplayValue();
  1102. if (prototypeValue != null) {
  1103. // Calculates the dimension based on the prototype value
  1104. result = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
  1105. prototypeValue,
  1106. -1, false, false));
  1107. } else {
  1108. // Calculate the dimension by iterating over all the elements in the combo
  1109. // box list.
  1110. ComboBoxModel model = comboBox.getModel();
  1111. int modelSize = model.getSize();
  1112. Dimension d;
  1113. Component cpn;
  1114. if (modelSize > 0 ) {
  1115. for (int i = 0; i < modelSize ; i++ ) {
  1116. // Calculates the maximum height and width based on the largest
  1117. // element
  1118. d = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
  1119. model.getElementAt(i),
  1120. -1, false, false));
  1121. result.width = Math.max(result.width,d.width);
  1122. result.height = Math.max(result.height,d.height);
  1123. }
  1124. } else {
  1125. result = getDefaultSize();
  1126. if (comboBox.isEditable()) {
  1127. result.width = 100;
  1128. }
  1129. }
  1130. }
  1131. if ( comboBox.isEditable() ) {
  1132. Dimension d = editor.getPreferredSize();
  1133. result.width = Math.max(result.width,d.width);
  1134. result.height = Math.max(result.height,d.height);
  1135. }
  1136. // Set the cached value
  1137. cachedDisplaySize.setSize(result.width, result.height);
  1138. isDisplaySizeDirty = false;
  1139. return result;
  1140. }
  1141. /**
  1142. * This has been refactored out in hopes that it may be investigated and
  1143. * simplified for the next major release. adding/removing
  1144. * the component to the currentValuePane and changing the font may be
  1145. * redundant operations.
  1146. */
  1147. private Dimension getSizeForComponent(Component comp) {
  1148. currentValuePane.add(comp);
  1149. comp.setFont(comboBox.getFont());
  1150. Dimension d = comp.getPreferredSize();
  1151. currentValuePane.remove(comp);
  1152. return d;
  1153. }
  1154. //
  1155. // end Size Utility Methods
  1156. //=============================
  1157. //=================================
  1158. // begin Keyboard Action Management
  1159. //
  1160. /**
  1161. * Adds keyboard actions to the JComboBox. Actions on enter and esc are already
  1162. * supplied. Add more actions as you need them.
  1163. */
  1164. protected void installKeyboardActions() {
  1165. InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  1166. SwingUtilities.replaceUIInputMap(comboBox, JComponent.
  1167. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
  1168. ActionMap am = getActionMap();
  1169. if (am != null) {
  1170. SwingUtilities.replaceUIActionMap(comboBox, am);
  1171. }
  1172. }
  1173. InputMap getInputMap(int condition) {
  1174. if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  1175. return (InputMap)UIManager.get("ComboBox.ancestorInputMap");
  1176. }
  1177. return null;
  1178. }
  1179. ActionMap getActionMap() {
  1180. return createActionMap();
  1181. }
  1182. static Action homeAction = new NavigationalAction(KeyEvent.VK_HOME);
  1183. static Action endAction = new NavigationalAction(KeyEvent.VK_END);
  1184. static Action pgUpAction = new NavigationalAction(KeyEvent.VK_PAGE_UP);
  1185. static Action pgDownAction = new NavigationalAction(KeyEvent.VK_PAGE_DOWN);
  1186. ActionMap createActionMap() {
  1187. ActionMap map = new ActionMapUIResource();
  1188. map.put("hidePopup", new HidePopupAction());
  1189. map.put("pageDownPassThrough", pgDownAction);
  1190. map.put("pageUpPassThrough", pgUpAction);
  1191. map.put("homePassThrough", homeAction);
  1192. map.put("endPassThrough", endAction);
  1193. map.put("selectNext", new DownAction());
  1194. map.put("togglePopup", new AltAction());
  1195. map.put("spacePopup", new SpaceAction());
  1196. map.put("selectPrevious", new UpAction());
  1197. map.put("enterPressed", new EnterAction());
  1198. return map;
  1199. }
  1200. boolean isTableCellEditor() {
  1201. return isTableCellEditor;
  1202. }
  1203. /**
  1204. * Removes the focus InputMap and ActionMap.
  1205. */
  1206. protected void uninstallKeyboardActions() {
  1207. SwingUtilities.replaceUIInputMap(comboBox, JComponent.
  1208. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
  1209. SwingUtilities.replaceUIActionMap(comboBox, null);
  1210. }
  1211. //
  1212. // Actions
  1213. //
  1214. class HidePopupAction extends AbstractAction {
  1215. public void actionPerformed( ActionEvent e ) {
  1216. JComboBox comboBox = (JComboBox)e.getSource();
  1217. if ( comboBox.isEnabled() ) {
  1218. comboBox.firePopupMenuCanceled();
  1219. comboBox.setPopupVisible(false);
  1220. }
  1221. }
  1222. public boolean isEnabled() {
  1223. return comboBox.isPopupVisible();
  1224. }
  1225. }
  1226. static class NavigationalAction extends AbstractAction {
  1227. int keyCode;
  1228. NavigationalAction(int keyCode) {
  1229. this.keyCode = keyCode;
  1230. }
  1231. public void actionPerformed(ActionEvent ev) {
  1232. JComboBox comboBox = (JComboBox)ev.getSource();
  1233. int index = getNextIndex(comboBox);
  1234. if (index >= 0 && index < comboBox.getItemCount()) {
  1235. comboBox.setSelectedIndex(index);
  1236. }
  1237. }
  1238. int getNextIndex(JComboBox comboBox) {
  1239. switch (keyCode) {
  1240. case KeyEvent.VK_PAGE_UP:
  1241. int listHeight = comboBox.getMaximumRowCount();
  1242. int index = comboBox.getSelectedIndex() - listHeight;
  1243. return (index < 0 ? 0: index);
  1244. case KeyEvent.VK_PAGE_DOWN:
  1245. listHeight = comboBox.getMaximumRowCount();
  1246. index = comboBox.getSelectedIndex() + listHeight;
  1247. int max = comboBox.getItemCount();
  1248. return (index < max ? index: max-1);
  1249. case KeyEvent.VK_HOME:
  1250. return 0;
  1251. case KeyEvent.VK_END:
  1252. return comboBox.getItemCount() - 1;
  1253. default:
  1254. return comboBox.getSelectedIndex();
  1255. }
  1256. }
  1257. }
  1258. static class DownAction extends AbstractAction {
  1259. public void actionPerformed(ActionEvent e) {
  1260. JComboBox comboBox = (JComboBox)e.getSource();
  1261. if ( comboBox.isEnabled() && comboBox.isShowing() ) {
  1262. if ( comboBox.isPopupVisible() ) {
  1263. BasicComboBoxUI ui = (BasicComboBoxUI)comboBox.getUI();
  1264. ui.selectNextPossibleValue();
  1265. } else {
  1266. comboBox.setPopupVisible(true);
  1267. }
  1268. }
  1269. }
  1270. }
  1271. static class EnterAction extends AbstractAction {
  1272. public void actionPerformed(ActionEvent e) {
  1273. JComboBox comboBox = (JComboBox)e.getSource();
  1274. if ( !comboBox.isEnabled() ) {
  1275. return;
  1276. }
  1277. BasicComboBoxUI ui = (BasicComboBoxUI)comboBox.getUI();
  1278. if ( ui.isTableCellEditor() ) {
  1279. // Forces the selection of the list item if the
  1280. // combo box is in a JTable.
  1281. comboBox.setSelectedIndex(ui.popup.getList().getSelectedIndex());
  1282. }
  1283. else {
  1284. if (comboBox.isPopupVisible()) {
  1285. comboBox.setPopupVisible(false);
  1286. } else {
  1287. // Call the default button binding.
  1288. // This is a pretty messy way of passing an event through
  1289. // to the root pane.
  1290. JRootPane root = SwingUtilities.getRootPane(comboBox);
  1291. if (root != null) {
  1292. InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
  1293. ActionMap am = root.getActionMap();
  1294. if (im != null && am != null) {
  1295. Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
  1296. if (obj != null) {
  1297. Action action = am.get(obj);
  1298. if (action != null) {
  1299. action.actionPerformed(e);
  1300. }
  1301. }
  1302. }
  1303. }
  1304. }
  1305. }
  1306. }
  1307. }
  1308. static class AltAction extends AbstractAction {
  1309. public void actionPerformed(ActionEvent e) {
  1310. JComboBox comboBox = (JComboBox)e.getSource();
  1311. if ( comboBox.isEnabled() ) {
  1312. BasicComboBoxUI ui = (BasicComboBoxUI)comboBox.getUI();
  1313. if ( ui.isTableCellEditor() ) {
  1314. // Forces the selection of the list item if the
  1315. // combo box is in a JTable.
  1316. comboBox.setSelectedIndex(ui.popup.getList().getSelectedIndex());
  1317. }
  1318. else {
  1319. comboBox.setPopupVisible(!comboBox.isPopupVisible());
  1320. }
  1321. }
  1322. }
  1323. }
  1324. // Same as the AltAction except that it doesn't invoke if
  1325. // the space key is pressed in the editable text portion.
  1326. static class SpaceAction extends AltAction {
  1327. public void actionPerformed(ActionEvent e) {
  1328. JComboBox comboBox = (JComboBox)e.getSource();
  1329. if ( !comboBox.isEditable() ) {
  1330. super.actionPerformed(e);
  1331. }
  1332. }
  1333. }
  1334. static class UpAction extends AbstractAction {
  1335. public void actionPerformed(ActionEvent e) {
  1336. JComboBox comboBox = (JComboBox)e.getSource();
  1337. if ( comboBox.isEnabled() ) {
  1338. BasicComboBoxUI ui = (BasicComboBoxUI)comboBox.getUI();
  1339. if (ui.isPopupVisible(comboBox)) {
  1340. ui.selectPreviousPossibleValue();
  1341. }
  1342. }
  1343. }
  1344. }
  1345. //
  1346. // end Keyboard Action Management
  1347. //===============================
  1348. class EditorFocusListener extends FocusAdapter {
  1349. /**
  1350. * This will make the comboBox fire an ActionEvent if the editor
  1351. * value is different from the selected item in the model.
  1352. * This allows for the entering of data in the combo box editor and
  1353. * sends notification when tabbing or clicking out of focus.
  1354. */
  1355. public void focusLost( FocusEvent e ) {
  1356. ComboBoxEditor editor = comboBox.getEditor();
  1357. Object item = editor.getItem();
  1358. if (!e.isTemporary() && item != null &&
  1359. !item.equals( comboBox.getSelectedItem())) {
  1360. comboBox.actionPerformed
  1361. (new ActionEvent(editor, 0, "",
  1362. EventQueue.getMostRecentEventTime(), 0));
  1363. }
  1364. }
  1365. }
  1366. class EditorActionListener implements ActionListener {
  1367. // Fix for 4515752: Forward the Enter pressed on the
  1368. // editable combo box to the default button if the item has
  1369. // not changed.
  1370. // Note: This could depend on event ordering. The first ActionEvent
  1371. // from the editor may be handled by the JComboBox in which case, the
  1372. // enterPressed action will always be invoked.
  1373. public void actionPerformed(ActionEvent evt) {
  1374. Object item = comboBox.getEditor().getItem();
  1375. if (item != null && item.equals(comboBox.getSelectedItem())) {
  1376. ActionMap am = comboBox.getActionMap();
  1377. if (am != null) {
  1378. Action action = am.get("enterPressed");
  1379. if (action != null) {
  1380. action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
  1381. evt.getActionCommand(),
  1382. evt.getModifiers()));
  1383. }
  1384. }
  1385. }
  1386. }
  1387. }
  1388. }