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                 width - (insets.left + insets.right + buttonSize),
1019                             height - (insets.top + insets.bottom));
1020    }
1021    else {
1022        return new Rectangle(insets.left + buttonSize, insets.top,
1023                 width - (insets.left + insets.right + buttonSize),
1024                             height - (insets.top + insets.bottom));
1025    }
1026    }
1027
1028    /**
1029     * Gets the insets from the JComboBox.
1030     */

1031    protected Insets getInsets() {
1032        return comboBox.getInsets();
1033    }
1034
1035    //
1036
// end Utility Methods
1037
//====================
1038

1039
1040    //===============================
1041
// begin Painting Utility Methods
1042
//
1043

1044    /**
1045     * Paints the currently selected item.
1046     */

1047    public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
1048        ListCellRenderer renderer = comboBox.getRenderer();
1049        Component c;
1050
1051        if ( hasFocus && !isPopupVisible(comboBox) ) {
1052            c = renderer.getListCellRendererComponent( listBox,
1053                                                       comboBox.getSelectedItem(),
1054                                                       -1,
1055                                                       true,
1056                                                       false );
1057        }
1058        else {
1059            c = renderer.getListCellRendererComponent( listBox,
1060                                                       comboBox.getSelectedItem(),
1061                                                       -1,
1062                                                       false,
1063                                                       false );
1064            c.setBackground(UIManager.getColor("ComboBox.background"));
1065        }
1066        c.setFont(comboBox.getFont());
1067        if ( hasFocus && !isPopupVisible(comboBox) ) {
1068            c.setForeground(listBox.getSelectionForeground());
1069            c.setBackground(listBox.getSelectionBackground());
1070        }
1071        else {
1072            if ( comboBox.isEnabled() ) {
1073                c.setForeground(comboBox.getForeground());
1074                c.setBackground(comboBox.getBackground());
1075            }
1076            else {
1077                c.setForeground(DefaultLookup.getColor(
1078                         comboBox, this, "ComboBox.disabledForeground", null));
1079                c.setBackground(DefaultLookup.getColor(
1080                         comboBox, this, "ComboBox.disabledBackground", null));
1081            }
1082        }
1083
1084        // Fix for 4238829: should lay out the JPanel.
1085
boolean shouldValidate = false;
1086        if (c instanceof JPanel) {
1087            shouldValidate = true;
1088        }
1089
1090        currentValuePane.paintComponent(g,c,comboBox,bounds.x,bounds.y,
1091                                        bounds.width,bounds.height, shouldValidate);
1092    }
1093
1094    /**
1095     * Paints the background of the currently selected item.
1096     */

1097    public void paintCurrentValueBackground(Graphics g,Rectangle bounds,boolean hasFocus) {
1098        Color t = g.getColor();
1099        if ( comboBox.isEnabled() )
1100            g.setColor(DefaultLookup.getColor(comboBox, this,
1101                                              "ComboBox.background", null));
1102        else
1103            g.setColor(DefaultLookup.getColor(comboBox, this,
1104                                     "ComboBox.disabledBackground", null));
1105        g.fillRect(bounds.x,bounds.y,bounds.width,bounds.height);
1106        g.setColor(t);
1107    }
1108
1109    /**
1110     * Repaint the currently selected item.
1111     */

1112    void repaintCurrentValue() {
1113        Rectangle r = rectangleForCurrentValue();
1114        comboBox.repaint(r.x,r.y,r.width,r.height);
1115    }
1116
1117    //
1118
// end Painting Utility Methods
1119
//=============================
1120

1121
1122    //===============================
1123
// begin Size Utility Methods
1124
//
1125

1126    /**
1127     * Return the default size of an empty display area of the combo box using
1128     * the current renderer and font.
1129     *
1130     * @return the size of an empty display area
1131     * @see #getDisplaySize
1132     */

1133    protected Dimension getDefaultSize() {
1134    // Calculates the height and width using the default text renderer
1135
Dimension d = getSizeForComponent(getDefaultListCellRenderer().getListCellRendererComponent(listBox, " ", -1, false, false));
1136
1137        return new Dimension(d.width, d.height);
1138    }
1139
1140    /**
1141     * Returns the calculated size of the display area. The display area is the
1142     * portion of the combo box in which the selected item is displayed. This
1143     * method will use the prototype display value if it has been set.
1144     * <p>
1145     * For combo boxes with a non trivial number of items, it is recommended to
1146     * use a prototype display value to significantly speed up the display
1147     * size calculation.
1148     *
1149     * @return the size of the display area calculated from the combo box items
1150     * @see javax.swing.JComboBox#setPrototypeDisplayValue
1151     */

1152    protected Dimension getDisplaySize() {
1153        if (!isDisplaySizeDirty) {
1154            return new Dimension(cachedDisplaySize);
1155        }
1156    Dimension result = new Dimension();
1157        
1158        ListCellRenderer renderer = comboBox.getRenderer();
1159        if (renderer == null) {
1160            renderer = new DefaultListCellRenderer();
1161        }
1162
1163        Object JavaDoc prototypeValue = comboBox.getPrototypeDisplayValue();
1164        if (prototypeValue != null) {
1165            // Calculates the dimension based on the prototype value
1166
result = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
1167                                           prototypeValue,
1168                                           -1, false, false));
1169        } else {
1170            // Calculate the dimension by iterating over all the elements in the combo
1171
// box list.
1172
ComboBoxModel model = comboBox.getModel();
1173            int modelSize = model.getSize();
1174        Dimension d;
1175
1176            Component cpn;
1177            
1178            if (modelSize > 0 ) {
1179                for (int i = 0; i < modelSize ; i++ ) {
1180                    // Calculates the maximum height and width based on the largest
1181
// element
1182
d = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
1183                                          model.getElementAt(i),
1184                                          -1, false, false));
1185                    result.width = Math.max(result.width,d.width);
1186                    result.height = Math.max(result.height,d.height);
1187
1188                }
1189            } else {
1190        result = getDefaultSize();
1191        if (comboBox.isEditable()) {
1192            result.width = 100;
1193        }
1194            }
1195        }
1196
1197    if ( comboBox.isEditable() ) {
1198        Dimension d = editor.getPreferredSize();
1199        result.width = Math.max(result.width,d.width);
1200        result.height = Math.max(result.height,d.height);
1201    }
1202        
1203        // Set the cached value
1204
cachedDisplaySize.setSize(result.width, result.height);
1205        isDisplaySizeDirty = false;
1206
1207        return result;
1208    }
1209
1210    /**
1211     * This has been refactored out in hopes that it may be investigated and
1212     * simplified for the next major release. adding/removing
1213     * the component to the currentValuePane and changing the font may be
1214     * redundant operations.
1215     */

1216    private Dimension getSizeForComponent(Component comp) {
1217    currentValuePane.add(comp);
1218    comp.setFont(comboBox.getFont());
1219    Dimension d = comp.getPreferredSize();
1220    currentValuePane.remove(comp);
1221    return d;
1222    }
1223    
1224
1225    //
1226
// end Size Utility Methods
1227
//=============================
1228

1229
1230    //=================================
1231
// begin Keyboard Action Management
1232
//
1233

1234    /**
1235     * Adds keyboard actions to the JComboBox. Actions on enter and esc are already
1236     * supplied. Add more actions as you need them.
1237     */

1238    protected void installKeyboardActions() {
1239    InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1240    SwingUtilities.replaceUIInputMap(comboBox, JComponent.
1241                 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
1242
1243
1244        LazyActionMap.installLazyActionMap(comboBox, BasicComboBoxUI JavaDoc.class,
1245                                           "ComboBox.actionMap");
1246    }
1247
1248    InputMap getInputMap(int condition) {
1249    if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
1250        return (InputMap)DefaultLookup.get(comboBox, this,
1251                                               "ComboBox.ancestorInputMap");
1252    }
1253    return null;
1254    }
1255
1256    boolean isTableCellEditor() {
1257    return isTableCellEditor;
1258    }
1259
1260    /**
1261     * Removes the focus InputMap and ActionMap.
1262     */

1263    protected void uninstallKeyboardActions() {
1264    SwingUtilities.replaceUIInputMap(comboBox, JComponent.
1265                 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1266    SwingUtilities.replaceUIActionMap(comboBox, null);
1267    }
1268
1269
1270    //
1271
// Actions
1272
//
1273
private static class Actions extends UIAction {
1274        private static final String JavaDoc HIDE = "hidePopup";
1275        private static final String JavaDoc DOWN = "selectNext";
1276        private static final String JavaDoc DOWN_2 = "selectNext2";
1277        private static final String JavaDoc TOGGLE = "togglePopup";
1278        private static final String JavaDoc TOGGLE_2 = "spacePopup";
1279        private static final String JavaDoc UP = "selectPrevious";
1280        private static final String JavaDoc UP_2 = "selectPrevious2";
1281        private static final String JavaDoc ENTER = "enterPressed";
1282        private static final String JavaDoc PAGE_DOWN = "pageDownPassThrough";
1283        private static final String JavaDoc PAGE_UP = "pageUpPassThrough";
1284        private static final String JavaDoc HOME = "homePassThrough";
1285        private static final String JavaDoc END = "endPassThrough";
1286
1287        Actions(String JavaDoc name) {
1288            super(name);
1289        }
1290
1291    public void actionPerformed( ActionEvent e ) {
1292            String JavaDoc key = getName();
1293            JComboBox comboBox = (JComboBox)e.getSource();
1294            BasicComboBoxUI JavaDoc ui = (BasicComboBoxUI JavaDoc)BasicLookAndFeel.getUIOfType(
1295                                  comboBox.getUI(), BasicComboBoxUI JavaDoc.class);
1296            if (key == HIDE) {
1297                comboBox.firePopupMenuCanceled();
1298                comboBox.setPopupVisible(false);
1299            }
1300            else if (key == PAGE_DOWN || key == PAGE_UP ||
1301                     key == HOME || key == END) {
1302                int index = getNextIndex(comboBox, key);
1303                if (index >= 0 && index < comboBox.getItemCount()) {
1304                    comboBox.setSelectedIndex(index);
1305                }
1306            }
1307            else if (key == DOWN) {
1308                if (comboBox.isShowing() ) {
1309                    if ( comboBox.isPopupVisible() ) {
1310                        if (ui != null) {
1311                            ui.selectNextPossibleValue();
1312                        }
1313                    } else {
1314                        comboBox.setPopupVisible(true);
1315                    }
1316                }
1317            }
1318            else if (key == DOWN_2) {
1319        // Special case in which pressing the arrow keys will not
1320
// make the popup appear - except for editable combo boxes
1321
// and combo boxes inside a table.
1322
if (comboBox.isShowing() ) {
1323                    if ( (comboBox.isEditable() ||
1324                            (ui != null && ui.isTableCellEditor()))
1325                         && !comboBox.isPopupVisible() ) {
1326                        comboBox.setPopupVisible(true);
1327                    } else {
1328                        if (ui != null) {
1329                            ui.selectNextPossibleValue();
1330                        }
1331                    }
1332                }
1333            }
1334            else if (key == TOGGLE || key == TOGGLE_2) {
1335                if (ui != null && (key == TOGGLE || !comboBox.isEditable())) {
1336                    if ( ui.isTableCellEditor() ) {
1337                        // Forces the selection of the list item if the
1338
// combo box is in a JTable.
1339
comboBox.setSelectedIndex(ui.popup.getList().
1340                                                  getSelectedIndex());
1341                    }
1342                    else {
1343                        comboBox.setPopupVisible(!comboBox.isPopupVisible());
1344                    }
1345                }
1346            }
1347            else if (key == UP) {
1348        if (ui != null) {
1349                    if (ui.isPopupVisible(comboBox)) {
1350                        ui.selectPreviousPossibleValue();
1351                    }
1352                    else if (DefaultLookup.getBoolean(comboBox, ui,
1353                                    "ComboBox.showPopupOnNavigation", false)) {
1354                        ui.setPopupVisible(comboBox, true);
1355                    }
1356                }
1357            }
1358        else if (key == UP_2) {
1359                 // Special case in which pressing the arrow keys will not
1360
// make the popup appear - except for editable combo boxes.
1361
if (comboBox.isShowing() && ui != null) {
1362                     if ( comboBox.isEditable() && !comboBox.isPopupVisible()) {
1363             comboBox.setPopupVisible(true);
1364                     } else {
1365             ui.selectPreviousPossibleValue();
1366                     }
1367                 }
1368             }
1369
1370            else if (key == ENTER) {
1371                if ( ui != null && ui.isTableCellEditor() ) {
1372                    // Forces the selection of the list item if the
1373
// combo box is in a JTable.
1374
comboBox.setSelectedIndex(ui.popup.getList().
1375                                              getSelectedIndex());
1376                }
1377                else {
1378                    if (comboBox.isPopupVisible()) {
1379                        comboBox.setPopupVisible(false);
1380                    } else {
1381                        // Call the default button binding.
1382
// This is a pretty messy way of passing an event through
1383
// to the root pane.
1384
JRootPane root = SwingUtilities.getRootPane(comboBox);
1385                        if (root != null) {
1386                            InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1387                            ActionMap am = root.getActionMap();
1388                            if (im != null && am != null) {
1389                                Object JavaDoc obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
1390                                if (obj != null) {
1391                                    Action action = am.get(obj);
1392                                    if (action != null) {
1393                                        action.actionPerformed(new ActionEvent(
1394                                         root, e.getID(), e.getActionCommand(),
1395                                         e.getWhen(), e.getModifiers()));
1396                                    }
1397                                }
1398                            }
1399                        }
1400                    }
1401                }
1402            }
1403        }
1404
1405        private int getNextIndex(JComboBox comboBox, String JavaDoc key) {
1406            if (key == PAGE_UP) {
1407                int listHeight = comboBox.getMaximumRowCount();
1408                int index = comboBox.getSelectedIndex() - listHeight;
1409                return (index < 0 ? 0: index);
1410            }
1411            else if (key == PAGE_DOWN) {
1412                int listHeight = comboBox.getMaximumRowCount();
1413                int index = comboBox.getSelectedIndex() + listHeight;
1414                int max = comboBox.getItemCount();
1415                return (index < max ? index: max-1);
1416            }
1417            else if (key == HOME) {
1418                return 0;
1419            }
1420            else if (key == END) {
1421                return comboBox.getItemCount() - 1;
1422            }
1423            return comboBox.getSelectedIndex();
1424    }
1425
1426    public boolean isEnabled(Object JavaDoc c) {
1427            if (getName() == HIDE) {
1428                return (c != null && ((JComboBox)c).isPopupVisible());
1429            }
1430            return true;
1431    }
1432    }
1433    //
1434
// end Keyboard Action Management
1435
//===============================
1436

1437
1438    //
1439
// Shared Handler, implements all listeners
1440
//
1441
private class Handler implements ActionListener, FocusListener,
1442                                     KeyListener, LayoutManager,
1443                                     ListDataListener, PropertyChangeListener JavaDoc {
1444        //
1445
// PropertyChangeListener
1446
//
1447
public void propertyChange(PropertyChangeEvent JavaDoc e) {
1448            String JavaDoc propertyName = e.getPropertyName();
1449        JComboBox comboBox = (JComboBox)e.getSource();
1450
1451            if ( propertyName == "model" ) {
1452                ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
1453                ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
1454
1455                if ( oldModel != null && listDataListener != null ) {
1456                    oldModel.removeListDataListener( listDataListener );
1457                }
1458
1459                if ( newModel != null && listDataListener != null ) {
1460                    newModel.addListDataListener( listDataListener );
1461                }
1462
1463                if ( editor != null ) {
1464                    comboBox.configureEditor( comboBox.getEditor(), comboBox.getSelectedItem() );
1465                }
1466                isMinimumSizeDirty = true;
1467                isDisplaySizeDirty = true;
1468                comboBox.revalidate();
1469        comboBox.repaint();
1470            }
1471            else if ( propertyName == "editor" && comboBox.isEditable() ) {
1472                addEditor();
1473                comboBox.revalidate();
1474            }
1475            else if ( propertyName == "editable" ) {
1476                if ( comboBox.isEditable() ) {
1477                    comboBox.setRequestFocusEnabled( false );
1478                    addEditor();
1479                } else {
1480                    comboBox.setRequestFocusEnabled( true );
1481                    removeEditor();
1482                }
1483
1484                updateToolTipTextForChildren();
1485
1486                comboBox.revalidate();
1487            }
1488            else if ( propertyName == "enabled" ) {
1489                boolean enabled = comboBox.isEnabled();
1490                if ( editor != null )
1491                    editor.setEnabled(enabled);
1492                if ( arrowButton != null )
1493                    arrowButton.setEnabled(enabled);
1494                comboBox.repaint();
1495            }
1496            else if ( propertyName == "maximumRowCount" ) {
1497                if ( isPopupVisible( comboBox ) ) {
1498                    setPopupVisible(comboBox, false);
1499                    setPopupVisible(comboBox, true);
1500                }
1501            }
1502            else if ( propertyName == "font" ) {
1503                listBox.setFont( comboBox.getFont() );
1504                if ( editor != null ) {
1505                    editor.setFont( comboBox.getFont() );
1506                }
1507                isMinimumSizeDirty = true;
1508                comboBox.validate();
1509            }
1510            else if ( propertyName == JComponent.TOOL_TIP_TEXT_KEY ) {
1511                updateToolTipTextForChildren();
1512        }
1513            else if ( propertyName == BasicComboBoxUI.IS_TABLE_CELL_EDITOR ) {
1514                Boolean JavaDoc inTable = (Boolean JavaDoc)e.getNewValue();
1515        isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
1516            }
1517        else if (propertyName == "prototypeDisplayValue") {
1518                isMinimumSizeDirty = true;
1519                isDisplaySizeDirty = true;
1520                comboBox.revalidate();
1521            }
1522        else if (propertyName == "renderer") {
1523                isMinimumSizeDirty = true;
1524                isDisplaySizeDirty = true;
1525                comboBox.revalidate();
1526            }
1527        }
1528
1529
1530        //
1531
// KeyListener
1532
//
1533

1534        // This listener checks to see if the key event isn't a navigation
1535
// key. If it finds a key event that wasn't a navigation key it
1536
// dispatches it to JComboBox.selectWithKeyChar() so that it can do
1537
// type-ahead.
1538
public void keyPressed( KeyEvent e ) {
1539        if ( isNavigationKey(e.getKeyCode(), e.getModifiers()) ) {
1540        lastTime = 0L;
1541        } else if ( comboBox.isEnabled() && comboBox.getModel().getSize()!=0 &&
1542            isTypeAheadKey( e ) ) {
1543        time = e.getWhen();
1544                if ( comboBox.selectWithKeyChar(e.getKeyChar()) ) {
1545                    e.consume();
1546                }
1547            }
1548        }
1549
1550        public void keyTyped(KeyEvent e) {
1551        }
1552
1553        public void keyReleased(KeyEvent e) {
1554        }
1555
1556        private boolean isTypeAheadKey( KeyEvent e ) {
1557            return !e.isAltDown() && !e.isControlDown() && !e.isMetaDown();
1558        }
1559
1560        //
1561
// FocusListener
1562
//
1563
// NOTE: The class is added to both the Editor and ComboBox.
1564
// The combo box listener hides the popup when the focus is lost.
1565
// It also repaints when focus is gained or lost.
1566

1567        public void focusGained( FocusEvent e ) {
1568            if (e.getSource() == comboBox.getEditor().getEditorComponent()) {
1569                return;
1570            }
1571            hasFocus = true;
1572            comboBox.repaint();
1573        
1574        if (comboBox.isEditable() && editor != null) {
1575        editor.requestFocus();
1576        }
1577        }
1578
1579        public void focusLost( FocusEvent e ) {
1580            if (e.getSource() == comboBox.getEditor().getEditorComponent()) {
1581                ComboBoxEditor editor = comboBox.getEditor();
1582                Object JavaDoc item = editor.getItem();
1583
1584                if (!e.isTemporary() && item != null &&
1585                    !item.equals( comboBox.getSelectedItem())) {
1586                    comboBox.actionPerformed
1587                        (new ActionEvent(editor, 0, "",
1588                                      EventQueue.getMostRecentEventTime(), 0));
1589                }
1590            }
1591
1592            hasFocus = false;
1593            if (!e.isTemporary()) {
1594                setPopupVisible(comboBox, false);
1595            }
1596            comboBox.repaint();
1597        }
1598
1599        //
1600
// ListDataListener
1601
//
1602

1603        // This listener watches for changes in the ComboBoxModel
1604
public void contentsChanged( ListDataEvent e ) {
1605        if ( !(e.getIndex0() == -1 && e.getIndex1() == -1) ) {
1606        isMinimumSizeDirty = true;
1607        comboBox.revalidate();
1608        }
1609
1610        // set the editor with the selected item since this
1611
// is the event handler for a selected item change.
1612
if (comboBox.isEditable() && editor != null) {
1613        comboBox.configureEditor( comboBox.getEditor(),
1614                      comboBox.getSelectedItem() );
1615        }
1616
1617        comboBox.repaint();
1618    }
1619
1620        public void intervalAdded( ListDataEvent e ) {
1621        isDisplaySizeDirty = true;
1622        contentsChanged( e );
1623        }
1624
1625        public void intervalRemoved( ListDataEvent e ) {
1626        isDisplaySizeDirty = true;
1627            contentsChanged( e );
1628        }
1629
1630        //
1631
// LayoutManager
1632
//
1633

1634        // This layout manager handles the 'standard' layout of combo boxes.
1635
// It puts the arrow button to the right and the editor to the left.
1636
// If there is no editor it still keeps the arrow button to the right.
1637
public void addLayoutComponent(String JavaDoc name, Component comp) {}
1638
1639        public void removeLayoutComponent(Component comp) {}
1640
1641        public Dimension preferredLayoutSize(Container parent) {
1642            return parent.getPreferredSize();
1643        }
1644
1645        public Dimension minimumLayoutSize(Container parent) {
1646            return parent.getMinimumSize();
1647        }
1648
1649        public void layoutContainer(Container parent) {
1650            JComboBox cb = (JComboBox)parent;
1651            int width = cb.getWidth();
1652            int height = cb.getHeight();
1653            
1654            Insets insets = getInsets();
1655            int buttonSize = height - (insets.top + insets.bottom);
1656            Rectangle cvb;
1657
1658            if ( arrowButton != null ) {
1659            if(BasicGraphicsUtils.isLeftToRight(cb)) {
1660            arrowButton.setBounds( width - (insets.right + buttonSize),
1661                       insets.top,
1662                       buttonSize, buttonSize);
1663        }
1664        else {
1665            arrowButton.setBounds( insets.left, insets.top,
1666                       buttonSize, buttonSize);
1667        }
1668            }
1669            if ( editor != null ) {
1670                cvb = rectangleForCurrentValue();
1671                editor.setBounds(cvb);
1672            }
1673        }
1674
1675
1676        //
1677
// ActionListener
1678
//
1679
// Fix for 4515752: Forward the Enter pressed on the
1680
// editable combo box to the default button if the item has
1681
// not changed.
1682

1683    // Note: This could depend on event ordering. The first ActionEvent
1684
// from the editor may be handled by the JComboBox in which case, the
1685
// enterPressed action will always be invoked.
1686
public void actionPerformed(ActionEvent evt) {
1687        Object JavaDoc item = comboBox.getEditor().getItem();
1688
1689        if (item != null && item.equals(comboBox.getSelectedItem())) {
1690        ActionMap am = comboBox.getActionMap();
1691        if (am != null) {
1692            Action action = am.get("enterPressed");
1693            if (action != null) {
1694            action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
1695                                   evt.getActionCommand(),
1696                                   evt.getModifiers()));
1697            }
1698        }
1699        }
1700    }
1701    }
1702
1703    class DefaultKeySelectionManager implements JComboBox.KeySelectionManager, UIResource {
1704    private String JavaDoc prefix = "";
1705    private String JavaDoc typedString = "";
1706
1707        public int selectionForKey(char aKey,ComboBoxModel aModel) {
1708        if (lastTime == 0L) {
1709        prefix = "";
1710        typedString = "";
1711        }
1712        boolean startingFromSelection = true;
1713
1714        int startIndex = comboBox.getSelectedIndex();
1715        if (time - lastTime < timeFactor) {
1716        typedString += aKey;
1717        if((prefix.length() == 1) && (aKey == prefix.charAt(0))) {
1718            // Subsequent same key presses move the keyboard focus to the next
1719
// object that starts with the same letter.
1720
startIndex++;
1721        } else {
1722            prefix = typedString;
1723        }
1724        } else {
1725        startIndex++;
1726        typedString = "" + aKey;
1727        prefix = typedString;
1728        }
1729        lastTime = time;
1730 
1731        if (startIndex < 0 || startIndex >= aModel.getSize()) {
1732        startingFromSelection = false;
1733        startIndex = 0;
1734        }
1735        int index = listBox.getNextMatch(prefix, startIndex,
1736                         Position.Bias.Forward);
1737        if (index < 0 && startingFromSelection) { // wrap
1738
index = listBox.getNextMatch(prefix, 0,
1739                         Position.Bias.Forward);
1740        }
1741        return index;
1742    }
1743    }
1744
1745}
1746
Popular Tags