Index: javax/swing/plaf/basic/BasicComboPopup.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicComboPopup.java,v retrieving revision 1.3 diff -u -r1.3 BasicComboPopup.java --- javax/swing/plaf/basic/BasicComboPopup.java 23 Sep 2004 15:03:21 -0000 1.3 +++ javax/swing/plaf/basic/BasicComboPopup.java 30 Sep 2004 09:50:24 -0000 @@ -39,6 +39,7 @@ import java.awt.Component; import java.awt.Dimension; +import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; @@ -58,10 +59,12 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPopupMenu; +import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; @@ -78,37 +81,32 @@ */ public class BasicComboPopup extends JPopupMenu implements ComboPopup { + /* Timer for autoscrolling */ protected Timer autoscrollTimer; - /** - * ComboBox associated with this popup - */ + /** ComboBox associated with this popup */ protected JComboBox comboBox; - /* - * FIXME: Document fields below - */ + /** FIXME: Need to document */ protected boolean hasEntered; - protected boolean isAutoScrolling; /** - * ItemListener listening to the selection changes in the combo box + * Indicates whether the scroll bar located in popup menu with comboBox's + * list of items is currently autoscrolling. This happens when mouse event + * originated in the combo box and is dragged outside of its bounds */ + protected boolean isAutoScrolling; + + /** ItemListener listening to the selection changes in the combo box */ protected ItemListener itemListener; - /** - * This listener is not used - */ + /** This listener is not used */ protected KeyListener keyListener; - /** - * JList which is used to display item is the combo box - */ + /** JList which is used to display item is the combo box */ protected JList list; - /** - * This listener is not used - */ + /** This listener is not used */ protected ListDataListener listDataListener; /** @@ -123,14 +121,10 @@ */ protected MouseMotionListener listMouseMotionListener; - /** - * This listener is not used - */ + /** This listener is not used */ protected ListSelectionListener listSelectionListener; - /** - * MouseListener listening to mouse events occuring in the combo box - */ + /** MouseListener listening to mouse events occuring in the combo box */ protected MouseListener mouseListener; /** @@ -145,21 +139,19 @@ */ protected PropertyChangeListener propertyChangeListener; - /* - * FIXME: Document fields below - */ - protected static int SCROLL_DOWN = 1; - protected static int SCROLL_UP = 0; + /** direction for scrolling down list of combo box's items */ + protected static final int SCROLL_DOWN = 1; + + /** direction for scrolling up list of combo box's items */ + protected static final int SCROLL_UP = 0; + + /** Indicates auto scrolling direction */ protected int scrollDirection; - /** - * JScrollPane that contains list portion of the combo box - */ + /** JScrollPane that contains list portion of the combo box */ protected JScrollPane scroller; - /** - * This field is not used - */ + /** This field is not used */ protected boolean valueIsAdjusting; /** @@ -184,13 +176,21 @@ // popup should have same width as the comboBox and should be hight anough // to display number of rows equal to 'maximumRowCount' property - int popupHeight = getPopupHeightForRowCount(comboBox.getMaximumRowCount()) - + 4; + int popupHeight = getPopupHeightForRowCount(comboBox.getMaximumRowCount()); - // FIXME: Uncomment this out once preferred size of JList will be working - // list.setPreferredSize(new Dimension(cbBounds.width, popupHeight)); + list.setPreferredSize(new Dimension(cbBounds.width, popupHeight)); super.setPopupSize(cbBounds.width, popupHeight); + // Highlight selected item in the combo box's drop down list + if (comboBox.getSelectedIndex() != -1) + list.setSelectedIndex(comboBox.getSelectedIndex()); + + //scroll scrollbar s.t. selected item is visible + JScrollBar scrollbar = scroller.getVerticalScrollBar(); + int selectedIndex = comboBox.getSelectedIndex(); + if (selectedIndex > comboBox.getMaximumRowCount()) + scrollbar.setValue(getPopupHeightForRowCount(selectedIndex)); + // location specified is relative to comboBox super.show(comboBox, 0, cbBounds.height); } @@ -557,37 +557,68 @@ } /** - * DOCUMENT ME! + * This method start scrolling combo box's list of items either up or down + * depending on the specified 'direction' * - * @param direction DOCUMENT ME! + * @param direction of the scrolling. */ protected void startAutoScrolling(int direction) { - // FIXME: Need to implement + // FIXME: add timer + isAutoScrolling = true; + + if (direction == SCROLL_UP) + autoScrollUp(); + else + autoScrollDown(); } /** - * DOCUMENT ME! + * This method stops scrolling the combo box's list of items */ protected void stopAutoScrolling() { - // FIXME: Need to implement + // FIXME: add timer + isAutoScrolling = false; } /** - * DOCUMENT ME! + * This method scrolls up list of combo box's items up and highlights that + * just became visible. */ protected void autoScrollUp() { - // FIXME: Need to implement + // scroll up the scroll bar to make the item above visible + JScrollBar scrollbar = scroller.getVerticalScrollBar(); + int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(), + SwingConstants.VERTICAL, + SCROLL_UP); + + scrollbar.setValue(scrollbar.getValue() - scrollToNext); + + // If we haven't reached the begging of the combo box's list of items, + // then highlight next element above currently highlighted element + if (list.getSelectedIndex() != 0) + list.setSelectedIndex(list.getSelectedIndex() - 1); } /** - * DOCUMENT ME! + * This method scrolls down list of combo box's and highlights item in the + * list that just became visible. */ protected void autoScrollDown() { - // FIXME: Need to implement + // scroll scrollbar down to make next item visible + JScrollBar scrollbar = scroller.getVerticalScrollBar(); + int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(), + SwingConstants.VERTICAL, + SCROLL_DOWN); + scrollbar.setValue(scrollbar.getValue() + scrollToNext); + + // If we haven't reached the end of the combo box's list of items + // then highlight next element below currently highlighted element + if (list.getSelectedIndex() + 1 != comboBox.getItemCount()) + list.setSelectedIndex(list.getSelectedIndex() + 1); } /** @@ -717,15 +748,36 @@ } /** - * This method is invoked whenever mouse is released + * This method is invoked whenever mouse event was originated in the combo + * box and released either in the combBox list of items or in the combo + * box itself. * * @param e MouseEvent that should be handled */ public void mouseReleased(MouseEvent e) { - // FIXME: should handle dragging events here, if - // mouse was dragged and released over the list of combobox's items, - // then item over which it was released should be selected. + // Get component over which mouse was released + Component src = (Component) e.getSource(); + int x = e.getX(); + int y = e.getY(); + Component releasedComponent = SwingUtilities.getDeepestComponentAt(src, + x, y); + + // if mouse was released inside the bounds of combo box then do nothing, + // Otherwise if mouse was released inside the list of combo box items + // then change selection and close popup + if (! (releasedComponent instanceof JComboBox)) + { + // List model contains the item over which mouse is released, + // since it is updated every time the mouse is moved over a different + // item in the list. Now that the mouse is released we need to + // update model of the combo box as well. + comboBox.setSelectedIndex(list.getSelectedIndex()); + + if (isAutoScrolling) + stopAutoScrolling(); + hide(); + } } } @@ -742,13 +794,70 @@ { } + /** + * This method is responsible for highlighting item in the drop down list + * over which the mouse is currently being dragged. + */ public void mouseDragged(MouseEvent e) { + // convert point of the drag event relative to combo box list component + // figure out over which list cell the mouse is currently being dragged + // and highlight the cell. The list model is changed but the change has + // no effect on combo box's data model. The list model is changed so + // that the appropriate item would be highlighted in the combo box's + // list. + if (BasicComboPopup.this.isVisible()) + { + int cbHeight = (int) comboBox.getPreferredSize().getHeight(); + int popupHeight = BasicComboPopup.this.getSize().height; + + // if mouse is dragged inside the the combo box's items list. + if (e.getY() > cbHeight && ! (e.getY() - cbHeight >= popupHeight)) + { + int index = list.locationToIndex(new Point(e.getX(), + (int) (e.getY() + - cbHeight))); + + int firstVisibleIndex = list.getFirstVisibleIndex(); + + // list.locationToIndex returns item's index that would + // be located at the specified point if the first item that + // is visible is item 0. However in the JComboBox it is not + // necessarily the case since list is contained in the + // JScrollPane so we need to adjust the index returned. + if (firstVisibleIndex != 0) + // FIXME: adjusted index here is off by one. I am adding one + // here to compensate for that. This should be + // index += firstVisibleIndex. Remove +1 once the bug is fixed. + index += firstVisibleIndex + 1; + + list.setSelectedIndex(index); + } + else + { + // if mouse is being dragged at the bottom of combo box's list + // of items or at the very top then scroll the list in the + // desired direction. + boolean movingUP = e.getY() < cbHeight; + boolean movingDown = e.getY() > cbHeight; + + if (movingUP) + { + scrollDirection = SCROLL_UP; + startAutoScrolling(SCROLL_UP); + } + else if (movingDown) + { + scrollDirection = SCROLL_DOWN; + startAutoScrolling(SCROLL_DOWN); + } + } + } } } /** - * ItemHandler is an item listener that listens to selection event occuring + * ItemHandler is an item listener that listens to selection events occuring * in the combo box. FIXME: should specify here what it does when item is * selected or deselected in the combo box list. */