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.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:     list.setFocusable(false);
 446:     installListListeners();
 447:   }
 448: 
 449:   /**
 450:    * This method installs list listeners.
 451:    */
 452:   protected void installListListeners()
 453:   {
 454:     // mouse listener listening to mouse events occuring in the 
 455:     // combo box's list of items.
 456:     listMouseListener = createListMouseListener();
 457:     list.addMouseListener(listMouseListener);
 458: 
 459:     // mouse listener listening to mouse motion events occuring in the
 460:     // combo box's list of items
 461:     listMouseMotionListener = createListMouseMotionListener();
 462:     list.addMouseMotionListener(listMouseMotionListener);
 463: 
 464:     listSelectionListener = createListSelectionListener();
 465:     list.addListSelectionListener(listSelectionListener);
 466:   }
 467: 
 468:   /**
 469:    * This method creates scroll pane that will contain the list of comboBox's
 470:    * items inside of it.
 471:    *
 472:    * @return JScrollPane
 473:    */
 474:   protected JScrollPane createScroller()
 475:   {
 476:     return new JScrollPane();
 477:   }
 478: 
 479:   /**
 480:    * This method configures scroll pane to contain list of comboBox's  items
 481:    */
 482:   protected void configureScroller()
 483:   {
 484:     scroller.setBorder(null);
 485:     scroller.getViewport().setView(list);
 486:     scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 487:   }
 488: 
 489:   /**
 490:    * This method configures popup menu that will be used to display Scrollpane
 491:    * with list of items inside of it.
 492:    */
 493:   protected void configurePopup()
 494:   {
 495:     setBorder(BorderFactory.createLineBorder(Color.BLACK));
 496:     // initialize list that will be used to display combo box's items
 497:     this.list = createList();
 498:     ((JLabel) list.getCellRenderer()).setHorizontalAlignment(SwingConstants.LEFT);
 499:     configureList();
 500: 
 501:     // initialize scroller. Add list to the scroller.    
 502:     scroller = createScroller();
 503:     configureScroller();
 504: 
 505:     // add scroller with list inside of it to JPopupMenu
 506:     super.add(scroller);
 507:   }
 508: 
 509:   /*
 510:    * This method installs listeners that will listen to changes occuring
 511:    * in the combo box.
 512:    */
 513:   protected void installComboBoxListeners()
 514:   {
 515:     // mouse listener that listens to mouse event in combo box
 516:     mouseListener = createMouseListener();
 517:     comboBox.addMouseListener(mouseListener);
 518: 
 519:     // mouse listener that listens to mouse dragging events in the combo box
 520:     mouseMotionListener = createMouseMotionListener();
 521:     comboBox.addMouseMotionListener(mouseMotionListener);
 522: 
 523:     // item listener listenening to selection events in the combo box
 524:     itemListener = createItemListener();
 525:     comboBox.addItemListener(itemListener);
 526: 
 527:     propertyChangeListener = createPropertyChangeListener();
 528:     comboBox.addPropertyChangeListener(propertyChangeListener);
 529:   }
 530: 
 531:   /**
 532:    * This method installs listeners that will listen to changes occuring in
 533:    * the comb box's data model
 534:    *
 535:    * @param model data model for the combo box for which to install listeners
 536:    */
 537:   protected void installComboBoxModelListeners(ComboBoxModel model)
 538:   {
 539:     // list data listener to listen for ListDataEvents in combo box.
 540:     // This listener is now obsolete and nothing is done here
 541:     listDataListener = createListDataListener();
 542:     comboBox.getModel().addListDataListener(listDataListener);
 543:   }
 544: 
 545:   /**
 546:    * DOCUMENT ME!
 547:    */
 548:   protected void installKeyboardActions()
 549:   {
 550:     // FIXME: Need to implement
 551:   }
 552: 
 553:   /**
 554:    * This method always returns false to indicate that  items in the combo box
 555:    * list are not focus traversable.
 556:    *
 557:    * @return false
 558:    */
 559:   public boolean isFocusTraversable()
 560:   {
 561:     return false;
 562:   }
 563: 
 564:   /**
 565:    * This method start scrolling combo box's list of items  either up or down
 566:    * depending on the specified 'direction'
 567:    *
 568:    * @param direction of the scrolling.
 569:    */
 570:   protected void startAutoScrolling(int direction)
 571:   {
 572:     // FIXME: add timer
 573:     isAutoScrolling = true;
 574: 
 575:     if (direction == SCROLL_UP)
 576:       autoScrollUp();
 577:     else
 578:       autoScrollDown();
 579:   }
 580: 
 581:   /**
 582:    * This method stops scrolling the combo box's list of items
 583:    */
 584:   protected void stopAutoScrolling()
 585:   {
 586:     // FIXME: add timer
 587:     isAutoScrolling = false;
 588:   }
 589: 
 590:   /**
 591:    * This method scrolls up list of combo box's items up and highlights that
 592:    * just became visible.
 593:    */
 594:   protected void autoScrollUp()
 595:   {
 596:     // scroll up the scroll bar to make the item above visible    
 597:     JScrollBar scrollbar = scroller.getVerticalScrollBar();
 598:     int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
 599:                                                        SwingConstants.VERTICAL,
 600:                                                        SCROLL_UP);
 601: 
 602:     scrollbar.setValue(scrollbar.getValue() - scrollToNext);
 603: 
 604:     // If we haven't reached the begging of the combo box's list of items, 
 605:     // then highlight next element above currently highlighted element    
 606:     if (list.getSelectedIndex() != 0)
 607:       list.setSelectedIndex(list.getSelectedIndex() - 1);
 608:   }
 609: 
 610:   /**
 611:    * This method scrolls down list of combo box's and highlights item in the
 612:    * list that just became visible.
 613:    */
 614:   protected void autoScrollDown()
 615:   {
 616:     // scroll scrollbar down to make next item visible    
 617:     JScrollBar scrollbar = scroller.getVerticalScrollBar();
 618:     int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
 619:                                                        SwingConstants.VERTICAL,
 620:                                                        SCROLL_DOWN);
 621:     scrollbar.setValue(scrollbar.getValue() + scrollToNext);
 622: 
 623:     // If we haven't reached the end of the combo box's list of items
 624:     // then highlight next element below currently highlighted element
 625:     if (list.getSelectedIndex() + 1 != comboBox.getItemCount())
 626:       list.setSelectedIndex(list.getSelectedIndex() + 1);
 627:   }
 628: 
 629:   /**
 630:    * This method helps to delegate focus to the right component in the
 631:    * JComboBox. If the comboBox is editable then focus is sent to
 632:    * ComboBoxEditor, otherwise it is delegated to JComboBox.
 633:    *
 634:    * @param e MouseEvent
 635:    */
 636:   protected void delegateFocus(MouseEvent e)
 637:   {
 638:     // FIXME: Need to implement
 639:   }
 640: 
 641:   /**
 642:    * This method displays combo box popup if the popup is  not currently shown
 643:    * on the screen and hides it if it is  currently visible
 644:    */
 645:   protected void togglePopup()
 646:   {
 647:     if (BasicComboPopup.this.isVisible())
 648:       hide();
 649:     else
 650:       show();
 651:   }
 652: 
 653:   /**
 654:    * DOCUMENT ME!
 655:    *
 656:    * @param e DOCUMENT ME!
 657:    *
 658:    * @return DOCUMENT ME!
 659:    */
 660:   protected MouseEvent convertMouseEvent(MouseEvent e)
 661:   {
 662:     return null;
 663:   }
 664: 
 665:   /**
 666:    * Returns required height of the popup such that number of items visible in
 667:    * it are equal to the maximum row count.  By default
 668:    * comboBox.maximumRowCount=8
 669:    *
 670:    * @param maxRowCount number of maximum visible rows in the  combo box's
 671:    *        popup list of items
 672:    *
 673:    * @return height of the popup required to fit number of items  equal to
 674:    *         JComboBox.maximumRowCount.
 675:    */
 676:   protected int getPopupHeightForRowCount(int maxRowCount)
 677:   {
 678:     int totalHeight = 0;
 679:     ListCellRenderer rend = list.getCellRenderer();
 680: 
 681:     if (comboBox.getItemCount() < maxRowCount)
 682:       maxRowCount = comboBox.getItemCount();
 683: 
 684:     for (int i = 0; i < maxRowCount; i++)
 685:       {
 686:     Component comp = rend.getListCellRendererComponent(list,
 687:                                                        comboBox.getModel()
 688:                                                                .getElementAt(i),
 689:                                                        -1, false, false);
 690:     Dimension dim = comp.getPreferredSize();
 691:     totalHeight += dim.height;
 692:       }
 693: 
 694:     return totalHeight;
 695:   }
 696: 
 697:   /**
 698:    * DOCUMENT ME!
 699:    *
 700:    * @param px DOCUMENT ME!
 701:    * @param py DOCUMENT ME!
 702:    * @param pw DOCUMENT ME!
 703:    * @param ph DOCUMENT ME!
 704:    *
 705:    * @return DOCUMENT ME!
 706:    */
 707:   protected Rectangle computePopupBounds(int px, int py, int pw, int ph)
 708:   {
 709:     return new Rectangle(px, py, pw, ph);
 710:   }
 711: 
 712:   /**
 713:    * This method changes the selection in the list to the item over which  the
 714:    * mouse is currently located.
 715:    *
 716:    * @param anEvent MouseEvent
 717:    * @param shouldScroll DOCUMENT ME!
 718:    */
 719:   protected void updateListBoxSelectionForEvent(MouseEvent anEvent,
 720:                                                 boolean shouldScroll)
 721:   {
 722:     // TODO: We need to handle the shouldScroll parameter somehow.
 723:     int index = list.locationToIndex(anEvent.getPoint());
 724:     // Check for valid index.
 725:     if (index >= 0)
 726:       list.setSelectedIndex(index);
 727:   }
 728: 
 729:   /**
 730:    * InvocationMouseHandler is a listener that listens to mouse events
 731:    * occuring in the combo box. Note that this listener doesn't listen to
 732:    * mouse events occuring in the popup portion of the combo box, it only
 733:    * listens to main combo box part(area that displays selected item).  This
 734:    * listener is responsible for showing and hiding popup portion  of the
 735:    * combo box.
 736:    */
 737:   protected class InvocationMouseHandler extends MouseAdapter
 738:   {
 739:     /**
 740:      * Creates a new InvocationMouseHandler object.
 741:      */
 742:     protected InvocationMouseHandler()
 743:     {
 744:       // Nothing to do here.
 745:     }
 746: 
 747:     /**
 748:      * This method is invoked whenever mouse is being pressed over the main
 749:      * part of the combo box. This method will show popup if  the popup is
 750:      * not shown on the screen right now, and it will hide popup otherwise.
 751:      *
 752:      * @param e MouseEvent that should be handled
 753:      */
 754:     public void mousePressed(MouseEvent e)
 755:     {
 756:       if (comboBox.isEnabled())
 757:         togglePopup();
 758:     }
 759: 
 760:     /**
 761:      * This method is invoked whenever mouse event was originated in the combo
 762:      * box and released either in the combBox list of items or in the combo
 763:      * box itself.
 764:      *
 765:      * @param e MouseEvent that should be handled
 766:      */
 767:     public void mouseReleased(MouseEvent e)
 768:     {
 769:       // Get component over which mouse was released
 770:       Component src = (Component) e.getSource();
 771:       int x = e.getX();
 772:       int y = e.getY();
 773:       Component releasedComponent = SwingUtilities.getDeepestComponentAt(src,
 774:                                                                          x, y);
 775: 
 776:       // if mouse was released inside the bounds of combo box then do nothing,
 777:       // Otherwise if mouse was released inside the list of combo box items
 778:       // then change selection and close popup
 779:       if (! (releasedComponent instanceof JComboBox))
 780:         {
 781:           // List model contains the item over which mouse is released,
 782:           // since it is updated every time the mouse is moved over a different
 783:           // item in the list. Now that the mouse is released we need to
 784:           // update model of the combo box as well.       
 785:           comboBox.setSelectedIndex(list.getSelectedIndex());
 786: 
 787:           if (isAutoScrolling)
 788:             stopAutoScrolling();
 789:           hide();
 790:         }
 791:     }
 792:   }
 793: 
 794:   /**
 795:    * InvocationMouseMotionListener is a mouse listener that listens to mouse
 796:    * dragging events occuring in the combo box.
 797:    */
 798:   protected class InvocationMouseMotionHandler extends MouseMotionAdapter
 799:   {
 800:     /**
 801:      * Creates a new InvocationMouseMotionHandler object.
 802:      */
 803:     protected InvocationMouseMotionHandler()
 804:     {
 805:       // Nothing to do here.
 806:     }
 807: 
 808:     /**
 809:      * This method is responsible for highlighting item in the drop down list
 810:      * over which the mouse is currently being dragged.
 811:      */
 812:     public void mouseDragged(MouseEvent e)
 813:     {
 814:       // convert point of the drag event relative to combo box list component
 815:       // figure out over which list cell the mouse is currently being dragged
 816:       // and highlight the cell. The list model is changed but the change has 
 817:       // no effect on combo box's data model. The list model is changed so 
 818:       // that the appropriate item would be highlighted in the combo box's 
 819:       // list.
 820:       if (BasicComboPopup.this.isVisible())
 821:         {
 822:       int cbHeight = (int) comboBox.getPreferredSize().getHeight();
 823:       int popupHeight = BasicComboPopup.this.getSize().height;
 824: 
 825:       // if mouse is dragged inside the the combo box's items list.
 826:       if (e.getY() > cbHeight && ! (e.getY() - cbHeight >= popupHeight))
 827:         {
 828:           int index = list.locationToIndex(new Point(e.getX(),
 829:                                                      (int) (e.getY()
 830:                                                      - cbHeight)));
 831: 
 832:           int firstVisibleIndex = list.getFirstVisibleIndex();
 833: 
 834:           // list.locationToIndex returns item's index that would
 835:           // be located at the specified point if the first item that
 836:           // is visible is item 0. However in the JComboBox it is not 
 837:           // necessarily the case since list is contained in the 
 838:           // JScrollPane so we need to adjust the index returned. 
 839:           if (firstVisibleIndex != 0)
 840:         // FIXME: adjusted index here is off by one. I am adding one
 841:         // here to compensate for that. This should be
 842:         // index += firstVisibleIndex. Remove +1 once the bug is fixed.
 843:         index += firstVisibleIndex + 1;
 844: 
 845:           list.setSelectedIndex(index);
 846:         }
 847:       else
 848:         {
 849:           // if mouse is being dragged at the bottom of combo box's list 
 850:           // of items or at the very top then scroll the list in the 
 851:           // desired direction.
 852:           boolean movingUP = e.getY() < cbHeight;
 853:           boolean movingDown = e.getY() > cbHeight;
 854: 
 855:           if (movingUP)
 856:             {
 857:           scrollDirection = SCROLL_UP;
 858:           startAutoScrolling(SCROLL_UP);
 859:             }
 860:           else if (movingDown)
 861:             {
 862:           scrollDirection = SCROLL_DOWN;
 863:           startAutoScrolling(SCROLL_DOWN);
 864:             }
 865:         }
 866:         }
 867:     }
 868:   }
 869: 
 870:   /**
 871:    * ItemHandler is an item listener that listens to selection events occuring
 872:    * in the combo box. FIXME: should specify here what it does when item is
 873:    * selected or deselected in the combo box list.
 874:    */
 875:   protected class ItemHandler extends Object implements ItemListener
 876:   {
 877:     /**
 878:      * Creates a new ItemHandler object.
 879:      */
 880:     protected ItemHandler()
 881:     {
 882:       // Nothing to do here.
 883:     }
 884: 
 885:     /**
 886:      * This method responds to the selection events occuring in the combo box.
 887:      *
 888:      * @param e ItemEvent specifying the combo box's selection
 889:      */
 890:     public void itemStateChanged(ItemEvent e)
 891:     {
 892:       // TODO: What should be done here?
 893:     }
 894:   }
 895: 
 896:   /**
 897:    * ListMouseHandler is a listener that listens to mouse events occuring in
 898:    * the combo box's list of items. This class is responsible for hiding
 899:    * popup portion of the combo box if the mouse is released inside the combo
 900:    * box's list.
 901:    */
 902:   protected class ListMouseHandler extends MouseAdapter
 903:   {
 904:     protected ListMouseHandler()
 905:     {
 906:       // Nothing to do here.
 907:     }
 908: 
 909:     public void mousePressed(MouseEvent e)
 910:     {
 911:       // TODO: What should be do here?
 912:     }
 913: 
 914:     public void mouseReleased(MouseEvent anEvent)
 915:     {
 916:       int index = list.locationToIndex(anEvent.getPoint());
 917:       // Check for valid index.
 918:       if (index >= 0)
 919:         comboBox.setSelectedIndex(index);
 920:       hide();
 921:     }
 922:   }
 923: 
 924:   /**
 925:    * ListMouseMotionHandler listens to mouse motion events occuring in the
 926:    * combo box's list. This class is responsible for highlighting items in
 927:    * the list when mouse is moved over them
 928:    */
 929:   protected class ListMouseMotionHandler extends MouseMotionAdapter
 930:   {
 931:     protected ListMouseMotionHandler()
 932:     {
 933:       // Nothing to do here.
 934:     }
 935: 
 936:     public void mouseMoved(MouseEvent anEvent)
 937:     {
 938:       updateListBoxSelectionForEvent(anEvent, false);
 939:     }
 940:   }
 941: 
 942:   /**
 943:    * This class listens to changes occuring in the bound properties of the
 944:    * combo box
 945:    */
 946:   protected class PropertyChangeHandler extends Object
 947:     implements PropertyChangeListener
 948:   {
 949:     protected PropertyChangeHandler()
 950:     {
 951:       // Nothing to do here.
 952:     }
 953: 
 954:     public void propertyChange(PropertyChangeEvent e)
 955:     {
 956:       if (e.getPropertyName().equals("renderer"))
 957:         {
 958:       list.setCellRenderer((ListCellRenderer) e.getNewValue());
 959:       revalidate();
 960:       repaint();
 961:         }
 962:       if (e.getPropertyName().equals("dataModel"))
 963:         {
 964:       list.setModel((ComboBoxModel) e.getNewValue());
 965:       revalidate();
 966:       repaint();
 967:         }
 968:     }
 969:   }
 970: 
 971:   // ------ private helper methods --------------------
 972: 
 973:   /**
 974:    * This method uninstalls listeners installed by the UI
 975:    */
 976:   private void uninstallListeners()
 977:   {
 978:     uninstallListListeners();
 979:     uninstallComboBoxListeners();
 980:     uninstallComboBoxModelListeners(comboBox.getModel());
 981:   }
 982: 
 983:   /**
 984:    * This method uninstalls Listeners registered with combo boxes list of
 985:    * items
 986:    */
 987:   private void uninstallListListeners()
 988:   {
 989:     list.removeMouseListener(listMouseListener);
 990:     listMouseListener = null;
 991: 
 992:     list.removeMouseMotionListener(listMouseMotionListener);
 993:     listMouseMotionListener = null;
 994:   }
 995: 
 996:   /**
 997:    * This method uninstalls listeners listening to combo box  associated with
 998:    * this popup menu
 999:    */
1000:   private void uninstallComboBoxListeners()
1001:   {
1002:     comboBox.removeMouseListener(mouseListener);
1003:     mouseListener = null;
1004: 
1005:     comboBox.removeMouseMotionListener(mouseMotionListener);
1006:     mouseMotionListener = null;
1007: 
1008:     comboBox.removeItemListener(itemListener);
1009:     itemListener = null;
1010: 
1011:     comboBox.removePropertyChangeListener(propertyChangeListener);
1012:     propertyChangeListener = null;
1013:   }
1014: 
1015:   // --------------------------------------------------------------------
1016:   //  The following classes are here only for backwards API compatibility
1017:   //  They aren't used.
1018:   // --------------------------------------------------------------------
1019: 
1020:   /**
1021:    * This class is not used any more.
1022:    */
1023:   public class ListDataHandler extends Object implements ListDataListener
1024:   {
1025:     public ListDataHandler()
1026:     {
1027:       // Nothing to do here.
1028:     }
1029: 
1030:     public void contentsChanged(ListDataEvent e)
1031:     {
1032:       // Nothing to do here.
1033:     }
1034: 
1035:     public void intervalAdded(ListDataEvent e)
1036:     {
1037:       // Nothing to do here.
1038:     }
1039: 
1040:     public void intervalRemoved(ListDataEvent e)
1041:     {
1042:       // Nothing to do here.
1043:     }
1044:   }
1045: 
1046:   /**
1047:    * This class is not used anymore
1048:    */
1049:   protected class ListSelectionHandler extends Object
1050:     implements ListSelectionListener
1051:   {
1052:     protected ListSelectionHandler()
1053:     {
1054:       // Nothing to do here.
1055:     }
1056: 
1057:     public void valueChanged(ListSelectionEvent e)
1058:     {
1059:       // Nothing to do here.
1060:     }
1061:   }
1062: 
1063:   /**
1064:    * This class is not used anymore
1065:    */
1066:   public class InvocationKeyHandler extends KeyAdapter
1067:   {
1068:     public InvocationKeyHandler()
1069:     {
1070:       // Nothing to do here.
1071:     }
1072: 
1073:     public void keyReleased(KeyEvent e)
1074:     {
1075:       // Nothing to do here.
1076:     }
1077:   }
1078: }