OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / plaf / basic / BasicComboPopup.java
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 }