Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]
    1   /*
    2    * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.swing.plaf.basic;
   27   
   28   import javax.accessibility.AccessibleContext;
   29   import javax.swing;
   30   import javax.swing.border.Border;
   31   import javax.swing.border.LineBorder;
   32   import javax.swing.event;
   33   import java.awt;
   34   import java.awt.event;
   35   import java.beans.PropertyChangeListener;
   36   import java.beans.PropertyChangeEvent;
   37   import java.io.Serializable;
   38   
   39   
   40   /**
   41    * This is a basic implementation of the <code>ComboPopup</code> interface.
   42    *
   43    * This class represents the ui for the popup portion of the combo box.
   44    * <p>
   45    * All event handling is handled by listener classes created with the
   46    * <code>createxxxListener()</code> methods and internal classes.
   47    * You can change the behavior of this class by overriding the
   48    * <code>createxxxListener()</code> methods and supplying your own
   49    * event listeners or subclassing from the ones supplied in this class.
   50    * <p>
   51    * <strong>Warning:</strong>
   52    * Serialized objects of this class will not be compatible with
   53    * future Swing releases. The current serialization support is
   54    * appropriate for short term storage or RMI between applications running
   55    * the same version of Swing.  As of 1.4, support for long term storage
   56    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   57    * has been added to the <code>java.beans</code> package.
   58    * Please see {@link java.beans.XMLEncoder}.
   59    *
   60    * @author Tom Santos
   61    * @author Mark Davidson
   62    */
   63   public class BasicComboPopup extends JPopupMenu implements ComboPopup {
   64       // An empty ListMode, this is used when the UI changes to allow
   65       // the JList to be gc'ed.
   66       private static class EmptyListModelClass implements ListModel,
   67                                                           Serializable {
   68           public int getSize() { return 0; }
   69           public Object getElementAt(int index) { return null; }
   70           public void addListDataListener(ListDataListener l) {}
   71           public void removeListDataListener(ListDataListener l) {}
   72       };
   73   
   74       static final ListModel EmptyListModel = new EmptyListModelClass();
   75   
   76       private static Border LIST_BORDER = new LineBorder(Color.BLACK, 1);
   77   
   78       protected JComboBox                comboBox;
   79       /**
   80        * This protected field is implementation specific. Do not access directly
   81        * or override. Use the accessor methods instead.
   82        *
   83        * @see #getList
   84        * @see #createList
   85        */
   86       protected JList                    list;
   87       /**
   88        * This protected field is implementation specific. Do not access directly
   89        * or override. Use the create method instead
   90        *
   91        * @see #createScroller
   92        */
   93       protected JScrollPane              scroller;
   94   
   95       /**
   96        * As of Java 2 platform v1.4 this previously undocumented field is no
   97        * longer used.
   98        */
   99       protected boolean                  valueIsAdjusting = false;
  100   
  101       // Listeners that are required by the ComboPopup interface
  102   
  103       /**
  104        * Implementation of all the listener classes.
  105        */
  106       private Handler handler;
  107   
  108       /**
  109        * This protected field is implementation specific. Do not access directly
  110        * or override. Use the accessor or create methods instead.
  111        *
  112        * @see #getMouseMotionListener
  113        * @see #createMouseMotionListener
  114        */
  115       protected MouseMotionListener      mouseMotionListener;
  116       /**
  117        * This protected field is implementation specific. Do not access directly
  118        * or override. Use the accessor or create methods instead.
  119        *
  120        * @see #getMouseListener
  121        * @see #createMouseListener
  122        */
  123       protected MouseListener            mouseListener;
  124   
  125       /**
  126        * This protected field is implementation specific. Do not access directly
  127        * or override. Use the accessor or create methods instead.
  128        *
  129        * @see #getKeyListener
  130        * @see #createKeyListener
  131        */
  132       protected KeyListener              keyListener;
  133   
  134       /**
  135        * This protected field is implementation specific. Do not access directly
  136        * or override. Use the create method instead.
  137        *
  138        * @see #createListSelectionListener
  139        */
  140       protected ListSelectionListener    listSelectionListener;
  141   
  142       // Listeners that are attached to the list
  143       /**
  144        * This protected field is implementation specific. Do not access directly
  145        * or override. Use the create method instead.
  146        *
  147        * @see #createListMouseListener
  148        */
  149       protected MouseListener            listMouseListener;
  150       /**
  151        * This protected field is implementation specific. Do not access directly
  152        * or override. Use the create method instead
  153        *
  154        * @see #createListMouseMotionListener
  155        */
  156       protected MouseMotionListener      listMouseMotionListener;
  157   
  158       // Added to the combo box for bound properties
  159       /**
  160        * This protected field is implementation specific. Do not access directly
  161        * or override. Use the create method instead
  162        *
  163        * @see #createPropertyChangeListener
  164        */
  165       protected PropertyChangeListener   propertyChangeListener;
  166   
  167       // Added to the combo box model
  168       /**
  169        * This protected field is implementation specific. Do not access directly
  170        * or override. Use the create method instead
  171        *
  172        * @see #createListDataListener
  173        */
  174       protected ListDataListener         listDataListener;
  175   
  176       /**
  177        * This protected field is implementation specific. Do not access directly
  178        * or override. Use the create method instead
  179        *
  180        * @see #createItemListener
  181        */
  182       protected ItemListener             itemListener;
  183   
  184       /**
  185        * This protected field is implementation specific. Do not access directly
  186        * or override.
  187        */
  188       protected Timer                    autoscrollTimer;
  189       protected boolean                  hasEntered = false;
  190       protected boolean                  isAutoScrolling = false;
  191       protected int                      scrollDirection = SCROLL_UP;
  192   
  193       protected static final int         SCROLL_UP = 0;
  194       protected static final int         SCROLL_DOWN = 1;
  195   
  196   
  197       //========================================
  198       // begin ComboPopup method implementations
  199       //
  200   
  201       /**
  202        * Implementation of ComboPopup.show().
  203        */
  204       public void show() {
  205           setListSelection(comboBox.getSelectedIndex());
  206   
  207           Point location = getPopupLocation();
  208           show( comboBox, location.x, location.y );
  209       }
  210   
  211   
  212       /**
  213        * Implementation of ComboPopup.hide().
  214        */
  215       public void hide() {
  216           MenuSelectionManager manager = MenuSelectionManager.defaultManager();
  217           MenuElement [] selection = manager.getSelectedPath();
  218           for ( int i = 0 ; i < selection.length ; i++ ) {
  219               if ( selection[i] == this ) {
  220                   manager.clearSelectedPath();
  221                   break;
  222               }
  223           }
  224           if (selection.length > 0) {
  225               comboBox.repaint();
  226           }
  227       }
  228   
  229       /**
  230        * Implementation of ComboPopup.getList().
  231        */
  232       public JList getList() {
  233           return list;
  234       }
  235   
  236       /**
  237        * Implementation of ComboPopup.getMouseListener().
  238        *
  239        * @return a <code>MouseListener</code> or null
  240        * @see ComboPopup#getMouseListener
  241        */
  242       public MouseListener getMouseListener() {
  243           if (mouseListener == null) {
  244               mouseListener = createMouseListener();
  245           }
  246           return mouseListener;
  247       }
  248   
  249       /**
  250        * Implementation of ComboPopup.getMouseMotionListener().
  251        *
  252        * @return a <code>MouseMotionListener</code> or null
  253        * @see ComboPopup#getMouseMotionListener
  254        */
  255       public MouseMotionListener getMouseMotionListener() {
  256           if (mouseMotionListener == null) {
  257               mouseMotionListener = createMouseMotionListener();
  258           }
  259           return mouseMotionListener;
  260       }
  261   
  262       /**
  263        * Implementation of ComboPopup.getKeyListener().
  264        *
  265        * @return a <code>KeyListener</code> or null
  266        * @see ComboPopup#getKeyListener
  267        */
  268       public KeyListener getKeyListener() {
  269           if (keyListener == null) {
  270               keyListener = createKeyListener();
  271           }
  272           return keyListener;
  273       }
  274   
  275       /**
  276        * Called when the UI is uninstalling.  Since this popup isn't in the component
  277        * tree, it won't get it's uninstallUI() called.  It removes the listeners that
  278        * were added in addComboBoxListeners().
  279        */
  280       public void uninstallingUI() {
  281           if (propertyChangeListener != null) {
  282               comboBox.removePropertyChangeListener( propertyChangeListener );
  283           }
  284           if (itemListener != null) {
  285               comboBox.removeItemListener( itemListener );
  286           }
  287           uninstallComboBoxModelListeners(comboBox.getModel());
  288           uninstallKeyboardActions();
  289           uninstallListListeners();
  290           // We do this, otherwise the listener the ui installs on
  291           // the model (the combobox model in this case) will keep a
  292           // reference to the list, causing the list (and us) to never get gced.
  293           list.setModel(EmptyListModel);
  294       }
  295   
  296       //
  297       // end ComboPopup method implementations
  298       //======================================
  299   
  300       /**
  301        * Removes the listeners from the combo box model
  302        *
  303        * @param model The combo box model to install listeners
  304        * @see #installComboBoxModelListeners
  305        */
  306       protected void uninstallComboBoxModelListeners( ComboBoxModel model ) {
  307           if (model != null && listDataListener != null) {
  308               model.removeListDataListener(listDataListener);
  309           }
  310       }
  311   
  312       protected void uninstallKeyboardActions() {
  313           // XXX - shouldn't call this method
  314   //        comboBox.unregisterKeyboardAction( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ) );
  315       }
  316   
  317   
  318   
  319       //===================================================================
  320       // begin Initialization routines
  321       //
  322       public BasicComboPopup( JComboBox combo ) {
  323           super();
  324           setName("ComboPopup.popup");
  325           comboBox = combo;
  326   
  327           setLightWeightPopupEnabled( comboBox.isLightWeightPopupEnabled() );
  328   
  329           // UI construction of the popup.
  330           list = createList();
  331           list.setName("ComboBox.list");
  332           configureList();
  333           scroller = createScroller();
  334           scroller.setName("ComboBox.scrollPane");
  335           configureScroller();
  336           configurePopup();
  337   
  338           installComboBoxListeners();
  339           installKeyboardActions();
  340       }
  341   
  342       // Overriden PopupMenuListener notification methods to inform combo box
  343       // PopupMenuListeners.
  344   
  345       protected void firePopupMenuWillBecomeVisible() {
  346           super.firePopupMenuWillBecomeVisible();
  347           comboBox.firePopupMenuWillBecomeVisible();
  348       }
  349   
  350       protected void firePopupMenuWillBecomeInvisible() {
  351           super.firePopupMenuWillBecomeInvisible();
  352           comboBox.firePopupMenuWillBecomeInvisible();
  353       }
  354   
  355       protected void firePopupMenuCanceled() {
  356           super.firePopupMenuCanceled();
  357           comboBox.firePopupMenuCanceled();
  358       }
  359   
  360       /**
  361        * Creates a listener
  362        * that will watch for mouse-press and release events on the combo box.
  363        *
  364        * <strong>Warning:</strong>
  365        * When overriding this method, make sure to maintain the existing
  366        * behavior.
  367        *
  368        * @return a <code>MouseListener</code> which will be added to
  369        * the combo box or null
  370        */
  371       protected MouseListener createMouseListener() {
  372           return getHandler();
  373       }
  374   
  375       /**
  376        * Creates the mouse motion listener which will be added to the combo
  377        * box.
  378        *
  379        * <strong>Warning:</strong>
  380        * When overriding this method, make sure to maintain the existing
  381        * behavior.
  382        *
  383        * @return a <code>MouseMotionListener</code> which will be added to
  384        *         the combo box or null
  385        */
  386       protected MouseMotionListener createMouseMotionListener() {
  387           return getHandler();
  388       }
  389   
  390       /**
  391        * Creates the key listener that will be added to the combo box. If
  392        * this method returns null then it will not be added to the combo box.
  393        *
  394        * @return a <code>KeyListener</code> or null
  395        */
  396       protected KeyListener createKeyListener() {
  397           return null;
  398       }
  399   
  400       /**
  401        * Creates a list selection listener that watches for selection changes in
  402        * the popup's list.  If this method returns null then it will not
  403        * be added to the popup list.
  404        *
  405        * @return an instance of a <code>ListSelectionListener</code> or null
  406        */
  407       protected ListSelectionListener createListSelectionListener() {
  408           return null;
  409       }
  410   
  411       /**
  412        * Creates a list data listener which will be added to the
  413        * <code>ComboBoxModel</code>. If this method returns null then
  414        * it will not be added to the combo box model.
  415        *
  416        * @return an instance of a <code>ListDataListener</code> or null
  417        */
  418       protected ListDataListener createListDataListener() {
  419           return null;
  420       }
  421   
  422       /**
  423        * Creates a mouse listener that watches for mouse events in
  424        * the popup's list. If this method returns null then it will
  425        * not be added to the combo box.
  426        *
  427        * @return an instance of a <code>MouseListener</code> or null
  428        */
  429       protected MouseListener createListMouseListener() {
  430           return getHandler();
  431       }
  432   
  433       /**
  434        * Creates a mouse motion listener that watches for mouse motion
  435        * events in the popup's list. If this method returns null then it will
  436        * not be added to the combo box.
  437        *
  438        * @return an instance of a <code>MouseMotionListener</code> or null
  439        */
  440       protected MouseMotionListener createListMouseMotionListener() {
  441           return getHandler();
  442       }
  443   
  444       /**
  445        * Creates a <code>PropertyChangeListener</code> which will be added to
  446        * the combo box. If this method returns null then it will not
  447        * be added to the combo box.
  448        *
  449        * @return an instance of a <code>PropertyChangeListener</code> or null
  450        */
  451       protected PropertyChangeListener createPropertyChangeListener() {
  452           return getHandler();
  453       }
  454   
  455       /**
  456        * Creates an <code>ItemListener</code> which will be added to the
  457        * combo box. If this method returns null then it will not
  458        * be added to the combo box.
  459        * <p>
  460        * Subclasses may override this method to return instances of their own
  461        * ItemEvent handlers.
  462        *
  463        * @return an instance of an <code>ItemListener</code> or null
  464        */
  465       protected ItemListener createItemListener() {
  466           return getHandler();
  467       }
  468   
  469       private Handler getHandler() {
  470           if (handler == null) {
  471               handler = new Handler();
  472           }
  473           return handler;
  474       }
  475   
  476       /**
  477        * Creates the JList used in the popup to display
  478        * the items in the combo box model. This method is called when the UI class
  479        * is created.
  480        *
  481        * @return a <code>JList</code> used to display the combo box items
  482        */
  483       protected JList createList() {
  484           return new JList( comboBox.getModel() ) {
  485               public void processMouseEvent(MouseEvent e)  {
  486                   if (e.isControlDown())  {
  487                       // Fix for 4234053. Filter out the Control Key from the list.
  488                       // ie., don't allow CTRL key deselection.
  489                       e = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(),
  490                                          e.getModifiers() ^ InputEvent.CTRL_MASK,
  491                                          e.getX(), e.getY(),
  492                                          e.getXOnScreen(), e.getYOnScreen(),
  493                                          e.getClickCount(),
  494                                          e.isPopupTrigger(),
  495                                          MouseEvent.NOBUTTON);
  496                   }
  497                   super.processMouseEvent(e);
  498               }
  499           };
  500       }
  501   
  502       /**
  503        * Configures the list which is used to hold the combo box items in the
  504        * popup. This method is called when the UI class
  505        * is created.
  506        *
  507        * @see #createList
  508        */
  509       protected void configureList() {
  510           list.setFont( comboBox.getFont() );
  511           list.setForeground( comboBox.getForeground() );
  512           list.setBackground( comboBox.getBackground() );
  513           list.setSelectionForeground( UIManager.getColor( "ComboBox.selectionForeground" ) );
  514           list.setSelectionBackground( UIManager.getColor( "ComboBox.selectionBackground" ) );
  515           list.setBorder( null );
  516           list.setCellRenderer( comboBox.getRenderer() );
  517           list.setFocusable( false );
  518           list.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
  519           setListSelection( comboBox.getSelectedIndex() );
  520           installListListeners();
  521       }
  522   
  523       /**
  524        * Adds the listeners to the list control.
  525        */
  526       protected void installListListeners() {
  527           if ((listMouseListener = createListMouseListener()) != null) {
  528               list.addMouseListener( listMouseListener );
  529           }
  530           if ((listMouseMotionListener = createListMouseMotionListener()) != null) {
  531               list.addMouseMotionListener( listMouseMotionListener );
  532           }
  533           if ((listSelectionListener = createListSelectionListener()) != null) {
  534               list.addListSelectionListener( listSelectionListener );
  535           }
  536       }
  537   
  538       void uninstallListListeners() {
  539           if (listMouseListener != null) {
  540               list.removeMouseListener(listMouseListener);
  541               listMouseListener = null;
  542           }
  543           if (listMouseMotionListener != null) {
  544               list.removeMouseMotionListener(listMouseMotionListener);
  545               listMouseMotionListener = null;
  546           }
  547           if (listSelectionListener != null) {
  548               list.removeListSelectionListener(listSelectionListener);
  549               listSelectionListener = null;
  550           }
  551           handler = null;
  552       }
  553   
  554       /**
  555        * Creates the scroll pane which houses the scrollable list.
  556        */
  557       protected JScrollPane createScroller() {
  558           JScrollPane sp = new JScrollPane( list,
  559                                   ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
  560                                   ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
  561           sp.setHorizontalScrollBar(null);
  562           return sp;
  563       }
  564   
  565       /**
  566        * Configures the scrollable portion which holds the list within
  567        * the combo box popup. This method is called when the UI class
  568        * is created.
  569        */
  570       protected void configureScroller() {
  571           scroller.setFocusable( false );
  572           scroller.getVerticalScrollBar().setFocusable( false );
  573           scroller.setBorder( null );
  574       }
  575   
  576       /**
  577        * Configures the popup portion of the combo box. This method is called
  578        * when the UI class is created.
  579        */
  580       protected void configurePopup() {
  581           setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
  582           setBorderPainted( true );
  583           setBorder(LIST_BORDER);
  584           setOpaque( false );
  585           add( scroller );
  586           setDoubleBuffered( true );
  587           setFocusable( false );
  588       }
  589   
  590       /**
  591        * This method adds the necessary listeners to the JComboBox.
  592        */
  593       protected void installComboBoxListeners() {
  594           if ((propertyChangeListener = createPropertyChangeListener()) != null) {
  595               comboBox.addPropertyChangeListener(propertyChangeListener);
  596           }
  597           if ((itemListener = createItemListener()) != null) {
  598               comboBox.addItemListener(itemListener);
  599           }
  600           installComboBoxModelListeners(comboBox.getModel());
  601       }
  602   
  603       /**
  604        * Installs the listeners on the combo box model. Any listeners installed
  605        * on the combo box model should be removed in
  606        * <code>uninstallComboBoxModelListeners</code>.
  607        *
  608        * @param model The combo box model to install listeners
  609        * @see #uninstallComboBoxModelListeners
  610        */
  611       protected void installComboBoxModelListeners( ComboBoxModel model ) {
  612           if (model != null && (listDataListener = createListDataListener()) != null) {
  613               model.addListDataListener(listDataListener);
  614           }
  615       }
  616   
  617       protected void installKeyboardActions() {
  618   
  619           /* XXX - shouldn't call this method. take it out for testing.
  620           ActionListener action = new ActionListener() {
  621               public void actionPerformed(ActionEvent e){
  622               }
  623           };
  624   
  625           comboBox.registerKeyboardAction( action,
  626                                            KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ),
  627                                            JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); */
  628   
  629       }
  630   
  631       //
  632       // end Initialization routines
  633       //=================================================================
  634   
  635   
  636       //===================================================================
  637       // begin Event Listenters
  638       //
  639   
  640       /**
  641        * A listener to be registered upon the combo box
  642        * (<em>not</em> its popup menu)
  643        * to handle mouse events
  644        * that affect the state of the popup menu.
  645        * The main purpose of this listener is to make the popup menu
  646        * appear and disappear.
  647        * This listener also helps
  648        * with click-and-drag scenarios by setting the selection if the mouse was
  649        * released over the list during a drag.
  650        *
  651        * <p>
  652        * <strong>Warning:</strong>
  653        * We recommend that you <em>not</em>
  654        * create subclasses of this class.
  655        * If you absolutely must create a subclass,
  656        * be sure to invoke the superclass
  657        * version of each method.
  658        *
  659        * @see BasicComboPopup#createMouseListener
  660        */
  661       protected class InvocationMouseHandler extends MouseAdapter {
  662           /**
  663            * Responds to mouse-pressed events on the combo box.
  664            *
  665            * @param e the mouse-press event to be handled
  666            */
  667           public void mousePressed( MouseEvent e ) {
  668               getHandler().mousePressed(e);
  669           }
  670   
  671           /**
  672            * Responds to the user terminating
  673            * a click or drag that began on the combo box.
  674            *
  675            * @param e the mouse-release event to be handled
  676            */
  677           public void mouseReleased( MouseEvent e ) {
  678               getHandler().mouseReleased(e);
  679           }
  680       }
  681   
  682       /**
  683        * This listener watches for dragging and updates the current selection in the
  684        * list if it is dragging over the list.
  685        */
  686       protected class InvocationMouseMotionHandler extends MouseMotionAdapter {
  687           public void mouseDragged( MouseEvent e ) {
  688               getHandler().mouseDragged(e);
  689           }
  690       }
  691   
  692       /**
  693        * As of Java 2 platform v 1.4, this class is now obsolete and is only included for
  694        * backwards API compatibility. Do not instantiate or subclass.
  695        * <p>
  696        * All the functionality of this class has been included in
  697        * BasicComboBoxUI ActionMap/InputMap methods.
  698        */
  699       public class InvocationKeyHandler extends KeyAdapter {
  700           public void keyReleased( KeyEvent e ) {}
  701       }
  702   
  703       /**
  704        * As of Java 2 platform v 1.4, this class is now obsolete, doesn't do anything, and
  705        * is only included for backwards API compatibility. Do not call or
  706        * override.
  707        */
  708       protected class ListSelectionHandler implements ListSelectionListener {
  709           public void valueChanged( ListSelectionEvent e ) {}
  710       }
  711   
  712       /**
  713        * As of 1.4, this class is now obsolete, doesn't do anything, and
  714        * is only included for backwards API compatibility. Do not call or
  715        * override.
  716        * <p>
  717        * The functionality has been migrated into <code>ItemHandler</code>.
  718        *
  719        * @see #createItemListener
  720        */
  721       public class ListDataHandler implements ListDataListener {
  722           public void contentsChanged( ListDataEvent e ) {}
  723   
  724           public void intervalAdded( ListDataEvent e ) {
  725           }
  726   
  727           public void intervalRemoved( ListDataEvent e ) {
  728           }
  729       }
  730   
  731       /**
  732        * This listener hides the popup when the mouse is released in the list.
  733        */
  734       protected class ListMouseHandler extends MouseAdapter {
  735           public void mousePressed( MouseEvent e ) {
  736           }
  737           public void mouseReleased(MouseEvent anEvent) {
  738               getHandler().mouseReleased(anEvent);
  739           }
  740       }
  741   
  742       /**
  743        * This listener changes the selected item as you move the mouse over the list.
  744        * The selection change is not committed to the model, this is for user feedback only.
  745        */
  746       protected class ListMouseMotionHandler extends MouseMotionAdapter {
  747           public void mouseMoved( MouseEvent anEvent ) {
  748               getHandler().mouseMoved(anEvent);
  749           }
  750       }
  751   
  752       /**
  753        * This listener watches for changes to the selection in the
  754        * combo box.
  755        */
  756       protected class ItemHandler implements ItemListener {
  757           public void itemStateChanged( ItemEvent e ) {
  758               getHandler().itemStateChanged(e);
  759           }
  760       }
  761   
  762       /**
  763        * This listener watches for bound properties that have changed in the
  764        * combo box.
  765        * <p>
  766        * Subclasses which wish to listen to combo box property changes should
  767        * call the superclass methods to ensure that the combo popup correctly
  768        * handles property changes.
  769        *
  770        * @see #createPropertyChangeListener
  771        */
  772       protected class PropertyChangeHandler implements PropertyChangeListener {
  773           public void propertyChange( PropertyChangeEvent e ) {
  774               getHandler().propertyChange(e);
  775           }
  776       }
  777   
  778   
  779       private class AutoScrollActionHandler implements ActionListener {
  780           private int direction;
  781   
  782           AutoScrollActionHandler(int direction) {
  783               this.direction = direction;
  784           }
  785   
  786           public void actionPerformed(ActionEvent e) {
  787               if (direction == SCROLL_UP) {
  788                   autoScrollUp();
  789               }
  790               else {
  791                   autoScrollDown();
  792               }
  793           }
  794       }
  795   
  796   
  797       private class Handler implements ItemListener, MouseListener,
  798                             MouseMotionListener, PropertyChangeListener,
  799                             Serializable {
  800           //
  801           // MouseListener
  802           // NOTE: this is added to both the JList and JComboBox
  803           //
  804           public void mouseClicked(MouseEvent e) {
  805           }
  806   
  807           public void mousePressed(MouseEvent e) {
  808               if (e.getSource() == list) {
  809                   return;
  810               }
  811               if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
  812                   return;
  813   
  814               if ( comboBox.isEditable() ) {
  815                   Component comp = comboBox.getEditor().getEditorComponent();
  816                   if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
  817                       comp.requestFocus();
  818                   }
  819               }
  820               else if (comboBox.isRequestFocusEnabled()) {
  821                   comboBox.requestFocus();
  822               }
  823               togglePopup();
  824           }
  825   
  826           public void mouseReleased(MouseEvent e) {
  827               if (e.getSource() == list) {
  828                   if (list.getModel().getSize() > 0) {
  829                       // JList mouse listener
  830                       if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
  831                           comboBox.getEditor().setItem(list.getSelectedValue());
  832                       }
  833                       comboBox.setSelectedIndex(list.getSelectedIndex());
  834                   }
  835                   comboBox.setPopupVisible(false);
  836                   // workaround for cancelling an edited item (bug 4530953)
  837                   if (comboBox.isEditable() && comboBox.getEditor() != null) {
  838                       comboBox.configureEditor(comboBox.getEditor(),
  839                                                comboBox.getSelectedItem());
  840                   }
  841                   return;
  842               }
  843               // JComboBox mouse listener
  844               Component source = (Component)e.getSource();
  845               Dimension size = source.getSize();
  846               Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
  847               if ( !bounds.contains( e.getPoint() ) ) {
  848                   MouseEvent newEvent = convertMouseEvent( e );
  849                   Point location = newEvent.getPoint();
  850                   Rectangle r = new Rectangle();
  851                   list.computeVisibleRect( r );
  852                   if ( r.contains( location ) ) {
  853                       if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
  854                           comboBox.getEditor().setItem(list.getSelectedValue());
  855                       }
  856                       comboBox.setSelectedIndex(list.getSelectedIndex());
  857                   }
  858                   comboBox.setPopupVisible(false);
  859               }
  860               hasEntered = false;
  861               stopAutoScrolling();
  862           }
  863   
  864           public void mouseEntered(MouseEvent e) {
  865           }
  866   
  867           public void mouseExited(MouseEvent e) {
  868           }
  869   
  870           //
  871           // MouseMotionListener:
  872           // NOTE: this is added to both the List and ComboBox
  873           //
  874           public void mouseMoved(MouseEvent anEvent) {
  875               if (anEvent.getSource() == list) {
  876                   Point location = anEvent.getPoint();
  877                   Rectangle r = new Rectangle();
  878                   list.computeVisibleRect( r );
  879                   if ( r.contains( location ) ) {
  880                       updateListBoxSelectionForEvent( anEvent, false );
  881                   }
  882               }
  883           }
  884   
  885           public void mouseDragged( MouseEvent e ) {
  886               if (e.getSource() == list) {
  887                   return;
  888               }
  889               if ( isVisible() ) {
  890                   MouseEvent newEvent = convertMouseEvent( e );
  891                   Rectangle r = new Rectangle();
  892                   list.computeVisibleRect( r );
  893   
  894                   if ( newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1 ) {
  895                       hasEntered = true;
  896                       if ( isAutoScrolling ) {
  897                           stopAutoScrolling();
  898                       }
  899                       Point location = newEvent.getPoint();
  900                       if ( r.contains( location ) ) {
  901                           updateListBoxSelectionForEvent( newEvent, false );
  902                       }
  903                   }
  904                   else {
  905                       if ( hasEntered ) {
  906                           int directionToScroll = newEvent.getPoint().y < r.y ? SCROLL_UP : SCROLL_DOWN;
  907                           if ( isAutoScrolling && scrollDirection != directionToScroll ) {
  908                               stopAutoScrolling();
  909                               startAutoScrolling( directionToScroll );
  910                           }
  911                           else if ( !isAutoScrolling ) {
  912                               startAutoScrolling( directionToScroll );
  913                           }
  914                       }
  915                       else {
  916                           if ( e.getPoint().y < 0 ) {
  917                               hasEntered = true;
  918                               startAutoScrolling( SCROLL_UP );
  919                           }
  920                       }
  921                   }
  922               }
  923           }
  924   
  925           //
  926           // PropertyChangeListener
  927           //
  928           public void propertyChange(PropertyChangeEvent e) {
  929               JComboBox comboBox = (JComboBox)e.getSource();
  930               String propertyName = e.getPropertyName();
  931   
  932               if ( propertyName == "model" ) {
  933                   ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
  934                   ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
  935                   uninstallComboBoxModelListeners(oldModel);
  936                   installComboBoxModelListeners(newModel);
  937   
  938                   list.setModel(newModel);
  939   
  940                   if ( isVisible() ) {
  941                       hide();
  942                   }
  943               }
  944               else if ( propertyName == "renderer" ) {
  945                   list.setCellRenderer( comboBox.getRenderer() );
  946                   if ( isVisible() ) {
  947                       hide();
  948                   }
  949               }
  950               else if (propertyName == "componentOrientation") {
  951                   // Pass along the new component orientation
  952                   // to the list and the scroller
  953   
  954                   ComponentOrientation o =(ComponentOrientation)e.getNewValue();
  955   
  956                   JList list = getList();
  957                   if (list!=null && list.getComponentOrientation()!=o) {
  958                       list.setComponentOrientation(o);
  959                   }
  960   
  961                   if (scroller!=null && scroller.getComponentOrientation()!=o) {
  962                       scroller.setComponentOrientation(o);
  963                   }
  964   
  965                   if (o!=getComponentOrientation()) {
  966                       setComponentOrientation(o);
  967                   }
  968               }
  969               else if (propertyName == "lightWeightPopupEnabled") {
  970                   setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
  971               }
  972           }
  973   
  974           //
  975           // ItemListener
  976           //
  977           public void itemStateChanged( ItemEvent e ) {
  978               if (e.getStateChange() == ItemEvent.SELECTED) {
  979                   JComboBox comboBox = (JComboBox)e.getSource();
  980                   setListSelection(comboBox.getSelectedIndex());
  981               }
  982           }
  983       }
  984   
  985       //
  986       // end Event Listeners
  987       //=================================================================
  988   
  989   
  990       /**
  991        * Overridden to unconditionally return false.
  992        */
  993       public boolean isFocusTraversable() {
  994           return false;
  995       }
  996   
  997       //===================================================================
  998       // begin Autoscroll methods
  999       //
 1000   
 1001       /**
 1002        * This protected method is implementation specific and should be private.
 1003        * do not call or override.
 1004        */
 1005       protected void startAutoScrolling( int direction ) {
 1006           // XXX - should be a private method within InvocationMouseMotionHandler
 1007           // if possible.
 1008           if ( isAutoScrolling ) {
 1009               autoscrollTimer.stop();
 1010           }
 1011   
 1012           isAutoScrolling = true;
 1013   
 1014           if ( direction == SCROLL_UP ) {
 1015               scrollDirection = SCROLL_UP;
 1016               Point convertedPoint = SwingUtilities.convertPoint( scroller, new Point( 1, 1 ), list );
 1017               int top = list.locationToIndex( convertedPoint );
 1018               list.setSelectedIndex( top );
 1019   
 1020               autoscrollTimer = new Timer( 100, new AutoScrollActionHandler(
 1021                                                SCROLL_UP) );
 1022           }
 1023           else if ( direction == SCROLL_DOWN ) {
 1024               scrollDirection = SCROLL_DOWN;
 1025               Dimension size = scroller.getSize();
 1026               Point convertedPoint = SwingUtilities.convertPoint( scroller,
 1027                                                                   new Point( 1, (size.height - 1) - 2 ),
 1028                                                                   list );
 1029               int bottom = list.locationToIndex( convertedPoint );
 1030               list.setSelectedIndex( bottom );
 1031   
 1032               autoscrollTimer = new Timer(100, new AutoScrollActionHandler(
 1033                                               SCROLL_DOWN));
 1034           }
 1035           autoscrollTimer.start();
 1036       }
 1037   
 1038       /**
 1039        * This protected method is implementation specific and should be private.
 1040        * do not call or override.
 1041        */
 1042       protected void stopAutoScrolling() {
 1043           isAutoScrolling = false;
 1044   
 1045           if ( autoscrollTimer != null ) {
 1046               autoscrollTimer.stop();
 1047               autoscrollTimer = null;
 1048           }
 1049       }
 1050   
 1051       /**
 1052        * This protected method is implementation specific and should be private.
 1053        * do not call or override.
 1054        */
 1055       protected void autoScrollUp() {
 1056           int index = list.getSelectedIndex();
 1057           if ( index > 0 ) {
 1058               list.setSelectedIndex( index - 1 );
 1059               list.ensureIndexIsVisible( index - 1 );
 1060           }
 1061       }
 1062   
 1063       /**
 1064        * This protected method is implementation specific and should be private.
 1065        * do not call or override.
 1066        */
 1067       protected void autoScrollDown() {
 1068           int index = list.getSelectedIndex();
 1069           int lastItem = list.getModel().getSize() - 1;
 1070           if ( index < lastItem ) {
 1071               list.setSelectedIndex( index + 1 );
 1072               list.ensureIndexIsVisible( index + 1 );
 1073           }
 1074       }
 1075   
 1076       //
 1077       // end Autoscroll methods
 1078       //=================================================================
 1079   
 1080   
 1081       //===================================================================
 1082       // begin Utility methods
 1083       //
 1084   
 1085       /**
 1086        * Gets the AccessibleContext associated with this BasicComboPopup.
 1087        * The AccessibleContext will have its parent set to the ComboBox.
 1088        *
 1089        * @return an AccessibleContext for the BasicComboPopup
 1090        * @since 1.5
 1091        */
 1092       public AccessibleContext getAccessibleContext() {
 1093           AccessibleContext context = super.getAccessibleContext();
 1094           context.setAccessibleParent(comboBox);
 1095           return context;
 1096       }
 1097   
 1098   
 1099       /**
 1100        * This is is a utility method that helps event handlers figure out where to
 1101        * send the focus when the popup is brought up.  The standard implementation
 1102        * delegates the focus to the editor (if the combo box is editable) or to
 1103        * the JComboBox if it is not editable.
 1104        */
 1105       protected void delegateFocus( MouseEvent e ) {
 1106           if ( comboBox.isEditable() ) {
 1107               Component comp = comboBox.getEditor().getEditorComponent();
 1108               if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
 1109                   comp.requestFocus();
 1110               }
 1111           }
 1112           else if (comboBox.isRequestFocusEnabled()) {
 1113               comboBox.requestFocus();
 1114           }
 1115       }
 1116   
 1117       /**
 1118        * Makes the popup visible if it is hidden and makes it hidden if it is
 1119        * visible.
 1120        */
 1121       protected void togglePopup() {
 1122           if ( isVisible() ) {
 1123               hide();
 1124           }
 1125           else {
 1126               show();
 1127           }
 1128       }
 1129   
 1130       /**
 1131        * Sets the list selection index to the selectedIndex. This
 1132        * method is used to synchronize the list selection with the
 1133        * combo box selection.
 1134        *
 1135        * @param selectedIndex the index to set the list
 1136        */
 1137       private void setListSelection(int selectedIndex) {
 1138           if ( selectedIndex == -1 ) {
 1139               list.clearSelection();
 1140           }
 1141           else {
 1142               list.setSelectedIndex( selectedIndex );
 1143               list.ensureIndexIsVisible( selectedIndex );
 1144           }
 1145       }
 1146   
 1147       protected MouseEvent convertMouseEvent( MouseEvent e ) {
 1148           Point convertedPoint = SwingUtilities.convertPoint( (Component)e.getSource(),
 1149                                                               e.getPoint(), list );
 1150           MouseEvent newEvent = new MouseEvent( (Component)e.getSource(),
 1151                                                 e.getID(),
 1152                                                 e.getWhen(),
 1153                                                 e.getModifiers(),
 1154                                                 convertedPoint.x,
 1155                                                 convertedPoint.y,
 1156                                                 e.getXOnScreen(),
 1157                                                 e.getYOnScreen(),
 1158                                                 e.getClickCount(),
 1159                                                 e.isPopupTrigger(),
 1160                                                 MouseEvent.NOBUTTON );
 1161           return newEvent;
 1162       }
 1163   
 1164   
 1165       /**
 1166        * Retrieves the height of the popup based on the current
 1167        * ListCellRenderer and the maximum row count.
 1168        */
 1169       protected int getPopupHeightForRowCount(int maxRowCount) {
 1170           // Set the cached value of the minimum row count
 1171           int minRowCount = Math.min( maxRowCount, comboBox.getItemCount() );
 1172           int height = 0;
 1173           ListCellRenderer renderer = list.getCellRenderer();
 1174           Object value = null;
 1175   
 1176           for ( int i = 0; i < minRowCount; ++i ) {
 1177               value = list.getModel().getElementAt( i );
 1178               Component c = renderer.getListCellRendererComponent( list, value, i, false, false );
 1179               height += c.getPreferredSize().height;
 1180           }
 1181   
 1182           if (height == 0) {
 1183               height = comboBox.getHeight();
 1184           }
 1185   
 1186           Border border = scroller.getViewportBorder();
 1187           if (border != null) {
 1188               Insets insets = border.getBorderInsets(null);
 1189               height += insets.top + insets.bottom;
 1190           }
 1191   
 1192           border = scroller.getBorder();
 1193           if (border != null) {
 1194               Insets insets = border.getBorderInsets(null);
 1195               height += insets.top + insets.bottom;
 1196           }
 1197   
 1198           return height;
 1199       }
 1200   
 1201       /**
 1202        * Calculate the placement and size of the popup portion of the combo box based
 1203        * on the combo box location and the enclosing screen bounds. If
 1204        * no transformations are required, then the returned rectangle will
 1205        * have the same values as the parameters.
 1206        *
 1207        * @param px starting x location
 1208        * @param py starting y location
 1209        * @param pw starting width
 1210        * @param ph starting height
 1211        * @return a rectangle which represents the placement and size of the popup
 1212        */
 1213       protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
 1214           Toolkit toolkit = Toolkit.getDefaultToolkit();
 1215           Rectangle screenBounds;
 1216   
 1217           // Calculate the desktop dimensions relative to the combo box.
 1218           GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
 1219           Point p = new Point();
 1220           SwingUtilities.convertPointFromScreen(p, comboBox);
 1221           if (gc != null) {
 1222               Insets screenInsets = toolkit.getScreenInsets(gc);
 1223               screenBounds = gc.getBounds();
 1224               screenBounds.width -= (screenInsets.left + screenInsets.right);
 1225               screenBounds.height -= (screenInsets.top + screenInsets.bottom);
 1226               screenBounds.x += (p.x + screenInsets.left);
 1227               screenBounds.y += (p.y + screenInsets.top);
 1228           }
 1229           else {
 1230               screenBounds = new Rectangle(p, toolkit.getScreenSize());
 1231           }
 1232   
 1233           Rectangle rect = new Rectangle(px,py,pw,ph);
 1234           if (py+ph > screenBounds.y+screenBounds.height
 1235               && ph < screenBounds.height) {
 1236               rect.y = -rect.height;
 1237           }
 1238           return rect;
 1239       }
 1240   
 1241       /**
 1242        * Calculates the upper left location of the Popup.
 1243        */
 1244       private Point getPopupLocation() {
 1245           Dimension popupSize = comboBox.getSize();
 1246           Insets insets = getInsets();
 1247   
 1248           // reduce the width of the scrollpane by the insets so that the popup
 1249           // is the same width as the combo box.
 1250           popupSize.setSize(popupSize.width - (insets.right + insets.left),
 1251                             getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
 1252           Rectangle popupBounds = computePopupBounds( 0, comboBox.getBounds().height,
 1253                                                       popupSize.width, popupSize.height);
 1254           Dimension scrollSize = popupBounds.getSize();
 1255           Point popupLocation = popupBounds.getLocation();
 1256   
 1257           scroller.setMaximumSize( scrollSize );
 1258           scroller.setPreferredSize( scrollSize );
 1259           scroller.setMinimumSize( scrollSize );
 1260   
 1261           list.revalidate();
 1262   
 1263           return popupLocation;
 1264       }
 1265   
 1266       /**
 1267        * A utility method used by the event listeners.  Given a mouse event, it changes
 1268        * the list selection to the list item below the mouse.
 1269        */
 1270       protected void updateListBoxSelectionForEvent(MouseEvent anEvent,boolean shouldScroll) {
 1271           // XXX - only seems to be called from this class. shouldScroll flag is
 1272           // never true
 1273           Point location = anEvent.getPoint();
 1274           if ( list == null )
 1275               return;
 1276           int index = list.locationToIndex(location);
 1277           if ( index == -1 ) {
 1278               if ( location.y < 0 )
 1279                   index = 0;
 1280               else
 1281                   index = comboBox.getModel().getSize() - 1;
 1282           }
 1283           if ( list.getSelectedIndex() != index ) {
 1284               list.setSelectedIndex(index);
 1285               if ( shouldScroll )
 1286                   list.ensureIndexIsVisible(index);
 1287           }
 1288       }
 1289   
 1290       //
 1291       // end Utility methods
 1292       //=================================================================
 1293   }

Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]