home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / JComboBox.java < prev    next >
Text File  |  1998-02-26  |  42KB  |  1,288 lines

  1. /*
  2.  * @(#)JComboBox.java    1.37 98/02/12
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing;
  21.  
  22. import java.beans.*;
  23. import java.util.*;
  24. import java.awt.*;
  25. import java.awt.event.*;
  26. import com.sun.java.swing.event.*;
  27. import com.sun.java.swing.plaf.*;
  28. import java.io.Serializable;
  29. import com.sun.java.swing.border.*;
  30. import com.sun.java.accessibility.*;
  31.  
  32. /**
  33.  * Swing's implementation of a ComboBox -- a combination of a text field and 
  34.  * drop-down list that lets the user either type in a value or select it from 
  35.  * a list that is displayed when the user asks for it. The editing capability 
  36.  * can also be disabled so that the JComboBox acts only as a drop down list.
  37.  * <p>
  38.  * Warning: serialized objects of this class will not be compatible with
  39.  * future swing releases.  The current serialization support is appropriate 
  40.  * for short term storage or RMI between Swing1.0 applications.  It will
  41.  * not be possible to load serialized Swing1.0 objects with future releases
  42.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  43.  * baseline for the serialized form of Swing objects.
  44.  *
  45.  * @beaninfo
  46.  *   attribute: isContainer false
  47.  *
  48.  * @version 1.37 02/12/98
  49.  * @author Arnaud Weber
  50.  */
  51. public class JComboBox extends JComponent implements ItemSelectable,ListDataListener,ActionListener, Accessible {
  52.     protected ComboBoxModel    dataModel;
  53.     protected ListCellRenderer renderer;
  54.     protected ComboBoxEditor       editor;
  55.     protected int maximumRowCount = 8;
  56.     protected boolean isEditable  = false;
  57.     protected Object selectedItemReminder = null;
  58.     protected KeySelectionManager keySelectionManager = null;
  59.     protected String actionCommand = "comboBoxChanged";
  60.     protected boolean lightWeightPopupEnabled = true;
  61.  
  62.     /**
  63.      * Creates a JComboBox that takes its items from an existing ComboBoxDataModel.
  64.      *
  65.      * @param aModel the ComboBoxModel that provides the displayed list of items
  66.      */
  67.     public JComboBox(ComboBoxModel aModel) {
  68.         super();
  69.         setModel(aModel);
  70.         updateUI();
  71.         addAncestorListener(new AncestorListener() {
  72.             public void ancestorAdded(AncestorEvent event) { hidePopup(); }
  73.             public void ancestorRemoved(AncestorEvent event) { hidePopup(); }
  74.             public void ancestorMoved(AncestorEvent event) { hidePopup(); }
  75.         });
  76.  
  77.     }
  78.  
  79.     /** 
  80.      * Creates a JComboBox that contains the elements in the specified array.
  81.      */
  82.     public JComboBox(final Object items[]) {
  83.         setModel(new DefaultComboBoxModel(items));
  84.         updateUI();
  85.     }
  86.  
  87.     /**
  88.      * Creates a JComboBox that contains the elements in the specified Vector.
  89.      */
  90.     public JComboBox(Vector items) {
  91.         setModel(new DefaultComboBoxModel(items));
  92.         updateUI();
  93.     }
  94.  
  95.     /**
  96.      * Creates a JComboBox with a default data model.
  97.      * The default data model is an empty list of objects. Use <code>addItem</code> 
  98.      * to add items.
  99.      */
  100.     public JComboBox() {
  101.         setModel(new DefaultComboBoxModel());
  102.         updateUI();
  103.     }
  104.  
  105.     /**
  106.      * Sets the L&F object that renders this component.
  107.      *
  108.      * @param ui  the ComboBoxUI L&F object
  109.      * @see UIDefaults#getUI
  110.      *
  111.      * @beaninfo
  112.      *       expert: true
  113.      *  description: The ComboBoxUI implementation that defines the combo box look and feel.
  114.      */
  115.     public void setUI(ComboBoxUI ui) {
  116.         super.setUI(ui);
  117.     }
  118.  
  119.     /**
  120.      * Notification from the UIFactory that the L&F has changed. 
  121.      *
  122.      * @see JComponent#updateUI
  123.      */
  124.     public void updateUI() {
  125.         setUI((ComboBoxUI)UIManager.getUI(this));
  126.     }
  127.  
  128.  
  129.     /**
  130.      * Returns the name of the L&F class that renders this component.
  131.      *
  132.      * @return "ComboBoxUI"
  133.      * @see JComponent#getUIClassID
  134.      * @see UIDefaults#getUI
  135.      */
  136.     public String getUIClassID() {
  137.         return "ComboBoxUI";
  138.     }
  139.  
  140.  
  141.     /**
  142.      * Returns the L&F object that renders this component.
  143.      *
  144.      * @return the ComboBoxUI object that renders this component
  145.      */
  146.     public ComboBoxUI getUI() {
  147.         return (ComboBoxUI)ui;
  148.     }
  149.  
  150.     /**
  151.      * Sets the data model that the JComboBox uses to obtain the list of items.
  152.      *
  153.      * @param aModel the ComboBoxModel that provides the displayed list of items
  154.      * 
  155.      * @beaninfo
  156.      *        bound: true
  157.      *  description: Model that the combo box uses to get data to display.
  158.      */
  159.     public void setModel(ComboBoxModel aModel) {
  160.         ComboBoxModel oldModel = dataModel;
  161.         if(dataModel != null)
  162.             dataModel.removeListDataListener(this);
  163.         dataModel = aModel;
  164.         firePropertyChange("model",oldModel,dataModel);
  165.         dataModel.addListDataListener(this);
  166.         invalidate();
  167.     }
  168.  
  169.     /**
  170.      * Returns the data model currently used by the JComboBox.
  171.      *
  172.      * @return the ComboBoxModel that provides the displayed list of items
  173.      */
  174.     public ComboBoxModel getModel() {
  175.         return dataModel;
  176.     }
  177.  
  178.     /*
  179.      * Properties
  180.      */
  181.  
  182.     /**
  183.      * When displaying the popup, JComboBox choose to use a light weight popup if
  184.      * it fits. This method allows you to disable this feature. You have to do disable
  185.      * it if your application mixes light weight and heavy weights components.
  186.      *
  187.      * @beaninfo
  188.      *       expert: true
  189.      *  description: When set, disables using light weight popups.
  190.      */
  191.     public void setLightWeightPopupEnabled(boolean aFlag) {
  192.         lightWeightPopupEnabled = aFlag;
  193.     }
  194.  
  195.     /**
  196.      * Returns true if lightweight (all-Java) popups are in use,
  197.      * or false if heavyweight (native peer) popups are being used.
  198.      *
  199.      * @return true if lightweight popups are in use
  200.      */
  201.     public boolean isLightWeightPopupEnabled() { 
  202.         return lightWeightPopupEnabled;
  203.     }
  204.  
  205.     /**
  206.      * Determines whether the JComboBox field is editable. An editable JComboBox
  207.      * allows the user to type into the field or selected an item from the list
  208.      * to initialize the field, after which it can be edited. (The editing affects
  209.      * only the field, the list item remains intact.) A non editable JComboBox 
  210.      * displays the selected item inthe field, but the selection cannot be modified.
  211.      *
  212.      * @param aFlag a boolean value, where true indicates that the field is editable
  213.      * 
  214.      * @beaninfo
  215.      *    preferred: true
  216.      *  description: If true, the user can type a new value in the combo box.
  217.      */
  218.     public void setEditable(boolean aFlag) {
  219.         isEditable = aFlag;
  220.         getUI().editablePropertyChanged();
  221.     }
  222.  
  223.     /**
  224.      * Returns true if the JComboBox is editable.
  225.      * 
  226.      * @return true if the JComboBox is editable, else false
  227.      */
  228.     public boolean isEditable() {
  229.         return isEditable;
  230.     }
  231.  
  232.     /**
  233.      * Sets the maximum number of rows the JComboBox displays.
  234.      * If the number of objects in the model is greater than count,
  235.      * the combo box uses a scrollbar.
  236.      *
  237.      * @param count an int specifying the maximum number of items to display
  238.      *              in the list before using a scrollbar
  239.      * @beaninfo
  240.      *    preferred: true
  241.      *  description: The maximum number of rows the popup should have
  242.      */
  243.     public void setMaximumRowCount(int count) {
  244.         ComboBoxUI ui = getUI();
  245.         maximumRowCount = count;
  246.         ui.maximumRowCountChanged();
  247.     }
  248.  
  249.     /**
  250.      * Returns the maximum number of items the combo box can display 
  251.      * without a scrollbar
  252.      *
  253.      * @return an int specifying the maximum number of items that are displayed
  254.      *         in the list before using a scrollbar
  255.      */
  256.     public int getMaximumRowCount() {
  257.         return maximumRowCount;
  258.     }
  259.  
  260.     /**
  261.      * Sets the renderer that paints the item selected from the list in
  262.      * the JComboBox field. The renderer is used if the JComboBox is not
  263.      * editable. If it is editable, the editor is used to render and edit
  264.      * the selected item.
  265.      * <p>
  266.      * The default renderer displays a string, obtained
  267.      * by calling the selected object's <code>toString</code> method.
  268.      * Other renderers can handle graphic images and composite items.
  269.      * <p>
  270.      * To display the selected item, <code>aRenderer.getListCellRendererComponent</code>
  271.      * is called, passing the list object and an index of -1.
  272.      *  
  273.      * @param aRenderer  the ListCellRenderer that displays the selected item.
  274.      * @see #setEditor
  275.      * @beaninfo
  276.      *     expert: true
  277.      *  description: The renderer that paints the item selected in the list.
  278.      */
  279.     public void setRenderer(ListCellRenderer aRenderer) {
  280.         renderer = aRenderer;
  281.         invalidate();
  282.     }
  283.  
  284.     /**
  285.      * Returns the renderer used to display the selected item in the JComboBox
  286.      * field.
  287.      *  
  288.      * @return  the ListCellRenderer that displays the selected item.
  289.      */
  290.     public ListCellRenderer getRenderer() {
  291.         return renderer;
  292.     }
  293.  
  294.     /**
  295.      * Sets the editor used to paint and edit the selected item in the JComboBox
  296.      * field. The editor is used only if the receiving JComboBox is editable. 
  297.      * If not editable, the combo box uses the renderer to paint the selected item.
  298.      *  
  299.      * @param anEditor  the ComboBoxEditor that displays the selected item
  300.      * @see #setRenderer
  301.      * @beaninfo
  302.      *    expert: true
  303.      *  description: The editor that combo box uses to edit the current value
  304.      */
  305.     public void setEditor(ComboBoxEditor anEditor) {
  306.         if(editor != null)
  307.             editor.removeActionListener(this);
  308.         editor = anEditor;
  309.         if(editor != null) {
  310.           editor.addActionListener(this);
  311.         }
  312.         getUI().editablePropertyChanged();
  313.     }
  314.  
  315.     /**
  316.      * Returns the editor used to paint and edit the selected item in the JComboBox
  317.      * field.
  318.      *  
  319.      * @return the ComboBoxEditor that displays the selected item
  320.      */
  321.     public ComboBoxEditor getEditor() {
  322.         return editor;
  323.     }
  324.  
  325.     /*
  326.      * Selection
  327.      */
  328.     /** 
  329.      * Sets the selected item in the JComboBox by specifying the object in the list.
  330.      * If <code>anObject</code> is in the list, the list displays with 
  331.      * <code>anObject</code> selected. If the object does not exist in the list,
  332.      * the default data model selects the first item in the list.
  333.      *
  334.      * @param anObject  the list object to select
  335.      * @beaninfo
  336.      *    preferred:   true
  337.      *    description: Sets the selected item in the JComboBox.
  338.      */
  339.     public void setSelectedItem(Object anObject) {
  340.         dataModel.setSelectedItem(anObject);
  341.     }
  342.  
  343.     /**
  344.      * Returns the currently selected item.
  345.      *
  346.      * @return  the currently selected list object from the data model
  347.      */
  348.     public Object getSelectedItem() {
  349.         return dataModel.getSelectedItem();
  350.     }
  351.  
  352.     /**
  353.      * Selects the item at index <code>anIndex</code>.
  354.      *
  355.      * @param anIndex an int specifying the list item to select, where 0 specifies
  356.      *                the first item in the list
  357.      * @beaninfo
  358.      *   preferred: true
  359.      *  description: The item at index is selected.
  360.      */
  361.     public void setSelectedIndex(int anIndex) {
  362.         int size = dataModel.getSize();
  363.  
  364.         if(anIndex < 0 || anIndex >= size)
  365.             throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
  366.         setSelectedItem(dataModel.getElementAt(anIndex));
  367.     }
  368.  
  369.     /**
  370.      * Returns the index of the currently selected item in the list. The result is not
  371.      * always defined if the JComboBox box allows selected items that are not in the
  372.      * list. Returns -1 if the receiving JComboBox has no selected item or if the 
  373.      * selected item is not in the list of items.
  374.      
  375.      * @return an int specifying the currently selected list item, where 0 specifies
  376.      *                the first item in the list, or -1 if no item is selected or if
  377.      *                the currently selected item is not in the list
  378.      */
  379.     public int getSelectedIndex() {
  380.         Object sObject = dataModel.getSelectedItem();
  381.         int i,c;
  382.         Object obj;
  383.  
  384.         for(i=0,c=dataModel.getSize();i<c;i++) {
  385.             obj = dataModel.getElementAt(i);
  386.             if(obj.equals(sObject))
  387.                 return i;
  388.         }
  389.         return -1;
  390.     }
  391.  
  392.     /** 
  393.      * Adds an item to the item list.
  394.      * This method works only if the JComboBox uses the default data model.
  395.      * JComboBox uses the default data model when created with the 
  396.      * empty constructor and no other model has been set.
  397.      *
  398.      * @param anObject the Object to add to the list
  399.      */
  400.     public void addItem(Object anObject) {
  401.         checkDefaultComboBoxModel();
  402.         ((DefaultComboBoxModel)dataModel).addObject(anObject);
  403.     }
  404.  
  405.     /** 
  406.      * Inserts an item into the item list at a given index. 
  407.      *
  408.      * @param anObject the Object to add to the list
  409.      * @param index    an int specifying the position at which to add the item
  410.      */
  411.     public void insertItemAt(Object anObject, int index) {
  412.         checkDefaultComboBoxModel();
  413.         ((DefaultComboBoxModel)dataModel).insertObjectAt(anObject,index);
  414.     }
  415.                                                                  
  416.     /** 
  417.      * Removes an item from the item list.
  418.      * This method works only if the JComboBox uses the default data model.
  419.      * JComboBox uses the default data model when created with the empty constructor
  420.      * and no other model has been set.
  421.      *
  422.      * @param anObject  the object to remove from the item list
  423.      */
  424.     public void removeItem(Object anObject) {
  425.         int index;
  426.         checkDefaultComboBoxModel();
  427.         index = ((DefaultComboBoxModel)dataModel).getIndexOf(anObject);
  428.         ((DefaultComboBoxModel)dataModel).removeObject(anObject);
  429.         if(!isEditable() && dataModel.getSize() > 0 && index < dataModel.getSize()) {
  430.             setSelectedIndex(index);
  431.         }
  432.     }
  433.  
  434.     /**  
  435.      * Removes the item at <code>anIndex</code>
  436.      * @param anIndex  an int specifying the idex of the item to remove, where 0
  437.      *                 indicates the first item in the list
  438.      */
  439.     public void removeItemAt(int anIndex) {
  440.         checkDefaultComboBoxModel();
  441.         ((DefaultComboBoxModel)dataModel).removeObjectAt(anIndex);
  442.     }
  443.  
  444.     /** 
  445.      * Removes all items from the item list.
  446.      * This method works only if the JComboBox uses the default data model.
  447.      * JComboBox uses the default data model when created with the empty constructor
  448.      * and no other model has been set.
  449.      */
  450.     public void removeAllItems() {
  451.         checkDefaultComboBoxModel();
  452.         ((DefaultComboBoxModel)dataModel).removeAllObjects();
  453.         if(!isEditable())
  454.             setSelectedItem(null);
  455.     }
  456.  
  457.     void checkDefaultComboBoxModel() {
  458.         if(!(dataModel instanceof DefaultComboBoxModel))
  459.             throw new InternalError("Cannot use this method with a custom data model.");
  460.     }
  461.  
  462.     /** Causes the combo box to display its popup window **/
  463.     public void showPopup() {
  464.         getUI().showPopup();
  465.     }
  466.  
  467.     /** Causes the combo box to close its popup window **/
  468.     public void hidePopup() {
  469.         getUI().hidePopup();
  470.     }
  471.  
  472.     /** Selection **/
  473.  
  474.     /** 
  475.      * Adds an ItemListener. <code>aListener</code> will receive an event when
  476.      * the selected item changes.
  477.      *
  478.      * @param aListener  the ItemListener that is to be notified
  479.      */
  480.     public void addItemListener(ItemListener aListener) {
  481.         listenerList.add(ItemListener.class,aListener);
  482.     }
  483.     
  484.     /** Removes an ItemListener
  485.      *
  486.      * @param aListener  the ItemListener to remove
  487.      */
  488.     public void removeItemListener(ItemListener aListener) {
  489.         listenerList.remove(ItemListener.class,aListener);
  490.     }
  491.  
  492.     /** 
  493.      * Adds an ActionListener. The listener will receive an action event
  494.      * the user finishes making a selection.
  495.      *
  496.      * @param l  the ActionListener that is to be notified
  497.      */
  498.     public void addActionListener(ActionListener l) {
  499.         listenerList.add(ActionListener.class,l);
  500.     }
  501.  
  502.     /** Removes an ActionListener 
  503.      *
  504.      * @param l  the ActionListener to remove
  505.      */
  506.     public void removeActionListener(ActionListener l) {
  507.         listenerList.remove(ActionListener.class,l);
  508.     }
  509.  
  510.     /** 
  511.      * Sets the action commnand that should be included in the event
  512.      * sent to action listeners.
  513.      *
  514.      * @param aCommand  a string containing the "command" that is sent
  515.      *                  to action listeners. The same listener can then
  516.      *                  do different things depending on the command it
  517.      *                  receives.
  518.      */
  519.     public void setActionCommand(String aCommand) {
  520.         actionCommand = aCommand;
  521.     }
  522.  
  523.     /** 
  524.      * Returns the action commnand that is included in the event sent to
  525.      *  action listeners.
  526.      *
  527.      * @return  the string containing the "command" that is sent
  528.      *          to action listeners.
  529.      */
  530.     public String getActionCommand() {
  531.         return actionCommand;
  532.     }
  533.     
  534.     /**
  535.      * Notify all listeners that have registered interest for
  536.      * notification on this event type.
  537.      *  
  538.      * @see EventListenerList
  539.      */
  540.     protected void fireItemStateChanged(ItemEvent e) {
  541.         // Guaranteed to return a non-null array
  542.         Object[] listeners = listenerList.getListenerList();
  543.         // Process the listeners last to first, notifying
  544.         // those that are interested in this event
  545.         for (int i = listeners.length-2; i>=0; i-=2) {
  546.             if (listeners[i]==ItemListener.class) {
  547.                 // Lazily create the event:
  548.                 // if (changeEvent == null)
  549.                 // changeEvent = new ChangeEvent(this);
  550.                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
  551.             }          
  552.         }
  553.     }   
  554.  
  555.     /**
  556.      * Notify all listeners that have registered interest for
  557.      * notification on this event type.
  558.      *  
  559.      * @see EventListenerList
  560.      */
  561.     protected void fireActionEvent() {
  562.         ActionEvent e = null;
  563.         // Guaranteed to return a non-null array
  564.         Object[] listeners = listenerList.getListenerList();
  565.         // Process the listeners last to first, notifying
  566.         // those that are interested in this event
  567.         for (int i = listeners.length-2; i>=0; i-=2) {
  568.             if (listeners[i]==ActionListener.class) {
  569.                 if(e == null) 
  570.                     e = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,getActionCommand());
  571.                 ((ActionListener)listeners[i+1]).actionPerformed(e);
  572.             }          
  573.         }
  574.     }
  575.  
  576.     /**
  577.      * This method is called when the selected item changes. Its default implementation
  578.      *  notifies the item listeners
  579.      */
  580.     protected void selectedItemChanged() {
  581.         if(selectedItemReminder != null) {
  582.             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  583.                                                             selectedItemReminder,
  584.                                                             ItemEvent.DESELECTED));
  585.         }
  586.  
  587.         selectedItemReminder = getModel().getSelectedItem();
  588.  
  589.         if(selectedItemReminder != null)
  590.             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  591.                                                selectedItemReminder,
  592.                                                ItemEvent.SELECTED));
  593.         fireActionEvent();
  594.     }
  595.  
  596.     /** 
  597.      * Returns an array containing the selected item. This method is implemented for 
  598.      * compatibility with ItemSelectable.
  599.      *
  600.      * @returns an array of Objects containing one element -- the selected item
  601.      */
  602.     public Object[] getSelectedObjects() {
  603.         Object selectedObject = getSelectedItem();
  604.         if(selectedObject == null)
  605.             return new Object[0];
  606.         else {
  607.             Object result[] = new Object[1];
  608.             result[0] = selectedObject;
  609.             return result;
  610.         }
  611.     }
  612.  
  613.     /** This method is public as an implementation side effect. 
  614.      *  do not call or override. 
  615.      */
  616.     public void actionPerformed(ActionEvent e) {
  617.         Object newItem = getEditor().getItem();
  618.         getModel().setSelectedItem(newItem);
  619.         getUI().hidePopup();
  620.     }
  621.  
  622.     /** This method is public as an implementation side effect. 
  623.      *  do not call or override. 
  624.      *
  625.      * @see com.sun.java.swing.event.ListDataListener
  626.      */
  627.     public void contentsChanged(ListDataEvent e) {
  628.         ComboBoxModel mod = getModel();
  629.         Object newSelectedItem = mod.getSelectedItem();
  630.  
  631.         /* If we are using the default model, and we are not editable
  632.          *  let's make sure that we do not have an empty selection
  633.          */
  634.         if(!isEditable() && mod instanceof DefaultComboBoxModel &&
  635.            newSelectedItem == null && getModel().getSize() > 0) {
  636.             setSelectedIndex(0); 
  637.         } else {
  638.             if(selectedItemReminder == null) {
  639.                 if(newSelectedItem != null) 
  640.                     selectedItemChanged();
  641.             } else {
  642.                 if(!selectedItemReminder.equals(newSelectedItem)) 
  643.                     selectedItemChanged();
  644.             }
  645.         }
  646.  
  647.         /* If we are not editable and the selected item is no longer in
  648.          *  the list, select the next one 
  649.          */
  650.         if(!isEditable() && newSelectedItem != null) {
  651.             int i,c;
  652.             boolean shouldResetSelectedItem = true;
  653.             Object o;
  654.             Object selectedItem = mod.getSelectedItem();
  655.  
  656.             for(i=0,c=mod.getSize();i<c;i++) {
  657.                 o = mod.getElementAt(i);
  658.                 if(o.equals(selectedItem)) {
  659.                     shouldResetSelectedItem = false;
  660.                     break;
  661.                 }
  662.             }
  663.  
  664.             if(shouldResetSelectedItem) {
  665.                 if(mod.getSize() > 0)
  666.                     setSelectedIndex(0);
  667.                 else
  668.                     setSelectedItem(null);
  669.             }
  670.         }
  671.     }
  672.  
  673.     /**
  674.      * Selects the list item that correponds to the specified keyboard character
  675.      * and returns true, if there is an item corresponding to that character.
  676.      * Otherwise, returns false.
  677.      *
  678.      * @param keyChar a char, typically this is a keyboard key typed by the user
  679.      */
  680.     public boolean selectWithKeyChar(char keyChar) {
  681.         int index;
  682.  
  683.         if(keySelectionManager == null)
  684.             keySelectionManager = createDefaultKeySelectionManager();
  685.         
  686.         index = keySelectionManager.selectionForKey(keyChar,getModel());
  687.         if(index != -1) {
  688.             setSelectedIndex(index);
  689.             return true;
  690.         } else
  691.             return false;
  692.     }
  693.  
  694.     /**
  695.      * Invoked items have been added to the internal data model.
  696.      * The "interval" includes the first and last values added. 
  697.      *
  698.      * @see com.sun.java.swing.event.ListDataListener
  699.      */
  700.     public void intervalAdded(ListDataEvent e) {
  701.         contentsChanged(e);
  702.     }
  703.  
  704.     /**
  705.      * Invoked when values have been removed from the data model. 
  706.      * The"interval" includes the first and last values removed. 
  707.      *
  708.      * @see com.sun.java.swing.event.ListDataListener
  709.      */
  710.     public void intervalRemoved(ListDataEvent e) {
  711.         contentsChanged(e);
  712.     }
  713.  
  714.     /**
  715.      * Enables the combo box so that items can be selected. When the
  716.      * combo box is disabled, items cannot be selected and values
  717.      * cannot be typed into its field (if it is editable).
  718.      *
  719.      * @param b a boolean value, where true enables the component and
  720.      *          false disables it
  721.      * @beaninfo
  722.      *    preferred: true
  723.      *  description: Whether the combo box is enabled.
  724.      */
  725.     public void setEnabled(boolean b) {
  726.         super.setEnabled(b);
  727.         getUI().enablePropertyChanged();
  728.     }
  729.  
  730.     /**
  731.      * Initializes the editor with the specified item.
  732.      *                                 
  733.      * @param anEditor the ComboBoxEditor that displays the list item in the
  734.      *                 combo box field and allows it to be edited
  735.      * @param anItem   the object to display and edit in the field
  736.      */
  737.     public void configureEditor(ComboBoxEditor anEditor,Object anItem) {
  738.         anEditor.setItem(anItem);
  739.     }
  740.     
  741.     /**
  742.      * Handles KeyEvents, looking for the Tab key. If the Tab key is found,
  743.      * the popup window is closed.
  744.      *
  745.      * @param e  the KeyEvent containing the keyboard key that was pressed  
  746.      */
  747.     public void processKeyEvent(KeyEvent e) {
  748.         if(e.getKeyCode() == KeyEvent.VK_TAB) {
  749.             hidePopup();
  750.         }
  751.         super.processKeyEvent(e);
  752.     }
  753.  
  754.     /**
  755.      * Returns true if the component can receive the focus. In this case,
  756.      * the component returns false if it is editable, so that the Editor
  757.      * object receives the focus, instead of the component.
  758.      *
  759.      * @return true if the component can receive the focus, else false. 
  760.      */
  761.     public boolean isFocusTraversable() {
  762.     return getUI().isFocusTraversable();
  763.     }
  764.  
  765.     /**
  766.      * Sets the object that translates a keyboard character into a list
  767.      * selection. Typically, the first selection with a matching first
  768.      * character becomes the selected item.
  769.      *
  770.      * @beaninfo
  771.      *       expert: true
  772.      *  description: The objects that changes the selection when a key is pressed.
  773.      */
  774.     public void setKeySelectionManager(KeySelectionManager aManager) {
  775.         keySelectionManager = aManager;
  776.     }
  777.  
  778.     /**
  779.      * Returns the list's key-selection manager.
  780.      *
  781.      * @return the KeySelectionManager currently in use
  782.      */
  783.     public KeySelectionManager getKeySelectionManager() {
  784.         return keySelectionManager;
  785.     }
  786.  
  787.     /* Accessing the model */
  788.     /**
  789.      * Returns the number of items in the list.
  790.      *
  791.      * @return an int equal to the number of items in the list
  792.      */
  793.     public int getItemCount() {
  794.         return dataModel.getSize();
  795.     }
  796.  
  797.     /**
  798.      * Returns the list item at the specified index.
  799.      *
  800.      * @param index  an int indicating the list position, where the first
  801.      *               item starts at zero
  802.      * @return the Object at that list position
  803.      */
  804.     public Object getItemAt(int index) {
  805.         return dataModel.getElementAt(index);
  806.     }
  807.  
  808.     /**
  809.      * Returns an instance of the default key-selection manager.
  810.      *
  811.      * @return the KeySelectionManager currently used by the list
  812.      * @see #setKeySelectionManager
  813.      */
  814.     protected KeySelectionManager createDefaultKeySelectionManager() {
  815.         return new DefaultKeySelectionManager();
  816.     }
  817.     
  818.     /**
  819.      * Returns true to indicate that this component paints every pixel
  820.      * in its range. (In other words, it does not have a transparent
  821.      * background or foreground.)
  822.      *
  823.      * @see JComponent#isOpaque
  824.      */
  825.     public boolean isOpaque() {
  826.         return true;
  827.     }
  828.  
  829.     /**
  830.      * Default data model for a combo box.
  831.      * <p>
  832.      * Warning: serialized objects of this class will not be compatible with
  833.      * future swing releases.  The current serialization support is appropriate
  834.      * for short term storage or RMI between Swing1.0 applications.  It will
  835.      * not be possible to load serialized Swing1.0 objects with future releases
  836.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  837.      * baseline for the serialized form of Swing objects.
  838.      */
  839.     class DefaultComboBoxModel extends AbstractListModel implements ComboBoxModel, Serializable
  840.     {
  841.         Vector objects = new Vector();
  842.         Object selectedObject;
  843.  
  844.         public DefaultComboBoxModel() {
  845.         }
  846.         public DefaultComboBoxModel(final Object items[]) {
  847.             int i,c;
  848.             for(i=0,c=items.length;i<c;i++)
  849.                 objects.addElement(items[i]);
  850.         }
  851.  
  852.         public DefaultComboBoxModel(Vector v) {
  853.             int i,c;
  854.             for(i=0,c=v.size();i<c;i++) 
  855.                 objects.addElement(v.elementAt(i));
  856.         }
  857.  
  858.         public void setSelectedItem(Object anObject) {
  859.             selectedObject = anObject;
  860.             fireContentsChanged(this, -1, -1);
  861.         }
  862.  
  863.         public Object getSelectedItem() {
  864.             return selectedObject;
  865.         }
  866.  
  867.         public int getSize() {
  868.             return objects.size();
  869.         }
  870.  
  871.         public Object getElementAt(int index) {
  872.             if(index >= 0 && index < objects.size())
  873.                 return objects.elementAt(index);
  874.             else
  875.                 return null;
  876.         }
  877.  
  878.         public int getIndexOf(Object anObject) {
  879.             return objects.indexOf(anObject);
  880.         }
  881.  
  882.         void addObject(Object anObject) {
  883.             objects.addElement(anObject);
  884.             fireIntervalAdded(this,objects.size()-1, objects.size()-1);
  885.         }
  886.  
  887.         void insertObjectAt(Object anObject,int index) {
  888.             objects.insertElementAt(anObject,index);
  889.             fireIntervalAdded(this, index, index);
  890.         }
  891.  
  892.         void removeObjectAt(int index) {
  893.             objects.removeElementAt(index);
  894.             fireIntervalRemoved(this, index, index);
  895.         }
  896.  
  897.         void removeObject(Object anObject) {
  898.             int index = objects.indexOf(anObject);
  899.             if(index != -1) {
  900.                 removeObjectAt(index);
  901.             }
  902.         }
  903.  
  904.         void removeAllObjects() {
  905.             int firstIndex = 0;
  906.             int lastIndex = objects.size()-1;
  907.             objects.removeAllElements();
  908.             fireIntervalRemoved(this, firstIndex, lastIndex);
  909.         }
  910.     }
  911.  
  912.  
  913.     /**
  914.      * The interface that defines a KeySelectionManager. To qualify as
  915.      * a KeySelectionManager, the class needs to implement the method
  916.      * that identifies the list index given a character and the 
  917.      * combo box data model.
  918.      */
  919.     public interface KeySelectionManager {
  920.         /** Given <code>aKey</code> and the model, returns the row
  921.          *  that should become selected. Return -1 if no match was
  922.          *  found. 
  923.          *
  924.          * @param  aKey  a char value, usually indicating a keyboard key that
  925.          *               was pressed
  926.          * @param aModel a ComboBoxModel -- the component's data model, containing
  927.          *               the list of selectable items 
  928.          * @return an int equal to the selected row, where 0 is the
  929.          *         first item and -1 is none. 
  930.          */
  931.         int selectionForKey(char aKey,ComboBoxModel aModel);
  932.     }
  933.     
  934.     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
  935.         public int selectionForKey(char aKey,ComboBoxModel aModel) {
  936.             int i,c;
  937.             int currentSelection = -1;
  938.             Object selectedItem = aModel.getSelectedItem();
  939.             String v;
  940.             String pattern;
  941.  
  942.             if(selectedItem != null) {
  943.                 selectedItem = selectedItem.toString();
  944.  
  945.                 for(i=0,c=aModel.getSize();i<c;i++) {
  946.                     if(selectedItem.equals(aModel.getElementAt(i).toString())) {
  947.                         currentSelection  =  i;
  948.                         break;
  949.                     }
  950.                         
  951.                 }
  952.             }
  953.  
  954.             pattern = ("" + aKey).toLowerCase();
  955.             aKey = pattern.charAt(0);
  956.  
  957.             for(i = ++currentSelection, c = aModel.getSize() ; i < c ; i++) {
  958.                 v = aModel.getElementAt(i).toString().toLowerCase();
  959.                 if(v.length() > 0 && v.charAt(0) == aKey)
  960.                     return i;
  961.             }
  962.  
  963.             for(i = 0 ; i < currentSelection ; i ++ ) {
  964.                 v = aModel.getElementAt(i).toString().toLowerCase();
  965.                 if(v.length() > 0 && v.charAt(0) == aKey)
  966.                     return i;
  967.             }
  968.             return -1;
  969.         }
  970.     }
  971.  
  972. ///////////////////
  973. // Accessiblity support
  974. ///////////////////
  975.  
  976.     /**
  977.      * Get the AccessibleContext associated with this JComponent
  978.      *
  979.      * @return the AccessibleContext of this JComponent
  980.      */
  981.     public AccessibleContext getAccessibleContext() {
  982.         if (accessibleContext == null) {
  983.             accessibleContext = new AccessibleJComboBox();
  984.         }
  985.         return accessibleContext;
  986.     }
  987.  
  988.     /**
  989.      * The class used to obtain the accessible role for this object.
  990.      * <p>
  991.      * Warning: serialized objects of this class will not be compatible with
  992.      * future swing releases.  The current serialization support is appropriate
  993.      * for short term storage or RMI between Swing1.0 applications.  It will
  994.      * not be possible to load serialized Swing1.0 objects with future releases
  995.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  996.      * baseline for the serialized form of Swing objects.
  997.      */
  998.     protected class AccessibleJComboBox extends AccessibleJComponent {
  999.  
  1000.         /**
  1001.          * Returns the number of accessible children in the object.  If all
  1002.          * of the children of this object implement Accessible, than this
  1003.          * method should return the number of children of this object.  For
  1004.      * the JComboBox, this will return 1 if the JComboBox is not editable
  1005.      * and 2 if the JComboBox is editable and the editor is Accessible.
  1006.          *
  1007.          * @return the number of accessible children in the object.
  1008.          */
  1009.         public int getAccessibleChildrenCount() {
  1010.         if (isEditable() 
  1011.         && (getEditor().getEditorComponent() instanceof Accessible)) {
  1012.         return 2;
  1013.         } else {
  1014.         return 1;
  1015.         }
  1016.         }
  1017.  
  1018.         /**
  1019.          * Return the nth Accessible child of the object.  The JList 
  1020.      * associated with the JComboBox will always be returned for
  1021.      * an index of 0.  If the JComboBox is editable and the editor
  1022.      * is Accessible, the editor will be returned for an index of 1.
  1023.      * Otherwise, null will be returned for all other indeces.
  1024.          *
  1025.          * @param i zero-based index of child
  1026.          * @return the nth Accessible child of the object
  1027.      * @see #isEditable
  1028.      * @see #getEditor
  1029.          */
  1030.         public Accessible getAccessibleChild(int i) {
  1031.             // force the combo box to create its components.  If this
  1032.         // is not done, the returned children can be null.
  1033.         Dimension force = getUI().getPreferredSize(JComboBox.this);
  1034.  
  1035.         Accessible ret = null;
  1036.         if (i == 0) {
  1037.         ret = new AccessibleJComboBoxList(JComboBox.this);
  1038.         } else if ((i == 1) && isEditable()) {
  1039.         Component e = getEditor().getEditorComponent();
  1040.         if (e instanceof Accessible) {
  1041.             ret = (Accessible) e;
  1042.         }
  1043.         }
  1044.         if (ret != null) {
  1045.         AccessibleContext ac = ret.getAccessibleContext();
  1046.         if (ac != null) {
  1047.             ac.setAccessibleParent(JComboBox.this);
  1048.         }
  1049.         }
  1050.         return ret;
  1051.         }
  1052.  
  1053.     protected class AccessibleJComboBoxList extends AccessibleContext
  1054.         implements Accessible, AccessibleComponent {
  1055.         private JComboBox parent = null;
  1056.         private AccessibleContext listAC = null;
  1057.         private AccessibleComponent listACmp = null;
  1058.  
  1059.         public AccessibleJComboBoxList(JComboBox parent) {
  1060.         this.parent = parent;
  1061.         this.setAccessibleParent(parent);
  1062.             listAC = parent.getUI().getList().getAccessibleContext();
  1063.             listACmp = listAC.getAccessibleComponent();
  1064.         }
  1065.  
  1066.             // Accessible Methods
  1067.     
  1068.             public AccessibleContext getAccessibleContext() {
  1069.                 return this;
  1070.             }
  1071.     
  1072.             // AccessibleContext methods
  1073.     
  1074.             public String getAccessibleName() {
  1075.         return listAC.getAccessibleName();
  1076.         }
  1077.  
  1078.             public void setAccessibleName(String s) {
  1079.             listAC.setAccessibleName(s);
  1080.         }
  1081.     
  1082.             public String getAccessibleDescription() {
  1083.         return listAC.getAccessibleDescription();
  1084.         }
  1085.     
  1086.             public void setAccessibleDescription(String s) {
  1087.         listAC.setAccessibleDescription(s);
  1088.             }
  1089.     
  1090.             public AccessibleRole getAccessibleRole() {
  1091.         return listAC.getAccessibleRole();
  1092.             }
  1093.     
  1094.             public AccessibleStateSet getAccessibleStateSet() {
  1095.         return listAC.getAccessibleStateSet();
  1096.             }
  1097.     
  1098.             public int getAccessibleIndexInParent() {
  1099.                 return 0;
  1100.             }
  1101.     
  1102.             public int getAccessibleChildrenCount() {
  1103.         return listAC.getAccessibleChildrenCount();
  1104.         }
  1105.  
  1106.             public Accessible getAccessibleChild(int i) {
  1107.         Accessible a = listAC.getAccessibleChild(i);
  1108.         AccessibleContext ac = a.getAccessibleContext();
  1109.         if (ac != null) {
  1110.             ac.setAccessibleParent(this);
  1111.         }
  1112.         return a;
  1113.             }
  1114.     
  1115.             public Locale getLocale() {
  1116.         return listAC.getLocale();
  1117.             }
  1118.     
  1119.             public void addPropertyChangeListener(PropertyChangeListener l) {
  1120.         listAC.addPropertyChangeListener(l);
  1121.             }
  1122.     
  1123.             public void removePropertyChangeListener(PropertyChangeListener l) {
  1124.         listAC.removePropertyChangeListener(l);
  1125.             }
  1126.     
  1127.         public AccessibleAction getAccessibleAction() {
  1128.                 return listAC.getAccessibleAction();
  1129.         }
  1130.  
  1131.         public AccessibleComponent getAccessibleComponent() {
  1132.                 return this; // to override getBounds() and getLocation...();
  1133.         }
  1134.  
  1135.         public AccessibleSelection getAccessibleSelection() {
  1136.                 return listAC.getAccessibleSelection();
  1137.         }
  1138.  
  1139.         public AccessibleText getAccessibleText() { 
  1140.                 return listAC.getAccessibleText();
  1141.         }
  1142.  
  1143.         public AccessibleValue getAccessibleValue() {
  1144.                 return listAC.getAccessibleValue();
  1145.         }
  1146.  
  1147.  
  1148.             // AccessibleComponent methods
  1149.     
  1150.             public Color getBackground() {
  1151.         return listACmp.getBackground();
  1152.             }
  1153.     
  1154.             public void setBackground(Color c) {
  1155.         listACmp.setBackground(c);
  1156.             }
  1157.     
  1158.             public Color getForeground() {
  1159.         return listACmp.getForeground();
  1160.             }
  1161.     
  1162.             public void setForeground(Color c) {
  1163.         listACmp.setForeground(c);
  1164.             }
  1165.     
  1166.             public Cursor getCursor() {
  1167.         return listACmp.getCursor();
  1168.             }
  1169.     
  1170.             public void setCursor(Cursor c) {
  1171.         listACmp.setCursor(c);
  1172.             }
  1173.     
  1174.             public Font getFont() {
  1175.         return listACmp.getFont();
  1176.             }
  1177.     
  1178.             public void setFont(Font f) {
  1179.         listACmp.setFont(f);
  1180.             }
  1181.     
  1182.             public FontMetrics getFontMetrics(Font f) {
  1183.         return listACmp.getFontMetrics(f);
  1184.             }
  1185.     
  1186.             public boolean isEnabled() {
  1187.         return listACmp.isEnabled();
  1188.             }
  1189.     
  1190.             public void setEnabled(boolean b) {
  1191.         listACmp.setEnabled(b);
  1192.             }
  1193.     
  1194.             public boolean isVisible() {
  1195.         return listACmp.isVisible();
  1196.             }
  1197.     
  1198.             public void setVisible(boolean b) {
  1199.         listACmp.setVisible(b);
  1200.             }
  1201.     
  1202.             public boolean isShowing() {
  1203.         return listACmp.isShowing();
  1204.             }
  1205.     
  1206.             public boolean contains(Point p) {
  1207.         return parent.contains(p);
  1208.             }
  1209.     
  1210.             public Point getLocationOnScreen() {
  1211.         return parent.getLocationOnScreen();
  1212.             }
  1213.     
  1214.             public Point getLocation() {
  1215.         return new Point(0,0);
  1216.             }
  1217.     
  1218.             public void setLocation(Point p) {
  1219.         // nothing
  1220.             }
  1221.                 
  1222.             public Rectangle getBounds() {
  1223.         Rectangle r = parent.getBounds();
  1224.         r.x = 0;
  1225.         r.y = 0;
  1226.         return r;
  1227.             }
  1228.     
  1229.             public void setBounds(Rectangle r) {
  1230.         // nothing
  1231.             }
  1232.     
  1233.             public Dimension getSize() {
  1234.         return parent.getSize();
  1235.             }
  1236.     
  1237.             public void setSize (Dimension d) {
  1238.         // nothing
  1239.             }
  1240.     
  1241.             public Accessible getAccessibleAt(Point p) {
  1242.         return listACmp.getAccessibleAt(p);
  1243.             }
  1244.     
  1245.             public boolean isFocusTraversable() {
  1246.         return listACmp.isFocusTraversable();
  1247.             }
  1248.     
  1249.             public void requestFocus() {
  1250.         parent.requestFocus();
  1251.             }
  1252.     
  1253.             public void addFocusListener(FocusListener l) {
  1254.         listACmp.addFocusListener(l);
  1255.             }
  1256.     
  1257.             public void removeFocusListener(FocusListener l) {
  1258.         listACmp.removeFocusListener(l);
  1259.             }
  1260.         } // inner class AccessibleJComboBoxList
  1261.  
  1262.         /**
  1263.          * Get the state set of this object.
  1264.          *
  1265.          * @return an instance of AccessibleState containing the current state 
  1266.          * of the object
  1267.          * @see AccessibleState
  1268.          */
  1269.         public AccessibleStateSet getAccessibleStateSet() {
  1270.             AccessibleStateSet states = super.getAccessibleStateSet();
  1271.             if (isEditable()) {
  1272.                 states.add(AccessibleState.EDITABLE);
  1273.             }
  1274.             return states;
  1275.         }
  1276.  
  1277.         public Accessible getAccessibleAt(Point p) {
  1278.         Accessible a = getAccessibleChild(1);
  1279.         if (a != null) {
  1280.         return a; // the editor
  1281.         } else {
  1282.             return getAccessibleChild(0); // the list
  1283.         }
  1284.         }
  1285.  
  1286.     } // innerclass AccessibleJComboBox
  1287. }
  1288.