KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicComboBoxUI


1 /*
2  * @(#)BasicComboBoxUI.java 1.172 06/04/20
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.plaf.basic;
9
10 import java.awt.*;
11 import java.awt.event.*;
12 import javax.swing.*;
13 import javax.accessibility.*;
14 import javax.swing.FocusManager JavaDoc;
15 import javax.swing.plaf.*;
16 import javax.swing.border.*;
17 import javax.swing.text.*;
18 import javax.swing.event.*;
19 import java.beans.PropertyChangeListener JavaDoc;
20 import java.beans.PropertyChangeEvent JavaDoc;
21 import sun.awt.AppContext;
22 import sun.swing.DefaultLookup;
23 import sun.swing.UIAction;
24
25 /**
26  * Basic UI implementation for JComboBox.
27  * <p>
28  * The combo box is a compound component which means that it is an agregate of
29  * many simpler components. This class creates and manages the listeners
30  * on the combo box and the combo box model. These listeners update the user
31  * interface in response to changes in the properties and state of the combo box.
32  * <p>
33  * All event handling is handled by listener classes created with the
34  * <code>createxxxListener()</code> methods and internal classes.
35  * You can change the behavior of this class by overriding the
36  * <code>createxxxListener()</code> methods and supplying your own
37  * event listeners or subclassing from the ones supplied in this class.
38  * <p>
39  * For adding specific actions,
40  * overide <code>installKeyboardActions</code> to add actions in response to
41  * KeyStroke bindings. See the article <a HREF="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">Keyboard Bindings in Swing</a>
42  * at <a HREF="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
43  *
44  * @version 1.172 04/20/06
45  * @author Arnaud Weber
46  * @author Tom Santos
47  * @author Mark Davidson
48  */

49 public class BasicComboBoxUI extends ComboBoxUI {
50     protected JComboBox comboBox;
51     /**
52      * This protected field is implementation specific. Do not access directly
53      * or override.
54      */

55     protected boolean hasFocus = false;
56
57     // Control the selection behavior of the JComboBox when it is used
58
// in the JTable DefaultCellEditor.
59
private boolean isTableCellEditor = false;
60     private static final String JavaDoc IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
61
62     // This list is for drawing the current item in the combo box.
63
protected JList listBox;
64
65     // Used to render the currently selected item in the combo box.
66
// It doesn't have anything to do with the popup's rendering.
67
protected CellRendererPane currentValuePane = new CellRendererPane();
68
69     // The implementation of ComboPopup that is used to show the popup.
70
protected ComboPopup JavaDoc popup;
71
72     // The Component that the ComboBoxEditor uses for editing
73
protected Component editor;
74
75     // The arrow button that invokes the popup.
76
protected JButton arrowButton;
77
78     // Listeners that are attached to the JComboBox
79
/**
80      * This protected field is implementation specific. Do not access directly
81      * or override. Override the listener construction method instead.
82      *
83      * @see #createKeyListener
84      */

85     protected KeyListener keyListener;
86     /**
87      * This protected field is implementation specific. Do not access directly
88      * or override. Override the listener construction method instead.
89      *
90      * @see #createFocusListener
91      */

92     protected FocusListener focusListener;
93     /**
94      * This protected field is implementation specific. Do not access directly
95      * or override. Override the listener construction method instead.
96      *
97      * @see #createPropertyChangeListener
98      */

99     protected PropertyChangeListener JavaDoc propertyChangeListener;
100
101     /**
102      * This protected field is implementation specific. Do not access directly
103      * or override. Override the listener construction method instead.
104      *
105      * @see #createItemListener
106      */

107     protected ItemListener itemListener;
108
109     // Listeners that the ComboPopup produces.
110
protected MouseListener popupMouseListener;
111     protected MouseMotionListener popupMouseMotionListener;
112     protected KeyListener popupKeyListener;
113
114     // This is used for knowing when to cache the minimum preferred size.
115
// If the data in the list changes, the cached value get marked for recalc.
116
// Added to the current JComboBox model
117
/**
118      * This protected field is implementation specific. Do not access directly
119      * or override. Override the listener construction method instead.
120      *
121      * @see #createListDataListener
122      */

123     protected ListDataListener listDataListener;
124
125     /**
126      * Implements all the Listeners needed by this class, all existing
127      * listeners redirect to it.
128      */

129     private Handler handler;
130
131     /**
132      * The time factor to treate the series of typed alphanumeric key
133      * as prefix for first letter navigation.
134      */

135     private long timeFactor = 1000L;
136
137     /**
138      * This is tricky, this variables is needed for DefaultKeySelectionManager
139      * to take into account time factor.
140      */

141     private long lastTime = 0L;
142     private long time = 0L;
143
144     /**
145      * The default key selection manager
146      */

147     JComboBox.KeySelectionManager keySelectionManager;
148
149     // Flag for recalculating the minimum preferred size.
150
protected boolean isMinimumSizeDirty = true;
151
152     // Cached minimum preferred size.
153
protected Dimension cachedMinimumSize = new Dimension( 0, 0 );
154
155     // Flag for calculating the display size
156
private boolean isDisplaySizeDirty = true;
157     
158     // Cached the size that the display needs to render the largest item
159
private Dimension cachedDisplaySize = new Dimension( 0, 0 );
160
161     // Key used for lookup of the DefaultListCellRenderer in the AppContext.
162
private static final Object JavaDoc COMBO_UI_LIST_CELL_RENDERER_KEY =
163                         new StringBuffer JavaDoc("DefaultListCellRendererKey");
164
165     static final StringBuffer JavaDoc HIDE_POPUP_KEY
166                   = new StringBuffer JavaDoc("HidePopupKey");
167
168     // Used for calculating the default size.
169
private static ListCellRenderer getDefaultListCellRenderer() {
170         ListCellRenderer renderer = (ListCellRenderer)AppContext.
171                          getAppContext().get(COMBO_UI_LIST_CELL_RENDERER_KEY);
172
173         if (renderer == null) {
174             renderer = new DefaultListCellRenderer();
175             AppContext.getAppContext().put(COMBO_UI_LIST_CELL_RENDERER_KEY,
176                                            new DefaultListCellRenderer());
177         }
178         return renderer;
179     }
180
181     /**
182      * Populates ComboBox's actions.
183      */

184     static void loadActionMap(LazyActionMap JavaDoc map) {
185     map.put(new Actions(Actions.HIDE));
186     map.put(new Actions(Actions.PAGE_DOWN));
187     map.put(new Actions(Actions.PAGE_UP));
188     map.put(new Actions(Actions.HOME));
189     map.put(new Actions(Actions.END));
190     map.put(new Actions(Actions.DOWN));
191     map.put(new Actions(Actions.DOWN_2));
192     map.put(new Actions(Actions.TOGGLE));
193     map.put(new Actions(Actions.TOGGLE_2));
194     map.put(new Actions(Actions.UP));
195     map.put(new Actions(Actions.UP_2));
196     map.put(new Actions(Actions.ENTER));
197     }
198
199     //========================
200
// begin UI Initialization
201
//
202

203     public static ComponentUI createUI(JComponent c) {
204         return new BasicComboBoxUI JavaDoc();
205     }
206
207     public void installUI( JComponent c ) {
208         isMinimumSizeDirty = true;
209
210         comboBox = (JComboBox)c;
211         installDefaults();
212         popup = createPopup();
213         listBox = popup.getList();
214
215     // Is this combo box a cell editor?
216
Boolean JavaDoc inTable = (Boolean JavaDoc)c.getClientProperty(IS_TABLE_CELL_EDITOR );
217     if (inTable != null) {
218         isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
219     }
220
221         if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
222             comboBox.setRenderer( createRenderer() );
223         }
224
225         if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
226             comboBox.setEditor( createEditor() );
227         }
228
229         installListeners();
230         installComponents();
231
232         comboBox.setLayout( createLayoutManager() );
233
234         comboBox.setRequestFocusEnabled( true );
235
236     installKeyboardActions();
237
238         comboBox.putClientProperty("doNotCancelPopup", HIDE_POPUP_KEY);
239
240         if (keySelectionManager == null || keySelectionManager instanceof UIResource) {
241             keySelectionManager = new DefaultKeySelectionManager();
242     }
243     comboBox.setKeySelectionManager(keySelectionManager);
244     }
245
246     public void uninstallUI( JComponent c ) {
247         setPopupVisible( comboBox, false);
248         popup.uninstallingUI();
249
250         uninstallKeyboardActions();
251
252         comboBox.setLayout( null );
253
254         uninstallComponents();
255         uninstallListeners();
256         uninstallDefaults();
257
258         if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
259             comboBox.setRenderer( null );
260         }
261         if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
262             if(comboBox.getEditor().getEditorComponent().hasFocus()) {
263                 // Leave focus in JComboBox.
264
comboBox.requestFocusInWindow();
265             }
266             comboBox.setEditor( null );
267         }
268
269         if (keySelectionManager instanceof UIResource) {
270         comboBox.setKeySelectionManager(null);
271     }
272
273         handler = null;
274         keyListener = null;
275         focusListener = null;
276         listDataListener = null;
277         propertyChangeListener = null;
278         popup = null;
279         listBox = null;
280         comboBox = null;
281     }
282
283     /**
284      * Installs the default colors, default font, default renderer, and default
285      * editor into the JComboBox.
286      */

287     protected void installDefaults() {
288         LookAndFeel.installColorsAndFont( comboBox,
289                                           "ComboBox.background",
290                                           "ComboBox.foreground",
291                                           "ComboBox.font" );
292         LookAndFeel.installBorder( comboBox, "ComboBox.border" );
293         LookAndFeel.installProperty( comboBox, "opaque", Boolean.TRUE);
294
295     Long JavaDoc l = (Long JavaDoc)UIManager.get("ComboBox.timeFactor");
296     timeFactor = (l!=null) ? l.longValue() : 1000L;
297     }
298
299     /**
300      * Create and install the listeners for the combo box and its model.
301      * This method is called when the UI is installed.
302      */

303     protected void installListeners() {
304     if ( (itemListener = createItemListener()) != null) {
305         comboBox.addItemListener( itemListener );
306     }
307         if ( (propertyChangeListener = createPropertyChangeListener()) != null ) {
308             comboBox.addPropertyChangeListener( propertyChangeListener );
309         }
310         if ( (keyListener = createKeyListener()) != null ) {
311             comboBox.addKeyListener( keyListener );
312         }
313         if ( (focusListener = createFocusListener()) != null ) {
314             comboBox.addFocusListener( focusListener );
315         }
316     if ((popupMouseListener = popup.getMouseListener()) != null) {
317         comboBox.addMouseListener( popupMouseListener );
318     }
319     if ((popupMouseMotionListener = popup.getMouseMotionListener()) != null) {
320         comboBox.addMouseMotionListener( popupMouseMotionListener );
321     }
322     if ((popupKeyListener = popup.getKeyListener()) != null) {
323         comboBox.addKeyListener(popupKeyListener);
324     }
325
326         if ( comboBox.getModel() != null ) {
327             if ( (listDataListener = createListDataListener()) != null ) {
328                 comboBox.getModel().addListDataListener( listDataListener );
329             }
330         }
331     }
332
333     /**
334      * Uninstalls the default colors, default font, default renderer, and default
335      * editor into the JComboBox.
336      */

337     protected void uninstallDefaults() {
338         LookAndFeel.installColorsAndFont( comboBox,
339                                           "ComboBox.background",
340                                           "ComboBox.foreground",
341                                           "ComboBox.font" );
342         LookAndFeel.uninstallBorder( comboBox );
343     }
344
345     /**
346      * Remove the installed listeners from the combo box and its model.
347      * The number and types of listeners removed and in this method should be
348      * the same that was added in <code>installListeners</code>
349      */

350     protected void uninstallListeners() {
351         if ( keyListener != null ) {
352             comboBox.removeKeyListener( keyListener );
353         }
354     if ( itemListener != null) {
355         comboBox.removeItemListener( itemListener );
356     }
357         if ( propertyChangeListener != null ) {
358             comboBox.removePropertyChangeListener( propertyChangeListener );
359         }
360         if ( focusListener != null) {
361             comboBox.removeFocusListener( focusListener );
362         }
363     if ( popupMouseListener != null) {
364         comboBox.removeMouseListener( popupMouseListener );
365     }
366     if ( popupMouseMotionListener != null) {
367         comboBox.removeMouseMotionListener( popupMouseMotionListener );
368     }
369     if (popupKeyListener != null) {
370         comboBox.removeKeyListener(popupKeyListener);
371     }
372         if ( comboBox.getModel() != null ) {
373             if ( listDataListener != null ) {
374                 comboBox.getModel().removeListDataListener( listDataListener );
375             }
376         }
377     }
378
379     /**
380      * Creates the popup portion of the combo box.
381      *
382      * @return an instance of <code>ComboPopup</code>
383      * @see ComboPopup
384      */

385     protected ComboPopup JavaDoc createPopup() {
386         return new BasicComboPopup JavaDoc( comboBox );
387     }
388
389     /**
390      * Creates a <code>KeyListener</code> which will be added to the
391      * combo box. If this method returns null then it will not be added
392      * to the combo box.
393      *
394      * @return an instance <code>KeyListener</code> or null
395      */

396     protected KeyListener createKeyListener() {
397         return getHandler();
398     }
399
400     /**
401      * Creates a <code>FocusListener</code> which will be added to the combo box.
402      * If this method returns null then it will not be added to the combo box.
403      *
404      * @return an instance of a <code>FocusListener</code> or null
405      */

406     protected FocusListener createFocusListener() {
407         return getHandler();
408     }
409
410     /**
411      * Creates a list data listener which will be added to the
412      * <code>ComboBoxModel</code>. If this method returns null then
413      * it will not be added to the combo box model.
414      *
415      * @return an instance of a <code>ListDataListener</code> or null
416      */

417     protected ListDataListener createListDataListener() {
418         return getHandler();
419     }
420
421     /**
422      * Creates an <code>ItemListener</code> which will be added to the
423      * combo box. If this method returns null then it will not
424      * be added to the combo box.
425      * <p>
426      * Subclasses may override this method to return instances of their own
427      * ItemEvent handlers.
428      *
429      * @return an instance of an <code>ItemListener</code> or null
430      */

431     protected ItemListener createItemListener() {
432         return null;
433     }
434
435     /**
436      * Creates a <code>PropertyChangeListener</code> which will be added to
437      * the combo box. If this method returns null then it will not
438      * be added to the combo box.
439      *
440      * @return an instance of a <code>PropertyChangeListener</code> or null
441      */

442     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
443         return getHandler();
444     }
445
446     /**
447      * Creates a layout manager for managing the components which make up the
448      * combo box.
449      *
450      * @return an instance of a layout manager
451      */

452     protected LayoutManager createLayoutManager() {
453         return getHandler();
454     }
455
456     /**
457      * Creates the default renderer that will be used in a non-editiable combo
458      * box. A default renderer will used only if a renderer has not been
459      * explicitly set with <code>setRenderer</code>.
460      *
461      * @return a <code>ListCellRender</code> used for the combo box
462      * @see javax.swing.JComboBox#setRenderer
463      */

464     protected ListCellRenderer createRenderer() {
465         return new BasicComboBoxRenderer.UIResource JavaDoc();
466     }
467
468     /**
469      * Creates the default editor that will be used in editable combo boxes.
470      * A default editor will be used only if an editor has not been
471      * explicitly set with <code>setEditor</code>.
472      *
473      * @return a <code>ComboBoxEditor</code> used for the combo box
474      * @see javax.swing.JComboBox#setEditor
475      */

476     protected ComboBoxEditor createEditor() {
477         return new BasicComboBoxEditor.UIResource JavaDoc();
478     }
479
480     /**
481      * Returns the shared listener.
482      */

483     private Handler getHandler() {
484         if (handler == null) {
485             handler = new Handler();
486         }
487         return handler;
488     }
489
490     //
491
// end UI Initialization
492
//======================
493

494
495     //======================
496
// begin Inner classes
497
//
498

499     /**
500      * This listener checks to see if the key event isn't a navigation key. If
501      * it finds a key event that wasn't a navigation key it dispatches it to
502      * JComboBox.selectWithKeyChar() so that it can do type-ahead.
503      *
504      * This public inner class should be treated as protected.
505      * Instantiate it only within subclasses of
506      * <code>BasicComboBoxUI</code>.
507      */

508     public class KeyHandler extends KeyAdapter {
509         public void keyPressed( KeyEvent e ) {
510             getHandler().keyPressed(e);
511         }
512     }
513
514     /**
515      * This listener hides the popup when the focus is lost. It also repaints
516      * when focus is gained or lost.
517      *
518      * This public inner class should be treated as protected.
519      * Instantiate it only within subclasses of
520      * <code>BasicComboBoxUI</code>.
521      */

522     public class FocusHandler implements FocusListener {
523         public void focusGained( FocusEvent e ) {
524             getHandler().focusGained(e);
525         }
526
527         public void focusLost( FocusEvent e ) {
528             getHandler().focusLost(e);
529         }
530     }
531
532     /**
533      * This listener watches for changes in the
534      * <code>ComboBoxModel</code>.
535      * <p>
536      * This public inner class should be treated as protected.
537      * Instantiate it only within subclasses of
538      * <code>BasicComboBoxUI</code>.
539      *
540      * @see #createListDataListener
541      */

542     public class ListDataHandler implements ListDataListener {
543         public void contentsChanged( ListDataEvent e ) {
544             getHandler().contentsChanged(e);
545     }
546
547         public void intervalAdded( ListDataEvent e ) {
548             getHandler().intervalAdded(e);
549         }
550
551         public void intervalRemoved( ListDataEvent e ) {
552             getHandler().intervalRemoved(e);
553         }
554     }
555
556     /**
557      * This listener watches for changes to the selection in the
558      * combo box.
559      * <p>
560      * This public inner class should be treated as protected.
561      * Instantiate it only within subclasses of
562      * <code>BasicComboBoxUI</code>.
563      *
564      * @see #createItemListener
565      */

566     public class ItemHandler implements ItemListener {
567     // This class used to implement behavior which is now redundant.
568
public void itemStateChanged(ItemEvent e) {}
569     }
570
571     /**
572      * This listener watches for bound properties that have changed in the
573      * combo box.
574      * <p>
575      * Subclasses which wish to listen to combo box property changes should
576      * call the superclass methods to ensure that the combo box ui correctly
577      * handles property changes.
578      * <p>
579      * This public inner class should be treated as protected.
580      * Instantiate it only within subclasses of
581      * <code>BasicComboBoxUI</code>.
582      *
583      * @see #createPropertyChangeListener
584      */

585     public class PropertyChangeHandler implements PropertyChangeListener JavaDoc {
586         public void propertyChange(PropertyChangeEvent JavaDoc e) {
587             getHandler().propertyChange(e);
588         }
589     }
590
591
592     // Syncronizes the ToolTip text for the components within the combo box to be the
593
// same value as the combo box ToolTip text.
594
private void updateToolTipTextForChildren() {
595         Component[] children = comboBox.getComponents();
596         for ( int i = 0; i < children.length; ++i ) {
597             if ( children[i] instanceof JComponent ) {
598                 ((JComponent)children[i]).setToolTipText( comboBox.getToolTipText() );
599             }
600         }
601     }
602
603     /**
604      * This layout manager handles the 'standard' layout of combo boxes. It puts
605      * the arrow button to the right and the editor to the left. If there is no
606      * editor it still keeps the arrow button to the right.
607      *
608      * This public inner class should be treated as protected.
609      * Instantiate it only within subclasses of
610      * <code>BasicComboBoxUI</code>.
611      */

612     public class ComboBoxLayoutManager implements LayoutManager {
613         public void addLayoutComponent(String JavaDoc name, Component comp) {}
614
615         public void removeLayoutComponent(Component comp) {}
616
617         public Dimension preferredLayoutSize(Container parent) {
618             return getHandler().preferredLayoutSize(parent);
619         }
620
621         public Dimension minimumLayoutSize(Container parent) {
622             return getHandler().minimumLayoutSize(parent);
623         }
624
625         public void layoutContainer(Container parent) {
626             getHandler().layoutContainer(parent);
627         }
628     }
629
630     //
631
// end Inner classes
632
//====================
633

634
635     //===============================
636
// begin Sub-Component Management
637
//
638

639     /**
640      * Creates and initializes the components which make up the
641      * aggregate combo box. This method is called as part of the UI
642      * installation process.
643      */

644     protected void installComponents() {
645         arrowButton = createArrowButton();
646         comboBox.add( arrowButton );
647
648         if (arrowButton != null) {
649             configureArrowButton();
650         }
651
652         if ( comboBox.isEditable() ) {
653             addEditor();
654         }
655
656         comboBox.add( currentValuePane );
657     }
658
659     /**
660      * The aggregate components which compise the combo box are
661      * unregistered and uninitialized. This method is called as part of the
662      * UI uninstallation process.
663      */

664     protected void uninstallComponents() {
665         if ( arrowButton != null ) {
666             unconfigureArrowButton();
667         }
668         if ( editor != null ) {
669             unconfigureEditor();
670         }
671         comboBox.removeAll(); // Just to be safe.
672
arrowButton = null;
673     }
674
675     /**
676      * This public method is implementation specific and should be private.
677      * do not call or override. To implement a specific editor create a
678      * custom <code>ComboBoxEditor</code>
679      *
680      * @see #createEditor
681      * @see javax.swing.JComboBox#setEditor
682      * @see javax.swing.ComboBoxEditor
683      */

684     public void addEditor() {
685         removeEditor();
686         editor = comboBox.getEditor().getEditorComponent();
687         if ( editor != null ) {
688             configureEditor();
689             comboBox.add(editor);
690             if(comboBox.isFocusOwner()) {
691                 // Switch focus to the editor component
692
editor.requestFocusInWindow();
693             }
694         }
695     }
696
697     /**
698      * This public method is implementation specific and should be private.
699      * do not call or override.
700      *
701      * @see #addEditor
702      */

703     public void removeEditor() {
704         if ( editor != null ) {
705             unconfigureEditor();
706             comboBox.remove( editor );
707         editor = null;
708         }
709     }
710
711     /**
712      * This protected method is implementation specific and should be private.
713      * do not call or override.
714      *
715      * @see #addEditor
716      */

717     protected void configureEditor() {
718         // Should be in the same state as the combobox
719
editor.setEnabled(comboBox.isEnabled());
720
721         editor.setFont( comboBox.getFont() );
722
723         if (focusListener != null) {
724             editor.addFocusListener(focusListener);
725         }
726
727     editor.addFocusListener( getHandler() );
728
729     comboBox.getEditor().addActionListener(getHandler());
730
731         if(editor instanceof JComponent) {
732             ((JComponent)editor).putClientProperty("doNotCancelPopup",
733                                                    HIDE_POPUP_KEY);
734             ((JComponent)editor).setInheritsPopupMenu(true);
735         }
736
737         comboBox.configureEditor(comboBox.getEditor(),comboBox.getSelectedItem());
738     }
739
740     /**
741      * This protected method is implementation specific and should be private.
742      * Do not call or override.
743      *
744      * @see #addEditor
745      */

746     protected void unconfigureEditor() {
747         if (focusListener != null) {
748             editor.removeFocusListener(focusListener);
749         }
750
751         editor.removeFocusListener(getHandler());
752         comboBox.getEditor().removeActionListener(getHandler());
753     }
754
755     /**
756      * This public method is implementation specific and should be private. Do
757      * not call or override.
758      *
759      * @see #createArrowButton
760      */

761     public void configureArrowButton() {
762         if ( arrowButton != null ) {
763             arrowButton.setEnabled( comboBox.isEnabled() );
764             arrowButton.setRequestFocusEnabled(false);
765         arrowButton.addMouseListener( popup.getMouseListener() );
766         arrowButton.addMouseMotionListener( popup.getMouseMotionListener() );
767             arrowButton.resetKeyboardActions();
768             arrowButton.putClientProperty("doNotCancelPopup", HIDE_POPUP_KEY);
769             arrowButton.setInheritsPopupMenu(true);
770         }
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     /**
787      * Creates an button which will be used as the control to show or hide
788      * the popup portion of the combo box.
789      *
790      * @return a button which represents the popup control
791      */

792     protected JButton createArrowButton() {
793         JButton button = new BasicArrowButton JavaDoc(BasicArrowButton.SOUTH,
794                     UIManager.getColor("ComboBox.buttonBackground"),
795                     UIManager.getColor("ComboBox.buttonShadow"),
796                     UIManager.getColor("ComboBox.buttonDarkShadow"),
797                     UIManager.getColor("ComboBox.buttonHighlight"));
798         button.setName("ComboBox.arrowButton");
799         return button;
800     }
801
802     //
803
// end Sub-Component Management
804
//===============================
805

806
807     //================================
808
// begin ComboBoxUI Implementation
809
//
810

811     /**
812      * Tells if the popup is visible or not.
813      */

814     public boolean isPopupVisible( JComboBox c ) {
815         return popup.isVisible();
816     }
817
818     /**
819      * Hides the popup.
820      */

821     public void setPopupVisible( JComboBox c, boolean v ) {
822         if ( v ) {
823             popup.show();
824         } else {
825             popup.hide();
826         }
827     }
828
829     /**
830      * Determines if the JComboBox is focus traversable. If the JComboBox is editable
831      * this returns false, otherwise it returns true.
832      */

833     public boolean isFocusTraversable( JComboBox c ) {
834         return !comboBox.isEditable();
835     }
836
837     //
838
// end ComboBoxUI Implementation
839
//==============================
840

841
842     //=================================
843
// begin ComponentUI Implementation
844

845     public void paint( Graphics g, JComponent c ) {
846         hasFocus = comboBox.hasFocus();
847         if ( !comboBox.isEditable() ) {
848             Rectangle r = rectangleForCurrentValue();
849             paintCurrentValueBackground(g,r,hasFocus);
850             paintCurrentValue(g,r,hasFocus);
851         }
852     }
853
854     public Dimension getPreferredSize( JComponent c ) {
855     return getMinimumSize(c);
856     }
857
858     /**
859      * The minumum size is the size of the display area plus insets plus the button.
860      */

861     public Dimension getMinimumSize( JComponent c ) {
862         if ( !isMinimumSizeDirty ) {
863             return new Dimension(cachedMinimumSize);
864         }
865         Dimension size = getDisplaySize();
866         Insets insets = getInsets();
867         size.height += insets.top + insets.bottom;
868         int buttonSize = size.height - (insets.top + insets.bottom);
869         size.width += insets.left + insets.right + buttonSize;
870
871         cachedMinimumSize.setSize( size.width, size.height );
872         isMinimumSizeDirty = false;
873
874         return new Dimension(size);
875     }
876
877     public Dimension getMaximumSize( JComponent c ) {
878     return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
879     }
880
881     // This is currently hacky...
882
public int getAccessibleChildrenCount(JComponent c) {
883         if ( comboBox.isEditable() ) {
884             return 2;
885         }
886         else {
887             return 1;
888         }
889     }
890
891     // This is currently hacky...
892
public Accessible getAccessibleChild(JComponent c, int i) {
893         // 0 = the popup
894
// 1 = the editor
895
switch ( i ) {
896         case 0:
897             if ( popup instanceof Accessible ) {
898                 AccessibleContext ac = ((Accessible) popup).getAccessibleContext();
899                 ac.setAccessibleParent(comboBox);
900                 return(Accessible) popup;
901             }
902             break;
903         case 1:
904             if ( comboBox.isEditable()
905                  && (editor instanceof Accessible) ) {
906                 AccessibleContext ac = ((Accessible) editor).getAccessibleContext();
907                 ac.setAccessibleParent(comboBox);
908                 return(Accessible) editor;
909             }
910             break;
911         }
912         return null;
913     }
914
915     //
916
// end ComponentUI Implementation
917
//===============================
918

919
920     //======================
921
// begin Utility Methods
922
//
923

924     /**
925      * Returns whether or not the supplied keyCode maps to a key that is used for
926      * navigation. This is used for optimizing key input by only passing non-
927      * navigation keys to the type-ahead mechanism. Subclasses should override this
928      * if they change the navigation keys.
929      */

930     protected boolean isNavigationKey( int keyCode ) {
931         return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN ||
932                keyCode == KeyEvent.VK_KP_UP || keyCode == KeyEvent.VK_KP_DOWN;
933     }
934
935     private boolean isNavigationKey(int keyCode, int modifiers) {
936     InputMap inputMap = comboBox.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
937     KeyStroke key = KeyStroke.getKeyStroke(keyCode, modifiers);
938     
939     if (inputMap != null && inputMap.get(key) != null) {
940         return true;
941     }
942     return false;
943     }
944
945     /**
946      * Selects the next item in the list. It won't change the selection if the
947      * currently selected item is already the last item.
948      */

949     protected void selectNextPossibleValue() {
950         int si;
951
952         if ( isTableCellEditor ) {
953             si = listBox.getSelectedIndex();
954         }
955         else {
956             si = comboBox.getSelectedIndex();
957         }
958
959         if ( si < comboBox.getModel().getSize() - 1 ) {
960             if ( isTableCellEditor ) {
961                 listBox.setSelectedIndex( si + 1 );
962                 listBox.ensureIndexIsVisible( si + 1 );
963             }
964             else {
965                 comboBox.setSelectedIndex(si+1);
966             }
967             comboBox.repaint();
968         }
969     }
970
971     /**
972      * Selects the previous item in the list. It won't change the selection if the
973      * currently selected item is already the first item.
974      */

975     protected void selectPreviousPossibleValue() {
976         int si;
977
978         if ( isTableCellEditor ) {
979             si = listBox.getSelectedIndex();
980         }
981         else {
982             si = comboBox.getSelectedIndex();
983         }
984
985         if ( si > 0 ) {
986             if ( isTableCellEditor ) {
987                 listBox.setSelectedIndex( si - 1 );
988                 listBox.ensureIndexIsVisible( si - 1 );
989             }
990             else {
991                 comboBox.setSelectedIndex(si-1);
992             }
993
994             comboBox.repaint();
995         }
996     }
997
998     /**
999      * Hides the popup if it is showing and shows the popup if it is hidden.
1000     */

1001    protected void toggleOpenClose() {
1002        setPopupVisible(comboBox, !isPopupVisible(comboBox));
1003    }
1004
1005    /**
1006     * Returns the area that is reserved for drawing the currently selected item.
1007     */

1008    protected Rectangle rectangleForCurrentValue() {
1009        int width = comboBox.getWidth();
1010        int height = comboBox.getHeight();
1011        Insets insets = getInsets();
1012        int buttonSize = height - (insets.top + insets.bottom);
1013    if ( arrowButton != null ) {
1014            buttonSize = arrowButton.getWidth();
1015    }
1016    if(BasicGraphicsUtils.isLeftToRight(comboBox)) {
1017        return new Rectangle(insets.left, insets.top,
1018     &nbs