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.Insets;
  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.ComboBoxModel;
  60: import javax.swing.JComboBox;
  61: import javax.swing.JLabel;
  62: import javax.swing.JList;
  63: import javax.swing.JPopupMenu;
  64: import javax.swing.JScrollBar;
  65: import javax.swing.JScrollPane;
  66: import javax.swing.ListCellRenderer;
  67: import javax.swing.ListSelectionModel;
  68: import javax.swing.SwingConstants;
  69: import javax.swing.SwingUtilities;
  70: import javax.swing.Timer;
  71: import javax.swing.event.ListDataEvent;
  72: import javax.swing.event.ListDataListener;
  73: import javax.swing.event.ListSelectionEvent;
  74: import javax.swing.event.ListSelectionListener;
  75: import javax.swing.event.PopupMenuEvent;
  76: import javax.swing.event.PopupMenuListener;
  77: 
  78: /**
  79:  * UI Delegate for ComboPopup
  80:  *
  81:  * @author Olga Rodimina
  82:  */
  83: public class BasicComboPopup extends JPopupMenu implements ComboPopup
  84: {
  85:   /* Timer for autoscrolling */
  86:   protected Timer autoscrollTimer;
  87: 
  88:   /** ComboBox associated with this popup */
  89:   protected JComboBox comboBox;
  90: 
  91:   /** FIXME: Need to document */
  92:   protected boolean hasEntered;
  93: 
  94:   /**
  95:    * Indicates whether the scroll bar located in popup menu with comboBox's
  96:    * list of items is currently autoscrolling. This happens when mouse event
  97:    * originated in the combo box and is dragged outside of its bounds
  98:    */
  99:   protected boolean isAutoScrolling;
 100: 
 101:   /** ItemListener listening to the selection changes in the combo box */
 102:   protected ItemListener itemListener;
 103: 
 104:   /** This listener is not used */
 105:   protected KeyListener keyListener;
 106: 
 107:   /** JList which is used to display item is the combo box */
 108:   protected JList list;
 109: 
 110:   /** This listener is not used */
 111:   protected ListDataListener listDataListener;
 112: 
 113:   /**
 114:    * MouseListener listening to mouse events occuring in the  combo box's
 115:    * list.
 116:    */
 117:   protected MouseListener listMouseListener;
 118: 
 119:   /**
 120:    * MouseMotionListener listening to mouse motion events occuring  in the
 121:    * combo box's list
 122:    */
 123:   protected MouseMotionListener listMouseMotionListener;
 124: 
 125:   /** This listener is not used */
 126:   protected ListSelectionListener listSelectionListener;
 127: 
 128:   /** MouseListener listening to mouse events occuring in the combo box */
 129:   protected MouseListener mouseListener;
 130: 
 131:   /**
 132:    * MouseMotionListener listening to mouse motion events occuring in the
 133:    * combo box
 134:    */
 135:   protected MouseMotionListener mouseMotionListener;
 136: 
 137:   /**
 138:    * PropertyChangeListener listening to changes occuring in the bound
 139:    * properties of the combo box
 140:    */
 141:   protected PropertyChangeListener propertyChangeListener;
 142: 
 143:   /** direction for scrolling down list of combo box's items */
 144:   protected static final int SCROLL_DOWN = 1;
 145: 
 146:   /** direction for scrolling up list of combo box's items */
 147:   protected static final int SCROLL_UP = 0;
 148: 
 149:   /** Indicates auto scrolling direction */
 150:   protected int scrollDirection;
 151: 
 152:   /** JScrollPane that contains list portion of the combo box */
 153:   protected JScrollPane scroller;
 154: 
 155:   /** This field is not used */
 156:   protected boolean valueIsAdjusting;
 157: 
 158:   /**
 159:    * Creates a new BasicComboPopup object.
 160:    *
 161:    * @param comboBox the combo box with which this popup should be associated
 162:    */
 163:   public BasicComboPopup(JComboBox comboBox)
 164:   {
 165:     this.comboBox = comboBox;
 166:     installComboBoxListeners();
 167:     configurePopup();
 168:     setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
 169:   }
 170: 
 171:   /**
 172:    * This method displays drow down list of combo box items on the screen.
 173:    */
 174:   public void show()
 175:   {
 176:     Rectangle cbBounds = comboBox.getBounds();
 177: 
 178:     // popup should have same width as the comboBox and should be hight anough
 179:     // to display number of rows equal to 'maximumRowCount' property
 180:     int popupHeight = getPopupHeightForRowCount(comboBox.getMaximumRowCount());
 181: 
 182:     list.setPreferredSize(new Dimension(cbBounds.width, popupHeight));
 183:     Insets insets1 = getInsets();
 184:     Insets insets2 = scroller.getInsets();
 185:     super.setPopupSize(cbBounds.width, popupHeight + insets1.top 
 186:             + insets1.bottom + insets2.top + insets2.bottom);
 187: 
 188:     // Highlight selected item in the combo box's drop down list
 189:     if (comboBox.getSelectedIndex() != -1)
 190:       list.setSelectedIndex(comboBox.getSelectedIndex());
 191: 
 192:     //scroll scrollbar s.t. selected item is visible
 193:     JScrollBar scrollbar = scroller.getVerticalScrollBar();
 194:     int selectedIndex = comboBox.getSelectedIndex();
 195:     if (selectedIndex > comboBox.getMaximumRowCount())
 196:       scrollbar.setValue(getPopupHeightForRowCount(selectedIndex));
 197: 
 198:     // location specified is relative to comboBox
 199:     super.show(comboBox, 0, cbBounds.height);
 200:   }
 201: 
 202:   /**
 203:    * This method hides drop down list of items
 204:    */
 205:   public void hide()
 206:   {
 207:     super.setVisible(false);
 208:   }
 209: 
 210:   /**
 211:    * Return list cointaining JComboBox's items
 212:    *
 213:    * @return list cointaining JComboBox's items
 214:    */
 215:   public JList getList()
 216:   {
 217:     return list;
 218:   }
 219: 
 220:   /**
 221:    * Returns MouseListener that is listening to mouse events occuring in the
 222:    * combo box.
 223:    *
 224:    * @return MouseListener
 225:    */
 226:   public MouseListener getMouseListener()
 227:   {
 228:     return mouseListener;
 229:   }
 230: 
 231:   /**
 232:    * Returns MouseMotionListener that is listening to mouse  motion events
 233:    * occuring in the combo box.
 234:    *
 235:    * @return MouseMotionListener
 236:    */
 237:   public MouseMotionListener getMouseMotionListener()
 238:   {
 239:     return mouseMotionListener;
 240:   }
 241: 
 242:   /**
 243:    * Returns KeyListener listening to key events occuring in the combo box.
 244:    * This method returns null because KeyHandler is not longer used.
 245:    *
 246:    * @return KeyListener
 247:    */
 248:   public KeyListener getKeyListener()
 249:   {
 250:     return keyListener;
 251:   }
 252: 
 253:   /**
 254:    * This method uninstalls the UI for the  given JComponent.
 255:    */
 256:   public void uninstallingUI()
 257:   {
 258:     uninstallComboBoxModelListeners(comboBox.getModel());
 259: 
 260:     uninstallListeners();
 261:     uninstallKeyboardActions();
 262:   }
 263: 
 264:   /**
 265:    * This method uninstalls listeners that were listening to changes occuring
 266:    * in the comb box's data model
 267:    *
 268:    * @param model data model for the combo box from which to uninstall
 269:    *        listeners
 270:    */
 271:   protected void uninstallComboBoxModelListeners(ComboBoxModel model)
 272:   {
 273:     model.removeListDataListener(listDataListener);
 274:   }
 275: 
 276:   /**
 277:    * This method uninstalls keyboard actions installed by the UI.
 278:    */
 279:   protected void uninstallKeyboardActions()
 280:   {
 281:     // FIXME: Need to implement
 282:   }
 283: 
 284:   /**
 285:    * This method fires PopupMenuEvent indicating that combo box's popup list
 286:    * of items will become visible
 287:    */
 288:   protected void firePopupMenuWillBecomeVisible()
 289:   {
 290:     PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
 291: 
 292:     for (int i = 0; i < ll.length; i++)
 293:       ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox));
 294:   }
 295: 
 296:   /**
 297:    * This method fires PopupMenuEvent indicating that combo box's popup list
 298:    * of items will become invisible.
 299:    */
 300:   protected void firePopupMenuWillBecomeInvisible()
 301:   {
 302:     PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
 303: 
 304:     for (int i = 0; i < ll.length; i++)
 305:       ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox));
 306:   }
 307: 
 308:   /**
 309:    * This method fires PopupMenuEvent indicating that combo box's popup list
 310:    * of items was closed without selection.
 311:    */
 312:   protected void firePopupMenuCanceled()
 313:   {
 314:     PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
 315: 
 316:     for (int i = 0; i < ll.length; i++)
 317:       ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox));
 318:   }
 319: 
 320:   /**
 321:    * Creates MouseListener to listen to mouse events occuring in the combo
 322:    * box. Note that this listener doesn't listen to mouse events occuring in
 323:    * the popup portion of the combo box, it only listens to main combo box
 324:    * part.
 325:    *
 326:    * @return new MouseMotionListener that listens to mouse events occuring in
 327:    *         the combo box
 328:    */
 329:   protected MouseListener createMouseListener()
 330:   {
 331:     return new InvocationMouseHandler();
 332:   }
 333: 
 334:   /**
 335:    * Create Mouse listener that listens to mouse dragging events occuring in
 336:    * the combo box. This listener is responsible for changing the selection
 337:    * in the combo box list to the component over which mouse is being
 338:    * currently dragged
 339:    *
 340:    * @return new MouseMotionListener that listens to mouse dragging events
 341:    *         occuring in the combo box
 342:    */
 343:   protected MouseMotionListener createMouseMotionListener()
 344:   {
 345:     return new InvocationMouseMotionHandler();
 346:   }
 347: 
 348:   /**
 349:    * KeyListener created in this method is not used anymore.
 350:    *
 351:    * @return KeyListener that does nothing
 352:    */
 353:   protected KeyListener createKeyListener()
 354:   {
 355:     return new InvocationKeyHandler();
 356:   }
 357: 
 358:   /**
 359:    * ListSelectionListener created in this method is not used anymore
 360:    *
 361:    * @return ListSelectionListener that does nothing
 362:    */
 363:   protected ListSelectionListener createListSelectionListener()
 364:   {
 365:     return new ListSelectionHandler();
 366:   }
 367: 
 368:   /**
 369:    * Creates ListDataListener. This method returns null, because
 370:    * ListDataHandler class is obsolete and is no longer used.
 371:    *
 372:    * @return null
 373:    */
 374:   protected ListDataListener createListDataListener()
 375:   {
 376:     return null;
 377:   }
 378: 
 379:   /**
 380:    * This method creates ListMouseListener to listen to mouse events occuring
 381:    * in the combo box's item list.
 382:    *
 383:    * @return MouseListener to listen to mouse events occuring in the combo
 384:    *         box's items list.
 385:    */
 386:   protected MouseListener createListMouseListener()
 387:   {
 388:     return new ListMouseHandler();
 389:   }
 390: 
 391:   /**
 392:    * Creates ListMouseMotionlistener to listen to mouse motion events occuring
 393:    * in the combo box's list. This listener is responsible for highlighting
 394:    * items in the list when mouse is moved over them.
 395:    *
 396:    * @return MouseMotionListener that handles mouse motion events occuring in
 397:    *         the list of the combo box.
 398:    */
 399:   protected MouseMotionListener createListMouseMotionListener()
 400:   {
 401:     return new ListMouseMotionHandler();
 402:   }
 403: 
 404:   /**
 405:    * Creates PropertyChangeListener to handle changes in the JComboBox's bound
 406:    * properties.
 407:    *
 408:    * @return PropertyChangeListener to handle changes in the JComboBox's bound
 409:    *         properties.
 410:    */
 411:   protected PropertyChangeListener createPropertyChangeListener()
 412:   {
 413:     return new PropertyChangeHandler();
 414:   }
 415: 
 416:   /**
 417:    * Creates new ItemListener that will listen to ItemEvents occuring in the
 418:    * combo box.
 419:    *
 420:    * @return ItemListener to listen to ItemEvents occuring in the combo box.
 421:    */
 422:   protected ItemListener createItemListener()
 423:   {
 424:     return new ItemHandler();
 425:   }
 426: 
 427:   /**
 428:    * Creates JList that will be used to display items in the combo box.
 429:    *
 430:    * @return JList that will be used to display items in the combo box.
 431:    */
 432:   protected JList createList()
 433:   {
 434:     JList l = new JList(comboBox.getModel());
 435:     l.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
 436:     return l;
 437:   }
 438: 
 439:   /**
 440:    * This method configures the list of comboBox's items by setting  default
 441:    * properties and installing listeners.
 442:    */
 443:   protected void configureList()
 444:   {
 445:     list.setModel(comboBox.getModel());
 446:     list.setVisibleRowCount(comboBox.getMaximumRowCount());
 447:     installListListeners();
 448:   }
 449: 
 450:   /**
 451:    * This method installs list listeners.
 452:    */
 453:   protected void installListListeners()
 454:   {
 455:     // mouse listener listening to mouse events occuring in the 
 456:     // combo box's list of items.
 457:     listMouseListener = createListMouseListener();
 458:     list.addMouseListener(listMouseListener);
 459: 
 460:     // mouse listener listening to mouse motion events occuring in the
 461:     // combo box's list of items
 462:     listMouseMotionListener = createListMouseMotionListener();
 463:     list.addMouseMotionListener(listMouseMotionListener);
 464: 
 465:     listSelectionListener = createListSelectionListener();
 466:     list.addListSelectionListener(listSelectionListener);
 467:   }
 468: 
 469:   /**
 470:    * This method creates scroll pane that will contain the list of comboBox's
 471:    * items inside of it.
 472:    *
 473:    * @return JScrollPane
 474:    */
 475:   protected JScrollPane createScroller()
 476:   {
 477:     return new JScrollPane();
 478:   }
 479: 
 480:   /**
 481:    * This method configures scroll pane to contain list of comboBox's  items
 482:    */
 483:   protected void configureScroller()
 484:   {
 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:     // 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: }