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

    1   /*
    2    * Copyright (c) 1998, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * 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           comboBox.firePopupMenuWillBecomeVisible();
  206           setListSelection(comboBox.getSelectedIndex());
  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() is called from BasicComboPopup.show() method
  348           // to let the user change the popup menu from the PopupMenuListener.popupMenuWillBecomeVisible()
  349       }
  350   
  351       protected void firePopupMenuWillBecomeInvisible() {
  352           super.firePopupMenuWillBecomeInvisible();
  353           comboBox.firePopupMenuWillBecomeInvisible();
  354       }
  355   
  356       protected void firePopupMenuCanceled() {
  357           super.firePopupMenuCanceled();
  358           comboBox.firePopupMenuCanceled();
  359       }
  360   
  361       /**
  362        * Creates a listener
  363        * that will watch for mouse-press and release events on the combo box.
  364        *
  365        * <strong>Warning:</strong>
  366        * When overriding this method, make sure to maintain the existing
  367        * behavior.
  368        *
  369        * @return a <code>MouseListener</code> which will be added to
  370        * the combo box or null
  371        */
  372       protected MouseListener createMouseListener() {
  373           return getHandler();
  374       }
  375   
  376       /**
  377        * Creates the mouse motion listener which will be added to the combo
  378        * box.
  379        *
  380        * <strong>Warning:</strong>
  381        * When overriding this method, make sure to maintain the existing
  382        * behavior.
  383        *
  384        * @return a <code>MouseMotionListener</code> which will be added to
  385        *         the combo box or null
  386        */
  387       protected MouseMotionListener createMouseMotionListener() {
  388           return getHandler();
  389       }
  390   
  391       /**
  392        * Creates the key listener that will be added to the combo box. If
  393        * this method returns null then it will not be added to the combo box.
  394        *
  395        * @return a <code>KeyListener</code> or null
  396        */
  397       protected KeyListener createKeyListener() {
  398           return null;
  399       }
  400   
  401       /**
  402        * Creates a list selection listener that watches for selection changes in
  403        * the popup's list.  If this method returns null then it will not
  404        * be added to the popup list.
  405        *
  406        * @return an instance of a <code>ListSelectionListener</code> or null
  407        */
  408       protected ListSelectionListener createListSelectionListener() {
  409           return null;
  410       }
  411   
  412       /**
  413        * Creates a list data listener which will be added to the
  414        * <code>ComboBoxModel</code>. If this method returns null then
  415        * it will not be added to the combo box model.
  416        *
  417        * @return an instance of a <code>ListDataListener</code> or null
  418        */
  419       protected ListDataListener createListDataListener() {
  420           return null;
  421       }
  422   
  423       /**
  424        * Creates a mouse listener that watches for mouse events in
  425        * the popup's list. If this method returns null then it will
  426        * not be added to the combo box.
  427        *
  428        * @return an instance of a <code>MouseListener</code> or null
  429        */
  430       protected MouseListener createListMouseListener() {
  431           return getHandler();
  432       }
  433   
  434       /**
  435        * Creates a mouse motion listener that watches for mouse motion
  436        * events in the popup's list. If this method returns null then it will
  437        * not be added to the combo box.
  438        *
  439        * @return an instance of a <code>MouseMotionListener</code> or null
  440        */
  441       protected MouseMotionListener createListMouseMotionListener() {
  442           return getHandler();
  443       }
  444   
  445       /**
  446        * Creates a <code>PropertyChangeListener</code> which will be added to
  447        * the combo box. If this method returns null then it will not
  448        * be added to the combo box.
  449        *
  450        * @return an instance of a <code>PropertyChangeListener</code> or null
  451        */
  452       protected PropertyChangeListener createPropertyChangeListener() {
  453           return getHandler();
  454       }
  455   
  456       /**
  457        * Creates an <code>ItemListener</code> which will be added to the
  458        * combo box. If this method returns null then it will not
  459        * be added to the combo box.
  460        * <p>
  461        * Subclasses may override this method to return instances of their own
  462        * ItemEvent handlers.
  463        *
  464        * @return an instance of an <code>ItemListener</code> or null
  465        */
  466       protected ItemListener createItemListener() {
  467           return getHandler();
  468       }
  469   
  470       private Handler getHandler() {
  471           if (handler == null) {
  472               handler = new Handler();
  473           }
  474           return handler;
  475       }
  476   
  477       /**
  478        * Creates the JList used in the popup to display
  479        * the items in the combo box model. This method is called when the UI class
  480        * is created.
  481        *
  482        * @return a <code>JList</code> used to display the combo box items
  483        */
  484       protected JList createList() {
  485           return new JList( comboBox.getModel() ) {
  486               public void processMouseEvent(MouseEvent e)  {
  487                   if (BasicGraphicsUtils.isMenuShortcutKeyDown(e))  {
  488                       // Fix for 4234053. Filter out the Control Key from the list.
  489                       // ie., don't allow CTRL key deselection.
  490                       Toolkit toolkit = Toolkit.getDefaultToolkit();
  491                       e = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(),
  492                                          e.getModifiers() ^ toolkit.getMenuShortcutKeyMask(),
  493                                          e.getX(), e.getY(),
  494                                          e.getXOnScreen(), e.getYOnScreen(),
  495                                          e.getClickCount(),
  496                                          e.isPopupTrigger(),
  497                                          MouseEvent.NOBUTTON);
  498                   }
  499                   super.processMouseEvent(e);
  500               }
  501           };
  502       }
  503   
  504       /**
  505        * Configures the list which is used to hold the combo box items in the
  506        * popup. This method is called when the UI class
  507        * is created.
  508        *
  509        * @see #createList
  510        */
  511       protected void configureList() {
  512           list.setFont( comboBox.getFont() );
  513           list.setForeground( comboBox.getForeground() );
  514           list.setBackground( comboBox.getBackground() );
  515           list.setSelectionForeground( UIManager.getColor( "ComboBox.selectionForeground" ) );
  516           list.setSelectionBackground( UIManager.getColor( "ComboBox.selectionBackground" ) );
  517           list.setBorder( null );
  518           list.setCellRenderer( comboBox.getRenderer() );
  519           list.setFocusable( false );
  520           list.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
  521           setListSelection( comboBox.getSelectedIndex() );
  522           installListListeners();
  523       }
  524   
  525       /**
  526        * Adds the listeners to the list control.
  527        */
  528       protected void installListListeners() {
  529           if ((listMouseListener = createListMouseListener()) != null) {
  530               list.addMouseListener( listMouseListener );
  531           }
  532           if ((listMouseMotionListener = createListMouseMotionListener()) != null) {
  533               list.addMouseMotionListener( listMouseMotionListener );
  534           }
  535           if ((listSelectionListener = createListSelectionListener()) != null) {
  536               list.addListSelectionListener( listSelectionListener );
  537           }
  538       }
  539   
  540       void uninstallListListeners() {
  541           if (listMouseListener != null) {
  542               list.removeMouseListener(listMouseListener);
  543               listMouseListener = null;
  544           }
  545           if (listMouseMotionListener != null) {
  546               list.removeMouseMotionListener(listMouseMotionListener);
  547               listMouseMotionListener = null;
  548           }
  549           if (listSelectionListener != null) {
  550               list.removeListSelectionListener(listSelectionListener);
  551               listSelectionListener = null;
  552           }
  553           handler = null;
  554       }
  555   
  556       /**
  557        * Creates the scroll pane which houses the scrollable list.
  558        */
  559       protected JScrollPane createScroller() {
  560           JScrollPane sp = new JScrollPane( list,
  561                                   ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
  562                                   ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
  563           sp.setHorizontalScrollBar(null);
  564           return sp;
  565       }
  566   
  567       /**
  568        * Configures the scrollable portion which holds the list within
  569        * the combo box popup. This method is called when the UI class
  570        * is created.
  571        */
  572       protected void configureScroller() {
  573           scroller.setFocusable( false );
  574           scroller.getVerticalScrollBar().setFocusable( false );
  575           scroller.setBorder( null );
  576       }
  577   
  578       /**
  579        * Configures the popup portion of the combo box. This method is called
  580        * when the UI class is created.
  581        */
  582       protected void configurePopup() {
  583           setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
  584           setBorderPainted( true );
  585           setBorder(LIST_BORDER);
  586           setOpaque( false );
  587           add( scroller );
  588           setDoubleBuffered( true );
  589           setFocusable( false );
  590       }
  591   
  592       /**
  593        * This method adds the necessary listeners to the JComboBox.
  594        */
  595       protected void installComboBoxListeners() {
  596           if ((propertyChangeListener = createPropertyChangeListener()) != null) {
  597               comboBox.addPropertyChangeListener(propertyChangeListener);
  598           }
  599           if ((itemListener = createItemListener()) != null) {
  600               comboBox.addItemListener(itemListener);
  601           }
  602           installComboBoxModelListeners(comboBox.getModel());
  603       }
  604   
  605       /**
  606        * Installs the listeners on the combo box model. Any listeners installed
  607        * on the combo box model should be removed in
  608        * <code>uninstallComboBoxModelListeners</code>.
  609        *
  610        * @param model The combo box model to install listeners
  611        * @see #uninstallComboBoxModelListeners
  612        */
  613       protected void installComboBoxModelListeners( ComboBoxModel model ) {
  614           if (model != null && (listDataListener = createListDataListener()) != null) {
  615               model.addListDataListener(listDataListener);
  616           }
  617       }
  618   
  619       protected void installKeyboardActions() {
  620   
  621           /* XXX - shouldn't call this method. take it out for testing.
  622           ActionListener action = new ActionListener() {
  623               public void actionPerformed(ActionEvent e){
  624               }
  625           };
  626   
  627           comboBox.registerKeyboardAction( action,
  628                                            KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ),
  629                                            JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); */
  630   
  631       }
  632   
  633       //
  634       // end Initialization routines
  635       //=================================================================
  636   
  637   
  638       //===================================================================
  639       // begin Event Listenters
  640       //
  641   
  642       /**
  643        * A listener to be registered upon the combo box
  644        * (<em>not</em> its popup menu)
  645        * to handle mouse events
  646        * that affect the state of the popup menu.
  647        * The main purpose of this listener is to make the popup menu
  648        * appear and disappear.
  649        * This listener also helps
  650        * with click-and-drag scenarios by setting the selection if the mouse was
  651        * released over the list during a drag.
  652        *
  653        * <p>
  654        * <strong>Warning:</strong>
  655        * We recommend that you <em>not</em>
  656        * create subclasses of this class.
  657        * If you absolutely must create a subclass,
  658        * be sure to invoke the superclass
  659        * version of each method.
  660        *
  661        * @see BasicComboPopup#createMouseListener
  662        */
  663       protected class InvocationMouseHandler extends MouseAdapter {
  664           /**
  665            * Responds to mouse-pressed events on the combo box.
  666            *
  667            * @param e the mouse-press event to be handled
  668            */
  669           public void mousePressed( MouseEvent e ) {
  670               getHandler().mousePressed(e);
  671           }
  672   
  673           /**
  674            * Responds to the user terminating
  675            * a click or drag that began on the combo box.
  676            *
  677            * @param e the mouse-release event to be handled
  678            */
  679           public void mouseReleased( MouseEvent e ) {
  680               getHandler().mouseReleased(e);
  681           }
  682       }
  683   
  684       /**
  685        * This listener watches for dragging and updates the current selection in the
  686        * list if it is dragging over the list.
  687        */
  688       protected class InvocationMouseMotionHandler extends MouseMotionAdapter {
  689           public void mouseDragged( MouseEvent e ) {
  690               getHandler().mouseDragged(e);
  691           }
  692       }
  693   
  694       /**
  695        * As of Java 2 platform v 1.4, this class is now obsolete and is only included for
  696        * backwards API compatibility. Do not instantiate or subclass.
  697        * <p>
  698        * All the functionality of this class has been included in
  699        * BasicComboBoxUI ActionMap/InputMap methods.
  700        */
  701       public class InvocationKeyHandler extends KeyAdapter {
  702           public void keyReleased( KeyEvent e ) {}
  703       }
  704   
  705       /**
  706        * As of Java 2 platform v 1.4, this class is now obsolete, doesn't do anything, and
  707        * is only included for backwards API compatibility. Do not call or
  708        * override.
  709        */
  710       protected class ListSelectionHandler implements ListSelectionListener {
  711           public void valueChanged( ListSelectionEvent e ) {}
  712       }
  713   
  714       /**
  715        * As of 1.4, this class is now obsolete, doesn't do anything, and
  716        * is only included for backwards API compatibility. Do not call or
  717        * override.
  718        * <p>
  719        * The functionality has been migrated into <code>ItemHandler</code>.
  720        *
  721        * @see #createItemListener
  722        */
  723       public class ListDataHandler implements ListDataListener {
  724           public void contentsChanged( ListDataEvent e ) {}
  725   
  726           public void intervalAdded( ListDataEvent e ) {
  727           }
  728   
  729           public void intervalRemoved( ListDataEvent e ) {
  730           }
  731       }
  732   
  733       /**
  734        * This listener hides the popup when the mouse is released in the list.
  735        */
  736       protected class ListMouseHandler extends MouseAdapter {
  737           public void mousePressed( MouseEvent e ) {
  738           }
  739           public void mouseReleased(MouseEvent anEvent) {
  740               getHandler().mouseReleased(anEvent);
  741           }
  742       }
  743   
  744       /**
  745        * This listener changes the selected item as you move the mouse over the list.
  746        * The selection change is not committed to the model, this is for user feedback only.
  747        */
  748       protected class ListMouseMotionHandler extends MouseMotionAdapter {
  749           public void mouseMoved( MouseEvent anEvent ) {
  750               getHandler().mouseMoved(anEvent);
  751           }
  752       }
  753   
  754       /**
  755        * This listener watches for changes to the selection in the
  756        * combo box.
  757        */
  758       protected class ItemHandler implements ItemListener {
  759           public void itemStateChanged( ItemEvent e ) {
  760               getHandler().itemStateChanged(e);
  761           }
  762       }
  763   
  764       /**
  765        * This listener watches for bound properties that have changed in the
  766        * combo box.
  767        * <p>
  768        * Subclasses which wish to listen to combo box property changes should
  769        * call the superclass methods to ensure that the combo popup correctly
  770        * handles property changes.
  771        *
  772        * @see #createPropertyChangeListener
  773        */
  774       protected class PropertyChangeHandler implements PropertyChangeListener {
  775           public void propertyChange( PropertyChangeEvent e ) {
  776               getHandler().propertyChange(e);
  777           }
  778       }
  779   
  780   
  781       private class AutoScrollActionHandler implements ActionListener {
  782           private int direction;
  783   
  784           AutoScrollActionHandler(int direction) {
  785               this.direction = direction;
  786           }
  787   
  788           public void actionPerformed(ActionEvent e) {
  789               if (direction == SCROLL_UP) {
  790                   autoScrollUp();
  791               }
  792               else {
  793                   autoScrollDown();
  794               }
  795           }
  796       }
  797   
  798   
  799       private class Handler implements ItemListener, MouseListener,
  800                             MouseMotionListener, PropertyChangeListener,
  801                             Serializable {
  802           //
  803           // MouseListener
  804           // NOTE: this is added to both the JList and JComboBox
  805           //
  806           public void mouseClicked(MouseEvent e) {
  807           }
  808   
  809           public void mousePressed(MouseEvent e) {
  810               if (e.getSource() == list) {
  811                   return;
  812               }
  813               if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
  814                   return;
  815   
  816               if ( comboBox.isEditable() ) {
  817                   Component comp = comboBox.getEditor().getEditorComponent();
  818                   if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
  819                       comp.requestFocus();
  820                   }
  821               }
  822               else if (comboBox.isRequestFocusEnabled()) {
  823                   comboBox.requestFocus();
  824               }
  825               togglePopup();
  826           }
  827   
  828           public void mouseReleased(MouseEvent e) {
  829               if (e.getSource() == list) {
  830                   if (list.getModel().getSize() > 0) {
  831                       // JList mouse listener
  832                       if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
  833                           comboBox.getEditor().setItem(list.getSelectedValue());
  834                       }
  835                       comboBox.setSelectedIndex(list.getSelectedIndex());
  836                   }
  837                   comboBox.setPopupVisible(false);
  838                   // workaround for cancelling an edited item (bug 4530953)
  839                   if (comboBox.isEditable() && comboBox.getEditor() != null) {
  840                       comboBox.configureEditor(comboBox.getEditor(),
  841                                                comboBox.getSelectedItem());
  842                   }
  843                   return;
  844               }
  845               // JComboBox mouse listener
  846               Component source = (Component)e.getSource();
  847               Dimension size = source.getSize();
  848               Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
  849               if ( !bounds.contains( e.getPoint() ) ) {
  850                   MouseEvent newEvent = convertMouseEvent( e );
  851                   Point location = newEvent.getPoint();
  852                   Rectangle r = new Rectangle();
  853                   list.computeVisibleRect( r );
  854                   if ( r.contains( location ) ) {
  855                       if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
  856                           comboBox.getEditor().setItem(list.getSelectedValue());
  857                       }
  858                       comboBox.setSelectedIndex(list.getSelectedIndex());
  859                   }
  860                   comboBox.setPopupVisible(false);
  861               }
  862               hasEntered = false;
  863               stopAutoScrolling();
  864           }
  865   
  866           public void mouseEntered(MouseEvent e) {
  867           }
  868   
  869           public void mouseExited(MouseEvent e) {
  870           }
  871   
  872           //
  873           // MouseMotionListener:
  874           // NOTE: this is added to both the List and ComboBox
  875           //
  876           public void mouseMoved(MouseEvent anEvent) {
  877               if (anEvent.getSource() == list) {
  878                   Point location = anEvent.getPoint();
  879                   Rectangle r = new Rectangle();
  880                   list.computeVisibleRect( r );
  881                   if ( r.contains( location ) ) {
  882                       updateListBoxSelectionForEvent( anEvent, false );
  883                   }
  884               }
  885           }
  886   
  887           public void mouseDragged( MouseEvent e ) {
  888               if (e.getSource() == list) {
  889                   return;
  890               }
  891               if ( isVisible() ) {
  892                   MouseEvent newEvent = convertMouseEvent( e );
  893                   Rectangle r = new Rectangle();
  894                   list.computeVisibleRect( r );
  895   
  896                   if ( newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1 ) {
  897                       hasEntered = true;
  898                       if ( isAutoScrolling ) {
  899                           stopAutoScrolling();
  900                       }
  901                       Point location = newEvent.getPoint();
  902                       if ( r.contains( location ) ) {
  903                           updateListBoxSelectionForEvent( newEvent, false );
  904                       }
  905                   }
  906                   else {
  907                       if ( hasEntered ) {
  908                           int directionToScroll = newEvent.getPoint().y < r.y ? SCROLL_UP : SCROLL_DOWN;
  909                           if ( isAutoScrolling && scrollDirection != directionToScroll ) {
  910                               stopAutoScrolling();
  911                               startAutoScrolling( directionToScroll );
  912                           }
  913                           else if ( !isAutoScrolling ) {
  914                               startAutoScrolling( directionToScroll );
  915                           }
  916                       }
  917                       else {
  918                           if ( e.getPoint().y < 0 ) {
  919                               hasEntered = true;
  920                               startAutoScrolling( SCROLL_UP );
  921                           }
  922                       }
  923                   }
  924               }
  925           }
  926   
  927           //
  928           // PropertyChangeListener
  929           //
  930           public void propertyChange(PropertyChangeEvent e) {
  931               JComboBox comboBox = (JComboBox)e.getSource();
  932               String propertyName = e.getPropertyName();
  933   
  934               if ( propertyName == "model" ) {
  935                   ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
  936                   ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
  937                   uninstallComboBoxModelListeners(oldModel);
  938                   installComboBoxModelListeners(newModel);
  939   
  940                   list.setModel(newModel);
  941   
  942                   if ( isVisible() ) {
  943                       hide();
  944                   }
  945               }
  946               else if ( propertyName == "renderer" ) {
  947                   list.setCellRenderer( comboBox.getRenderer() );
  948                   if ( isVisible() ) {
  949                       hide();
  950                   }
  951               }
  952               else if (propertyName == "componentOrientation") {
  953                   // Pass along the new component orientation
  954                   // to the list and the scroller
  955   
  956                   ComponentOrientation o =(ComponentOrientation)e.getNewValue();
  957   
  958                   JList list = getList();
  959                   if (list!=null && list.getComponentOrientation()!=o) {
  960                       list.setComponentOrientation(o);
  961                   }
  962   
  963                   if (scroller!=null && scroller.getComponentOrientation()!=o) {
  964                       scroller.setComponentOrientation(o);
  965                   }
  966   
  967                   if (o!=getComponentOrientation()) {
  968                       setComponentOrientation(o);
  969                   }
  970               }
  971               else if (propertyName == "lightWeightPopupEnabled") {
  972                   setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
  973               }
  974           }
  975   
  976           //
  977           // ItemListener
  978           //
  979           public void itemStateChanged( ItemEvent e ) {
  980               if (e.getStateChange() == ItemEvent.SELECTED) {
  981                   JComboBox comboBox = (JComboBox)e.getSource();
  982                   setListSelection(comboBox.getSelectedIndex());
  983               }
  984           }
  985       }
  986   
  987       //
  988       // end Event Listeners
  989       //=================================================================
  990   
  991   
  992       /**
  993        * Overridden to unconditionally return false.
  994        */
  995       public boolean isFocusTraversable() {
  996           return false;
  997       }
  998   
  999       //===================================================================
 1000       // begin Autoscroll methods
 1001       //
 1002   
 1003       /**
 1004        * This protected method is implementation specific and should be private.
 1005        * do not call or override.
 1006        */
 1007       protected void startAutoScrolling( int direction ) {
 1008           // XXX - should be a private method within InvocationMouseMotionHandler
 1009           // if possible.
 1010           if ( isAutoScrolling ) {
 1011               autoscrollTimer.stop();
 1012           }
 1013   
 1014           isAutoScrolling = true;
 1015   
 1016           if ( direction == SCROLL_UP ) {
 1017               scrollDirection = SCROLL_UP;
 1018               Point convertedPoint = SwingUtilities.convertPoint( scroller, new Point( 1, 1 ), list );
 1019               int top = list.locationToIndex( convertedPoint );
 1020               list.setSelectedIndex( top );
 1021   
 1022               autoscrollTimer = new Timer( 100, new AutoScrollActionHandler(
 1023                                                SCROLL_UP) );
 1024           }
 1025           else if ( direction == SCROLL_DOWN ) {
 1026               scrollDirection = SCROLL_DOWN;
 1027               Dimension size = scroller.getSize();
 1028               Point convertedPoint = SwingUtilities.convertPoint( scroller,
 1029                                                                   new Point( 1, (size.height - 1) - 2 ),
 1030                                                                   list );
 1031               int bottom = list.locationToIndex( convertedPoint );
 1032               list.setSelectedIndex( bottom );
 1033   
 1034               autoscrollTimer = new Timer(100, new AutoScrollActionHandler(
 1035                                               SCROLL_DOWN));
 1036           }
 1037           autoscrollTimer.start();
 1038       }
 1039   
 1040       /**
 1041        * This protected method is implementation specific and should be private.
 1042        * do not call or override.
 1043        */
 1044       protected void stopAutoScrolling() {
 1045           isAutoScrolling = false;
 1046   
 1047           if ( autoscrollTimer != null ) {
 1048               autoscrollTimer.stop();
 1049               autoscrollTimer = null;
 1050           }
 1051       }
 1052   
 1053       /**
 1054        * This protected method is implementation specific and should be private.
 1055        * do not call or override.
 1056        */
 1057       protected void autoScrollUp() {
 1058           int index = list.getSelectedIndex();
 1059           if ( index > 0 ) {
 1060               list.setSelectedIndex( index - 1 );
 1061               list.ensureIndexIsVisible( index - 1 );
 1062           }
 1063       }
 1064   
 1065       /**
 1066        * This protected method is implementation specific and should be private.
 1067        * do not call or override.
 1068        */
 1069       protected void autoScrollDown() {
 1070           int index = list.getSelectedIndex();
 1071           int lastItem = list.getModel().getSize() - 1;
 1072           if ( index < lastItem ) {
 1073               list.setSelectedIndex( index + 1 );
 1074               list.ensureIndexIsVisible( index + 1 );
 1075           }
 1076       }
 1077   
 1078       //
 1079       // end Autoscroll methods
 1080       //=================================================================
 1081   
 1082   
 1083       //===================================================================
 1084       // begin Utility methods
 1085       //
 1086   
 1087       /**
 1088        * Gets the AccessibleContext associated with this BasicComboPopup.
 1089        * The AccessibleContext will have its parent set to the ComboBox.
 1090        *
 1091        * @return an AccessibleContext for the BasicComboPopup
 1092        * @since 1.5
 1093        */
 1094       public AccessibleContext getAccessibleContext() {
 1095           AccessibleContext context = super.getAccessibleContext();
 1096           context.setAccessibleParent(comboBox);
 1097           return context;
 1098       }
 1099   
 1100   
 1101       /**
 1102        * This is is a utility method that helps event handlers figure out where to
 1103        * send the focus when the popup is brought up.  The standard implementation
 1104        * delegates the focus to the editor (if the combo box is editable) or to
 1105        * the JComboBox if it is not editable.
 1106        */
 1107       protected void delegateFocus( MouseEvent e ) {
 1108           if ( comboBox.isEditable() ) {
 1109               Component comp = comboBox.getEditor().getEditorComponent();
 1110               if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
 1111                   comp.requestFocus();
 1112               }
 1113           }
 1114           else if (comboBox.isRequestFocusEnabled()) {
 1115               comboBox.requestFocus();
 1116           }
 1117       }
 1118   
 1119       /**
 1120        * Makes the popup visible if it is hidden and makes it hidden if it is
 1121        * visible.
 1122        */
 1123       protected void togglePopup() {
 1124           if ( isVisible() ) {
 1125               hide();
 1126           }
 1127           else {
 1128               show();
 1129           }
 1130       }
 1131   
 1132       /**
 1133        * Sets the list selection index to the selectedIndex. This
 1134        * method is used to synchronize the list selection with the
 1135        * combo box selection.
 1136        *
 1137        * @param selectedIndex the index to set the list
 1138        */
 1139       private void setListSelection(int selectedIndex) {
 1140           if ( selectedIndex == -1 ) {
 1141               list.clearSelection();
 1142           }
 1143           else {
 1144               list.setSelectedIndex( selectedIndex );
 1145               list.ensureIndexIsVisible( selectedIndex );
 1146           }
 1147       }
 1148   
 1149       protected MouseEvent convertMouseEvent( MouseEvent e ) {
 1150           Point convertedPoint = SwingUtilities.convertPoint( (Component)e.getSource(),
 1151                                                               e.getPoint(), list );
 1152           MouseEvent newEvent = new MouseEvent( (Component)e.getSource(),
 1153                                                 e.getID(),
 1154                                                 e.getWhen(),
 1155                                                 e.getModifiers(),
 1156                                                 convertedPoint.x,
 1157                                                 convertedPoint.y,
 1158                                                 e.getXOnScreen(),
 1159                                                 e.getYOnScreen(),
 1160                                                 e.getClickCount(),
 1161                                                 e.isPopupTrigger(),
 1162                                                 MouseEvent.NOBUTTON );
 1163           return newEvent;
 1164       }
 1165   
 1166   
 1167       /**
 1168        * Retrieves the height of the popup based on the current
 1169        * ListCellRenderer and the maximum row count.
 1170        */
 1171       protected int getPopupHeightForRowCount(int maxRowCount) {
 1172           // Set the cached value of the minimum row count
 1173           int minRowCount = Math.min( maxRowCount, comboBox.getItemCount() );
 1174           int height = 0;
 1175           ListCellRenderer renderer = list.getCellRenderer();
 1176           Object value = null;
 1177   
 1178           for ( int i = 0; i < minRowCount; ++i ) {
 1179               value = list.getModel().getElementAt( i );
 1180               Component c = renderer.getListCellRendererComponent( list, value, i, false, false );
 1181               height += c.getPreferredSize().height;
 1182           }
 1183   
 1184           if (height == 0) {
 1185               height = comboBox.getHeight();
 1186           }
 1187   
 1188           Border border = scroller.getViewportBorder();
 1189           if (border != null) {
 1190               Insets insets = border.getBorderInsets(null);
 1191               height += insets.top + insets.bottom;
 1192           }
 1193   
 1194           border = scroller.getBorder();
 1195           if (border != null) {
 1196               Insets insets = border.getBorderInsets(null);
 1197               height += insets.top + insets.bottom;
 1198           }
 1199   
 1200           return height;
 1201       }
 1202   
 1203       /**
 1204        * Calculate the placement and size of the popup portion of the combo box based
 1205        * on the combo box location and the enclosing screen bounds. If
 1206        * no transformations are required, then the returned rectangle will
 1207        * have the same values as the parameters.
 1208        *
 1209        * @param px starting x location
 1210        * @param py starting y location
 1211        * @param pw starting width
 1212        * @param ph starting height
 1213        * @return a rectangle which represents the placement and size of the popup
 1214        */
 1215       protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
 1216           Toolkit toolkit = Toolkit.getDefaultToolkit();
 1217           Rectangle screenBounds;
 1218   
 1219           // Calculate the desktop dimensions relative to the combo box.
 1220           GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
 1221           Point p = new Point();
 1222           SwingUtilities.convertPointFromScreen(p, comboBox);
 1223           if (gc != null) {
 1224               Insets screenInsets = toolkit.getScreenInsets(gc);
 1225               screenBounds = gc.getBounds();
 1226               screenBounds.width -= (screenInsets.left + screenInsets.right);
 1227               screenBounds.height -= (screenInsets.top + screenInsets.bottom);
 1228               screenBounds.x += (p.x + screenInsets.left);
 1229               screenBounds.y += (p.y + screenInsets.top);
 1230           }
 1231           else {
 1232               screenBounds = new Rectangle(p, toolkit.getScreenSize());
 1233           }
 1234   
 1235           Rectangle rect = new Rectangle(px,py,pw,ph);
 1236           if (py+ph > screenBounds.y+screenBounds.height
 1237               && ph < screenBounds.height) {
 1238               rect.y = -rect.height;
 1239           }
 1240           return rect;
 1241       }
 1242   
 1243       /**
 1244        * Calculates the upper left location of the Popup.
 1245        */
 1246       private Point getPopupLocation() {
 1247           Dimension popupSize = comboBox.getSize();
 1248           Insets insets = getInsets();
 1249   
 1250           // reduce the width of the scrollpane by the insets so that the popup
 1251           // is the same width as the combo box.
 1252           popupSize.setSize(popupSize.width - (insets.right + insets.left),
 1253                             getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
 1254           Rectangle popupBounds = computePopupBounds( 0, comboBox.getBounds().height,
 1255                                                       popupSize.width, popupSize.height);
 1256           Dimension scrollSize = popupBounds.getSize();
 1257           Point popupLocation = popupBounds.getLocation();
 1258   
 1259           scroller.setMaximumSize( scrollSize );
 1260           scroller.setPreferredSize( scrollSize );
 1261           scroller.setMinimumSize( scrollSize );
 1262   
 1263           list.revalidate();
 1264   
 1265           return popupLocation;
 1266       }
 1267   
 1268       /**
 1269        * A utility method used by the event listeners.  Given a mouse event, it changes
 1270        * the list selection to the list item below the mouse.
 1271        */
 1272       protected void updateListBoxSelectionForEvent(MouseEvent anEvent,boolean shouldScroll) {
 1273           // XXX - only seems to be called from this class. shouldScroll flag is
 1274           // never true
 1275           Point location = anEvent.getPoint();
 1276           if ( list == null )
 1277               return;
 1278           int index = list.locationToIndex(location);
 1279           if ( index == -1 ) {
 1280               if ( location.y < 0 )
 1281                   index = 0;
 1282               else
 1283                   index = comboBox.getModel().getSize() - 1;
 1284           }
 1285           if ( list.getSelectedIndex() != index ) {
 1286               list.setSelectedIndex(index);
 1287               if ( shouldScroll )
 1288                   list.ensureIndexIsVisible(index);
 1289           }
 1290       }
 1291   
 1292       //
 1293       // end Utility methods
 1294       //=================================================================
 1295   }

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