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