Source for javax.swing.plaf.basic.BasicComboPopup

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