001/* BasicComboPopup.java -- 002 Copyright (C) 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.plaf.basic; 040 041import java.awt.Color; 042import java.awt.Component; 043import java.awt.Dimension; 044import java.awt.Insets; 045import java.awt.Point; 046import java.awt.Rectangle; 047import java.awt.event.ItemEvent; 048import java.awt.event.ItemListener; 049import java.awt.event.KeyAdapter; 050import java.awt.event.KeyEvent; 051import java.awt.event.KeyListener; 052import java.awt.event.MouseAdapter; 053import java.awt.event.MouseEvent; 054import java.awt.event.MouseListener; 055import java.awt.event.MouseMotionAdapter; 056import java.awt.event.MouseMotionListener; 057import java.beans.PropertyChangeEvent; 058import java.beans.PropertyChangeListener; 059 060import javax.swing.BorderFactory; 061import javax.swing.ComboBoxModel; 062import javax.swing.JComboBox; 063import javax.swing.JList; 064import javax.swing.JPopupMenu; 065import javax.swing.JScrollBar; 066import javax.swing.JScrollPane; 067import javax.swing.ListCellRenderer; 068import javax.swing.ListSelectionModel; 069import javax.swing.MenuSelectionManager; 070import javax.swing.SwingConstants; 071import javax.swing.SwingUtilities; 072import javax.swing.Timer; 073import javax.swing.UIManager; 074import javax.swing.event.ListDataEvent; 075import javax.swing.event.ListDataListener; 076import javax.swing.event.ListSelectionEvent; 077import javax.swing.event.ListSelectionListener; 078import javax.swing.event.PopupMenuEvent; 079import javax.swing.event.PopupMenuListener; 080 081/** 082 * UI Delegate for ComboPopup 083 * 084 * @author Olga Rodimina 085 */ 086public class BasicComboPopup extends JPopupMenu implements ComboPopup 087{ 088 /* Timer for autoscrolling */ 089 protected Timer autoscrollTimer; 090 091 /** ComboBox associated with this popup */ 092 protected JComboBox comboBox; 093 094 /** FIXME: Need to document */ 095 protected boolean hasEntered; 096 097 /** 098 * Indicates whether the scroll bar located in popup menu with comboBox's 099 * list of items is currently autoscrolling. This happens when mouse event 100 * originated in the combo box and is dragged outside of its bounds 101 */ 102 protected boolean isAutoScrolling; 103 104 /** ItemListener listening to the selection changes in the combo box */ 105 protected ItemListener itemListener; 106 107 /** This listener is not used */ 108 protected KeyListener keyListener; 109 110 /** JList which is used to display item is the combo box */ 111 protected JList list; 112 113 /** This listener is not used */ 114 protected ListDataListener listDataListener; 115 116 /** 117 * MouseListener listening to mouse events occuring in the combo box's 118 * list. 119 */ 120 protected MouseListener listMouseListener; 121 122 /** 123 * MouseMotionListener listening to mouse motion events occuring in the 124 * combo box's list 125 */ 126 protected MouseMotionListener listMouseMotionListener; 127 128 /** This listener is not used */ 129 protected ListSelectionListener listSelectionListener; 130 131 /** MouseListener listening to mouse events occuring in the combo box */ 132 protected MouseListener mouseListener; 133 134 /** 135 * MouseMotionListener listening to mouse motion events occuring in the 136 * combo box 137 */ 138 protected MouseMotionListener mouseMotionListener; 139 140 /** 141 * PropertyChangeListener listening to changes occuring in the bound 142 * properties of the combo box 143 */ 144 protected PropertyChangeListener propertyChangeListener; 145 146 /** direction for scrolling down list of combo box's items */ 147 protected static final int SCROLL_DOWN = 1; 148 149 /** direction for scrolling up list of combo box's items */ 150 protected static final int SCROLL_UP = 0; 151 152 /** Indicates auto scrolling direction */ 153 protected int scrollDirection; 154 155 /** JScrollPane that contains list portion of the combo box */ 156 protected JScrollPane scroller; 157 158 /** This field is not used */ 159 protected boolean valueIsAdjusting; 160 161 /** 162 * Creates a new BasicComboPopup object. 163 * 164 * @param comboBox the combo box with which this popup should be associated 165 */ 166 public BasicComboPopup(JComboBox comboBox) 167 { 168 this.comboBox = comboBox; 169 mouseListener = createMouseListener(); 170 mouseMotionListener = createMouseMotionListener(); 171 keyListener = createKeyListener(); 172 173 list = createList(); 174 configureList(); 175 scroller = createScroller(); 176 configureScroller(); 177 configurePopup(); 178 installComboBoxListeners(); 179 installKeyboardActions(); 180 } 181 182 /** 183 * This method displays drow down list of combo box items on the screen. 184 */ 185 public void show() 186 { 187 Dimension size = comboBox.getSize(); 188 size.height = getPopupHeightForRowCount(comboBox.getMaximumRowCount()); 189 Insets i = getInsets(); 190 size.width -= i.left + i.right; 191 Rectangle bounds = computePopupBounds(0, comboBox.getBounds().height, 192 size.width, size.height); 193 194 scroller.setMaximumSize(bounds.getSize()); 195 scroller.setPreferredSize(bounds.getSize()); 196 scroller.setMinimumSize(bounds.getSize()); 197 list.invalidate(); 198 199 syncListSelection(); 200 201 list.ensureIndexIsVisible(list.getSelectedIndex()); 202 setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled()); 203 show(comboBox, bounds.x, bounds.y); 204 } 205 206 /** 207 * This method hides drop down list of items 208 */ 209 public void hide() 210 { 211 MenuSelectionManager menuSelectionManager = 212 MenuSelectionManager.defaultManager(); 213 javax.swing.MenuElement[] menuElements = 214 menuSelectionManager.getSelectedPath(); 215 for (int i = 0; i < menuElements.length; i++) 216 { 217 if (menuElements[i] == this) 218 { 219 menuSelectionManager.clearSelectedPath(); 220 break; 221 } 222 } 223 comboBox.repaint(); 224 } 225 226 /** 227 * Return list cointaining JComboBox's items 228 * 229 * @return list cointaining JComboBox's items 230 */ 231 public JList getList() 232 { 233 return list; 234 } 235 236 /** 237 * Returns MouseListener that is listening to mouse events occuring in the 238 * combo box. 239 * 240 * @return MouseListener 241 */ 242 public MouseListener getMouseListener() 243 { 244 return mouseListener; 245 } 246 247 /** 248 * Returns MouseMotionListener that is listening to mouse motion events 249 * occuring in the combo box. 250 * 251 * @return MouseMotionListener 252 */ 253 public MouseMotionListener getMouseMotionListener() 254 { 255 return mouseMotionListener; 256 } 257 258 /** 259 * Returns KeyListener listening to key events occuring in the combo box. 260 * This method returns null because KeyHandler is not longer used. 261 * 262 * @return KeyListener 263 */ 264 public KeyListener getKeyListener() 265 { 266 return keyListener; 267 } 268 269 /** 270 * This method uninstalls the UI for the given JComponent. 271 */ 272 public void uninstallingUI() 273 { 274 if (propertyChangeListener != null) 275 { 276 comboBox.removePropertyChangeListener(propertyChangeListener); 277 } 278 if (itemListener != null) 279 { 280 comboBox.removeItemListener(itemListener); 281 } 282 uninstallComboBoxModelListeners(comboBox.getModel()); 283 uninstallKeyboardActions(); 284 uninstallListListeners(); 285 } 286 287 /** 288 * This method uninstalls listeners that were listening to changes occuring 289 * in the comb box's data model 290 * 291 * @param model data model for the combo box from which to uninstall 292 * listeners 293 */ 294 protected void uninstallComboBoxModelListeners(ComboBoxModel model) 295 { 296 model.removeListDataListener(listDataListener); 297 } 298 299 /** 300 * This method uninstalls keyboard actions installed by the UI. 301 */ 302 protected void uninstallKeyboardActions() 303 { 304 // Nothing to do here. 305 } 306 307 /** 308 * This method fires PopupMenuEvent indicating that combo box's popup list 309 * of items will become visible 310 */ 311 protected void firePopupMenuWillBecomeVisible() 312 { 313 PopupMenuListener[] ll = comboBox.getPopupMenuListeners(); 314 315 for (int i = 0; i < ll.length; i++) 316 ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox)); 317 } 318 319 /** 320 * This method fires PopupMenuEvent indicating that combo box's popup list 321 * of items will become invisible. 322 */ 323 protected void firePopupMenuWillBecomeInvisible() 324 { 325 PopupMenuListener[] ll = comboBox.getPopupMenuListeners(); 326 327 for (int i = 0; i < ll.length; i++) 328 ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox)); 329 } 330 331 /** 332 * This method fires PopupMenuEvent indicating that combo box's popup list 333 * of items was closed without selection. 334 */ 335 protected void firePopupMenuCanceled() 336 { 337 PopupMenuListener[] ll = comboBox.getPopupMenuListeners(); 338 339 for (int i = 0; i < ll.length; i++) 340 ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox)); 341 } 342 343 /** 344 * Creates MouseListener to listen to mouse events occuring in the combo 345 * box. Note that this listener doesn't listen to mouse events occuring in 346 * the popup portion of the combo box, it only listens to main combo box 347 * part. 348 * 349 * @return new MouseMotionListener that listens to mouse events occuring in 350 * the combo box 351 */ 352 protected MouseListener createMouseListener() 353 { 354 return new InvocationMouseHandler(); 355 } 356 357 /** 358 * Create Mouse listener that listens to mouse dragging events occuring in 359 * the combo box. This listener is responsible for changing the selection 360 * in the combo box list to the component over which mouse is being 361 * currently dragged 362 * 363 * @return new MouseMotionListener that listens to mouse dragging events 364 * occuring in the combo box 365 */ 366 protected MouseMotionListener createMouseMotionListener() 367 { 368 return new InvocationMouseMotionHandler(); 369 } 370 371 /** 372 * KeyListener created in this method is not used anymore. 373 * 374 * @return KeyListener that does nothing 375 */ 376 protected KeyListener createKeyListener() 377 { 378 return new InvocationKeyHandler(); 379 } 380 381 /** 382 * ListSelectionListener created in this method is not used anymore 383 * 384 * @return ListSelectionListener that does nothing 385 */ 386 protected ListSelectionListener createListSelectionListener() 387 { 388 return new ListSelectionHandler(); 389 } 390 391 /** 392 * Creates ListDataListener. This method returns null, because 393 * ListDataHandler class is obsolete and is no longer used. 394 * 395 * @return null 396 */ 397 protected ListDataListener createListDataListener() 398 { 399 return null; 400 } 401 402 /** 403 * This method creates ListMouseListener to listen to mouse events occuring 404 * in the combo box's item list. 405 * 406 * @return MouseListener to listen to mouse events occuring in the combo 407 * box's items list. 408 */ 409 protected MouseListener createListMouseListener() 410 { 411 return new ListMouseHandler(); 412 } 413 414 /** 415 * Creates ListMouseMotionlistener to listen to mouse motion events occuring 416 * in the combo box's list. This listener is responsible for highlighting 417 * items in the list when mouse is moved over them. 418 * 419 * @return MouseMotionListener that handles mouse motion events occuring in 420 * the list of the combo box. 421 */ 422 protected MouseMotionListener createListMouseMotionListener() 423 { 424 return new ListMouseMotionHandler(); 425 } 426 427 /** 428 * Creates PropertyChangeListener to handle changes in the JComboBox's bound 429 * properties. 430 * 431 * @return PropertyChangeListener to handle changes in the JComboBox's bound 432 * properties. 433 */ 434 protected PropertyChangeListener createPropertyChangeListener() 435 { 436 return new PropertyChangeHandler(); 437 } 438 439 /** 440 * Creates new ItemListener that will listen to ItemEvents occuring in the 441 * combo box. 442 * 443 * @return ItemListener to listen to ItemEvents occuring in the combo box. 444 */ 445 protected ItemListener createItemListener() 446 { 447 return new ItemHandler(); 448 } 449 450 /** 451 * Creates JList that will be used to display items in the combo box. 452 * 453 * @return JList that will be used to display items in the combo box. 454 */ 455 protected JList createList() 456 { 457 JList l = new JList(comboBox.getModel()); 458 return l; 459 } 460 461 /** 462 * This method configures the list of comboBox's items by setting default 463 * properties and installing listeners. 464 */ 465 protected void configureList() 466 { 467 list.setFont(comboBox.getFont()); 468 list.setForeground(comboBox.getForeground()); 469 list.setBackground(comboBox.getBackground()); 470 Color sfg = UIManager.getColor("ComboBox.selectionForeground"); 471 list.setSelectionForeground(sfg); 472 Color sbg = UIManager.getColor("ComboBox.selectionBackground"); 473 list.setSelectionBackground(sbg); 474 list.setBorder(null); 475 list.setCellRenderer(comboBox.getRenderer()); 476 list.setFocusable(false); 477 list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 478 installListListeners(); 479 } 480 481 /** 482 * This method installs list listeners. 483 */ 484 protected void installListListeners() 485 { 486 // mouse listener listening to mouse events occuring in the 487 // combo box's list of items. 488 listMouseListener = createListMouseListener(); 489 list.addMouseListener(listMouseListener); 490 491 // mouse listener listening to mouse motion events occuring in the 492 // combo box's list of items 493 listMouseMotionListener = createListMouseMotionListener(); 494 list.addMouseMotionListener(listMouseMotionListener); 495 496 listSelectionListener = createListSelectionListener(); 497 list.addListSelectionListener(listSelectionListener); 498 } 499 500 /** 501 * This method creates scroll pane that will contain the list of comboBox's 502 * items inside of it. 503 * 504 * @return JScrollPane 505 */ 506 protected JScrollPane createScroller() 507 { 508 return new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 509 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 510 } 511 512 /** 513 * This method configures scroll pane to contain list of comboBox's items 514 */ 515 protected void configureScroller() 516 { 517 scroller.setBorder(null); 518 scroller.setFocusable(false); 519 scroller.getVerticalScrollBar().setFocusable(false); 520 } 521 522 /** 523 * This method configures popup menu that will be used to display Scrollpane 524 * with list of items inside of it. 525 */ 526 protected void configurePopup() 527 { 528 setBorderPainted(true); 529 setBorder(BorderFactory.createLineBorder(Color.BLACK)); 530 setOpaque(false); 531 add(scroller); 532 setFocusable(false); 533 } 534 535 /* 536 * This method installs listeners that will listen to changes occuring 537 * in the combo box. 538 */ 539 protected void installComboBoxListeners() 540 { 541 // item listener listenening to selection events in the combo box 542 itemListener = createItemListener(); 543 comboBox.addItemListener(itemListener); 544 545 propertyChangeListener = createPropertyChangeListener(); 546 comboBox.addPropertyChangeListener(propertyChangeListener); 547 548 installComboBoxModelListeners(comboBox.getModel()); 549 } 550 551 /** 552 * This method installs listeners that will listen to changes occuring in 553 * the comb box's data model 554 * 555 * @param model data model for the combo box for which to install listeners 556 */ 557 protected void installComboBoxModelListeners(ComboBoxModel model) 558 { 559 // list data listener to listen for ListDataEvents in combo box. 560 // This listener is now obsolete and nothing is done here 561 listDataListener = createListDataListener(); 562 comboBox.getModel().addListDataListener(listDataListener); 563 } 564 565 /** 566 * Installs the keyboard actions. 567 */ 568 protected void installKeyboardActions() 569 { 570 // Nothing to do here 571 } 572 573 /** 574 * This method always returns false to indicate that items in the combo box 575 * list are not focus traversable. 576 * 577 * @return false 578 */ 579 public boolean isFocusTraversable() 580 { 581 return false; 582 } 583 584 /** 585 * This method start scrolling combo box's list of items either up or down 586 * depending on the specified 'direction' 587 * 588 * @param direction of the scrolling. 589 */ 590 protected void startAutoScrolling(int direction) 591 { 592 // FIXME: add timer 593 isAutoScrolling = true; 594 595 if (direction == SCROLL_UP) 596 autoScrollUp(); 597 else 598 autoScrollDown(); 599 } 600 601 /** 602 * This method stops scrolling the combo box's list of items 603 */ 604 protected void stopAutoScrolling() 605 { 606 // FIXME: add timer 607 isAutoScrolling = false; 608 } 609 610 /** 611 * This method scrolls up list of combo box's items up and highlights that 612 * just became visible. 613 */ 614 protected void autoScrollUp() 615 { 616 // scroll up the scroll bar to make the item above visible 617 JScrollBar scrollbar = scroller.getVerticalScrollBar(); 618 int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(), 619 SwingConstants.VERTICAL, 620 SCROLL_UP); 621 622 scrollbar.setValue(scrollbar.getValue() - scrollToNext); 623 624 // If we haven't reached the begging of the combo box's list of items, 625 // then highlight next element above currently highlighted element 626 if (list.getSelectedIndex() != 0) 627 list.setSelectedIndex(list.getSelectedIndex() - 1); 628 } 629 630 /** 631 * This method scrolls down list of combo box's and highlights item in the 632 * list that just became visible. 633 */ 634 protected void autoScrollDown() 635 { 636 // scroll scrollbar down to make next item visible 637 JScrollBar scrollbar = scroller.getVerticalScrollBar(); 638 int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(), 639 SwingConstants.VERTICAL, 640 SCROLL_DOWN); 641 scrollbar.setValue(scrollbar.getValue() + scrollToNext); 642 643 // If we haven't reached the end of the combo box's list of items 644 // then highlight next element below currently highlighted element 645 if (list.getSelectedIndex() + 1 != comboBox.getItemCount()) 646 list.setSelectedIndex(list.getSelectedIndex() + 1); 647 } 648 649 /** 650 * This method helps to delegate focus to the right component in the 651 * JComboBox. If the comboBox is editable then focus is sent to 652 * ComboBoxEditor, otherwise it is delegated to JComboBox. 653 * 654 * @param e MouseEvent 655 */ 656 protected void delegateFocus(MouseEvent e) 657 { 658 if (comboBox.isEditable()) 659 comboBox.getEditor().getEditorComponent().requestFocus(); 660 else 661 comboBox.requestFocus(); 662 } 663 664 /** 665 * This method displays combo box popup if the popup is not currently shown 666 * on the screen and hides it if it is currently visible 667 */ 668 protected void togglePopup() 669 { 670 if (isVisible()) 671 hide(); 672 else 673 show(); 674 } 675 676 /** 677 * DOCUMENT ME! 678 * 679 * @param e DOCUMENT ME! 680 * 681 * @return DOCUMENT ME! 682 */ 683 protected MouseEvent convertMouseEvent(MouseEvent e) 684 { 685 Point point = SwingUtilities.convertPoint((Component) e.getSource(), 686 e.getPoint(), list); 687 MouseEvent newEvent = new MouseEvent((Component) e.getSource(), 688 e.getID(), e.getWhen(), 689 e.getModifiers(), point.x, point.y, 690 e.getModifiers(), 691 e.isPopupTrigger()); 692 return newEvent; 693 } 694 695 /** 696 * Returns required height of the popup such that number of items visible in 697 * it are equal to the maximum row count. By default 698 * comboBox.maximumRowCount=8 699 * 700 * @param maxRowCount number of maximum visible rows in the combo box's 701 * popup list of items 702 * 703 * @return height of the popup required to fit number of items equal to 704 * JComboBox.maximumRowCount. 705 */ 706 protected int getPopupHeightForRowCount(int maxRowCount) 707 { 708 int totalHeight = 0; 709 ListCellRenderer rend = list.getCellRenderer(); 710 711 if (comboBox.getItemCount() < maxRowCount) 712 maxRowCount = comboBox.getItemCount(); 713 714 for (int i = 0; i < maxRowCount; i++) 715 { 716 Component comp = rend.getListCellRendererComponent(list, 717 comboBox.getModel() 718 .getElementAt(i), 719 -1, false, false); 720 Dimension dim = comp.getPreferredSize(); 721 totalHeight += dim.height; 722 } 723 724 return totalHeight == 0 ? 100 : totalHeight; 725 } 726 727 /** 728 * DOCUMENT ME! 729 * 730 * @param px DOCUMENT ME! 731 * @param py DOCUMENT ME! 732 * @param pw DOCUMENT ME! 733 * @param ph DOCUMENT ME! 734 * 735 * @return DOCUMENT ME! 736 */ 737 protected Rectangle computePopupBounds(int px, int py, int pw, int ph) 738 { 739 return new Rectangle(px, py, pw, ph); 740 } 741 742 /** 743 * This method changes the selection in the list to the item over which the 744 * mouse is currently located. 745 * 746 * @param anEvent MouseEvent 747 * @param shouldScroll DOCUMENT ME! 748 */ 749 protected void updateListBoxSelectionForEvent(MouseEvent anEvent, 750 boolean shouldScroll) 751 { 752 Point point = anEvent.getPoint(); 753 if (list != null) 754 { 755 int index = list.locationToIndex(point); 756 if (index == -1) 757 { 758 if (point.y < 0) 759 index = 0; 760 else 761 index = comboBox.getModel().getSize() - 1; 762 } 763 if (list.getSelectedIndex() != index) 764 { 765 list.setSelectedIndex(index); 766 if (shouldScroll) 767 list.ensureIndexIsVisible(index); 768 } 769 } 770 } 771 772 /** 773 * InvocationMouseHandler is a listener that listens to mouse events 774 * occuring in the combo box. Note that this listener doesn't listen to 775 * mouse events occuring in the popup portion of the combo box, it only 776 * listens to main combo box part(area that displays selected item). This 777 * listener is responsible for showing and hiding popup portion of the 778 * combo box. 779 */ 780 protected class InvocationMouseHandler extends MouseAdapter 781 { 782 /** 783 * Creates a new InvocationMouseHandler object. 784 */ 785 protected InvocationMouseHandler() 786 { 787 // Nothing to do here. 788 } 789 790 /** 791 * This method is invoked whenever mouse is being pressed over the main 792 * part of the combo box. This method will show popup if the popup is 793 * not shown on the screen right now, and it will hide popup otherwise. 794 * 795 * @param e MouseEvent that should be handled 796 */ 797 public void mousePressed(MouseEvent e) 798 { 799 if (SwingUtilities.isLeftMouseButton(e) && comboBox.isEnabled()) 800 { 801 delegateFocus(e); 802 togglePopup(); 803 } 804 } 805 806 /** 807 * This method is invoked whenever mouse event was originated in the combo 808 * box and released either in the combBox list of items or in the combo 809 * box itself. 810 * 811 * @param e MouseEvent that should be handled 812 */ 813 public void mouseReleased(MouseEvent e) 814 { 815 Component component = (Component) e.getSource(); 816 Dimension size = component.getSize(); 817 Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1); 818 // If mouse was released inside the bounds of combo box then do nothing, 819 // Otherwise if mouse was released inside the list of combo box items 820 // then change selection and close popup 821 if (! bounds.contains(e.getPoint())) 822 { 823 MouseEvent convEvent = convertMouseEvent(e); 824 Point point = convEvent.getPoint(); 825 Rectangle visRect = new Rectangle(); 826 list.computeVisibleRect(visRect); 827 if (visRect.contains(point)) 828 { 829 updateListBoxSelectionForEvent(convEvent, false); 830 comboBox.setSelectedIndex(list.getSelectedIndex()); 831 } 832 hide(); 833 } 834 hasEntered = false; 835 stopAutoScrolling(); 836 } 837 } 838 839 /** 840 * InvocationMouseMotionListener is a mouse listener that listens to mouse 841 * dragging events occuring in the combo box. 842 */ 843 protected class InvocationMouseMotionHandler extends MouseMotionAdapter 844 { 845 /** 846 * Creates a new InvocationMouseMotionHandler object. 847 */ 848 protected InvocationMouseMotionHandler() 849 { 850 // Nothing to do here. 851 } 852 853 /** 854 * This method is responsible for highlighting item in the drop down list 855 * over which the mouse is currently being dragged. 856 */ 857 public void mouseDragged(MouseEvent e) 858 { 859 if (isVisible()) 860 { 861 MouseEvent convEvent = convertMouseEvent(e); 862 Rectangle visRect = new Rectangle(); 863 list.computeVisibleRect(visRect); 864 if (convEvent.getPoint().y >= visRect.y 865 && (convEvent.getPoint().y <= visRect.y + visRect.height - 1)) 866 { 867 hasEntered = true; 868 if (isAutoScrolling) 869 stopAutoScrolling(); 870 Point point = convEvent.getPoint(); 871 if (visRect.contains(point)) 872 { 873 valueIsAdjusting = true; 874 updateListBoxSelectionForEvent(convEvent, false); 875 valueIsAdjusting = false; 876 } 877 } 878 else if (hasEntered) 879 { 880 int dir = convEvent.getPoint().y < visRect.y ? SCROLL_UP 881 : SCROLL_DOWN; 882 if (isAutoScrolling && scrollDirection != dir) 883 { 884 stopAutoScrolling(); 885 startAutoScrolling(dir); 886 } 887 else if (!isAutoScrolling) 888 startAutoScrolling(dir); 889 } 890 else if (e.getPoint().y < 0) 891 { 892 hasEntered = true; 893 startAutoScrolling(SCROLL_UP); 894 } 895 } 896 } 897 } 898 899 /** 900 * ItemHandler is an item listener that listens to selection events occuring 901 * in the combo box. FIXME: should specify here what it does when item is 902 * selected or deselected in the combo box list. 903 */ 904 protected class ItemHandler extends Object implements ItemListener 905 { 906 /** 907 * Creates a new ItemHandler object. 908 */ 909 protected ItemHandler() 910 { 911 // Nothing to do here. 912 } 913 914 /** 915 * This method responds to the selection events occuring in the combo box. 916 * 917 * @param e ItemEvent specifying the combo box's selection 918 */ 919 public void itemStateChanged(ItemEvent e) 920 { 921 if (e.getStateChange() == ItemEvent.SELECTED && ! valueIsAdjusting) 922 { 923 valueIsAdjusting = true; 924 syncListSelection(); 925 valueIsAdjusting = false; 926 list.ensureIndexIsVisible(comboBox.getSelectedIndex()); 927 } 928 } 929 } 930 931 /** 932 * ListMouseHandler is a listener that listens to mouse events occuring in 933 * the combo box's list of items. This class is responsible for hiding 934 * popup portion of the combo box if the mouse is released inside the combo 935 * box's list. 936 */ 937 protected class ListMouseHandler extends MouseAdapter 938 { 939 protected ListMouseHandler() 940 { 941 // Nothing to do here. 942 } 943 944 public void mousePressed(MouseEvent e) 945 { 946 // Nothing to do here. 947 } 948 949 public void mouseReleased(MouseEvent anEvent) 950 { 951 comboBox.setSelectedIndex(list.getSelectedIndex()); 952 hide(); 953 } 954 } 955 956 /** 957 * ListMouseMotionHandler listens to mouse motion events occuring in the 958 * combo box's list. This class is responsible for highlighting items in 959 * the list when mouse is moved over them 960 */ 961 protected class ListMouseMotionHandler extends MouseMotionAdapter 962 { 963 protected ListMouseMotionHandler() 964 { 965 // Nothing to do here. 966 } 967 968 public void mouseMoved(MouseEvent anEvent) 969 { 970 Point point = anEvent.getPoint(); 971 Rectangle visRect = new Rectangle(); 972 list.computeVisibleRect(visRect); 973 if (visRect.contains(point)) 974 { 975 valueIsAdjusting = true; 976 updateListBoxSelectionForEvent(anEvent, false); 977 valueIsAdjusting = false; 978 } 979 } 980 } 981 982 /** 983 * This class listens to changes occuring in the bound properties of the 984 * combo box 985 */ 986 protected class PropertyChangeHandler extends Object 987 implements PropertyChangeListener 988 { 989 protected PropertyChangeHandler() 990 { 991 // Nothing to do here. 992 } 993 994 public void propertyChange(PropertyChangeEvent e) 995 { 996 if (e.getPropertyName().equals("renderer")) 997 { 998 list.setCellRenderer(comboBox.getRenderer()); 999 if (isVisible()) 1000 hide(); 1001 } 1002 if (e.getPropertyName().equals("model")) 1003 { 1004 ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue(); 1005 uninstallComboBoxModelListeners(oldModel); 1006 ComboBoxModel newModel = (ComboBoxModel) e.getNewValue(); 1007 list.setModel(newModel); 1008 installComboBoxModelListeners(newModel); 1009 if (comboBox.getItemCount() > 0) 1010 comboBox.setSelectedIndex(0); 1011 if (isVisible()) 1012 hide(); 1013 } 1014 } 1015 } 1016 1017 // ------ private helper methods -------------------- 1018 1019 /** 1020 * This method uninstalls Listeners registered with combo boxes list of 1021 * items 1022 */ 1023 private void uninstallListListeners() 1024 { 1025 list.removeMouseListener(listMouseListener); 1026 listMouseListener = null; 1027 1028 list.removeMouseMotionListener(listMouseMotionListener); 1029 listMouseMotionListener = null; 1030 } 1031 1032 void syncListSelection() 1033 { 1034 int index = comboBox.getSelectedIndex(); 1035 if (index == -1) 1036 list.clearSelection(); 1037 else 1038 list.setSelectedIndex(index); 1039 } 1040 1041 // -------------------------------------------------------------------- 1042 // The following classes are here only for backwards API compatibility 1043 // They aren't used. 1044 // -------------------------------------------------------------------- 1045 1046 /** 1047 * This class is not used any more. 1048 */ 1049 public class ListDataHandler extends Object implements ListDataListener 1050 { 1051 public ListDataHandler() 1052 { 1053 // Nothing to do here. 1054 } 1055 1056 public void contentsChanged(ListDataEvent e) 1057 { 1058 // Nothing to do here. 1059 } 1060 1061 public void intervalAdded(ListDataEvent e) 1062 { 1063 // Nothing to do here. 1064 } 1065 1066 public void intervalRemoved(ListDataEvent e) 1067 { 1068 // Nothing to do here. 1069 } 1070 } 1071 1072 /** 1073 * This class is not used anymore 1074 */ 1075 protected class ListSelectionHandler extends Object 1076 implements ListSelectionListener 1077 { 1078 protected ListSelectionHandler() 1079 { 1080 // Nothing to do here. 1081 } 1082 1083 public void valueChanged(ListSelectionEvent e) 1084 { 1085 // Nothing to do here. 1086 } 1087 } 1088 1089 /** 1090 * This class is not used anymore 1091 */ 1092 public class InvocationKeyHandler extends KeyAdapter 1093 { 1094 public InvocationKeyHandler() 1095 { 1096 // Nothing to do here. 1097 } 1098 1099 public void keyReleased(KeyEvent e) 1100 { 1101 // Nothing to do here. 1102 } 1103 } 1104}