KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicComboPopup


1 /*
2  * @(#)BasicComboPopup.java 1.78 04/03/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.plaf.basic;
9
10 import javax.accessibility.AccessibleContext JavaDoc;
11 import javax.swing.*;
12 import javax.swing.border.Border JavaDoc;
13 import javax.swing.border.LineBorder JavaDoc;
14 import javax.swing.event.*;
15 import java.awt.*;
16 import java.awt.event.*;
17 import java.beans.PropertyChangeListener JavaDoc;
18 import java.beans.PropertyChangeEvent JavaDoc;
19 import java.io.Serializable JavaDoc;
20
21
22 /**
23  * This is a basic implementation of the <code>ComboPopup</code> interface.
24  *
25  * This class represents the ui for the popup portion of the combo box.
26  * <p>
27  * All event handling is handled by listener classes created with the
28  * <code>createxxxListener()</code> methods and internal classes.
29  * You can change the behavior of this class by overriding the
30  * <code>createxxxListener()</code> methods and supplying your own
31  * event listeners or subclassing from the ones supplied in this class.
32  * <p>
33  * <strong>Warning:</strong>
34  * Serialized objects of this class will not be compatible with
35  * future Swing releases. The current serialization support is
36  * appropriate for short term storage or RMI between applications running
37  * the same version of Swing. As of 1.4, support for long term storage
38  * of all JavaBeans<sup><font size="-2">TM</font></sup>
39  * has been added to the <code>java.beans</code> package.
40  * Please see {@link java.beans.XMLEncoder}.
41  *
42  * @version 1.78 03/05/04
43  * @author Tom Santos
44  * @author Mark Davidson
45  */

46 public class BasicComboPopup extends JPopupMenu implements ComboPopup JavaDoc {
47     // An empty ListMode, this is used when the UI changes to allow
48
// the JList to be gc'ed.
49
private static class EmptyListModelClass implements ListModel,
50                                                         Serializable JavaDoc {
51     public int getSize() { return 0; }
52     public Object JavaDoc getElementAt(int index) { return null; }
53     public void addListDataListener(ListDataListener l) {}
54     public void removeListDataListener(ListDataListener l) {}
55     };
56
57     static final ListModel EmptyListModel = new EmptyListModelClass();
58
59     private static Border JavaDoc LIST_BORDER = new LineBorder JavaDoc(Color.BLACK, 1);
60
61     protected JComboBox comboBox;
62     /**
63      * This protected field is implementation specific. Do not access directly
64      * or override. Use the accessor methods instead.
65      *
66      * @see #getList
67      * @see #createList
68      */

69     protected JList list;
70     /**
71      * This protected field is implementation specific. Do not access directly
72      * or override. Use the create method instead
73      *
74      * @see #createScroller
75      */

76     protected JScrollPane scroller;
77
78     /**
79      * As of Java 2 platform v1.4 this previously undocumented field is no
80      * longer used.
81      */

82     protected boolean valueIsAdjusting = false;
83
84     // Listeners that are required by the ComboPopup interface
85

86     /**
87      * Implementation of all the listener classes.
88      */

89     private Handler handler;
90
91     /**
92      * This protected field is implementation specific. Do not access directly
93      * or override. Use the accessor or create methods instead.
94      *
95      * @see #getMouseMotionListener
96      * @see #createMouseMotionListener
97      */

98     protected MouseMotionListener mouseMotionListener;
99     /**
100      * This protected field is implementation specific. Do not access directly
101      * or override. Use the accessor or create methods instead.
102      *
103      * @see #getMouseListener
104      * @see #createMouseListener
105      */

106     protected MouseListener mouseListener;
107
108     /**
109      * This protected field is implementation specific. Do not access directly
110      * or override. Use the accessor or create methods instead.
111      *
112      * @see #getKeyListener
113      * @see #createKeyListener
114      */

115     protected KeyListener keyListener;
116
117     /**
118      * This protected field is implementation specific. Do not access directly
119      * or override. Use the create method instead.
120      *
121      * @see #createListSelectionListener
122      */

123     protected ListSelectionListener listSelectionListener;
124
125     // Listeners that are attached to the list
126
/**
127      * This protected field is implementation specific. Do not access directly
128      * or override. Use the create method instead.
129      *
130      * @see #createListMouseListener
131      */

132     protected MouseListener listMouseListener;
133     /**
134      * This protected field is implementation specific. Do not access directly
135      * or override. Use the create method instead
136      *
137      * @see #createListMouseMotionListener
138      */

139     protected MouseMotionListener listMouseMotionListener;
140
141     // Added to the combo box for bound properties
142
/**
143      * This protected field is implementation specific. Do not access directly
144      * or override. Use the create method instead
145      *
146      * @see #createPropertyChangeListener
147      */

148     protected PropertyChangeListener JavaDoc propertyChangeListener;
149
150     // Added to the combo box model
151
/**
152      * This protected field is implementation specific. Do not access directly
153      * or override. Use the create method instead
154      *
155      * @see #createListDataListener
156      */

157     protected ListDataListener listDataListener;
158
159     /**
160      * This protected field is implementation specific. Do not access directly
161      * or override. Use the create method instead
162      *
163      * @see #createItemListener
164      */

165     protected ItemListener itemListener;
166
167     /**
168      * This protected field is implementation specific. Do not access directly
169      * or override.
170      */

171     protected Timer autoscrollTimer;
172     protected boolean hasEntered = false;
173     protected boolean isAutoScrolling = false;
174     protected int scrollDirection = SCROLL_UP;
175
176     protected static final int SCROLL_UP = 0;
177     protected static final int SCROLL_DOWN = 1;
178
179
180     //========================================
181
// begin ComboPopup method implementations
182
//
183

184     /**
185      * Implementation of ComboPopup.show().
186      */

187     public void show() {
188     setListSelection(comboBox.getSelectedIndex());
189
190     Point location = getPopupLocation();
191         show( comboBox, location.x, location.y );
192     }
193
194
195     /**
196      * Implementation of ComboPopup.hide().
197      */

198     public void hide() {
199         MenuSelectionManager manager = MenuSelectionManager.defaultManager();
200         MenuElement [] selection = manager.getSelectedPath();
201         for ( int i = 0 ; i < selection.length ; i++ ) {
202             if ( selection[i] == this ) {
203                 manager.clearSelectedPath();
204                 break;
205             }
206         }
207         if (selection.length > 0) {
208             comboBox.repaint();
209         }
210     }
211
212     /**
213      * Implementation of ComboPopup.getList().
214      */

215     public JList getList() {
216         return list;
217     }
218
219     /**
220      * Implementation of ComboPopup.getMouseListener().
221      *
222      * @return a <code>MouseListener</code> or null
223      * @see ComboPopup#getMouseListener
224      */

225     public MouseListener getMouseListener() {
226     if (mouseListener == null) {
227         mouseListener = createMouseListener();
228     }
229         return mouseListener;
230     }
231
232     /**
233      * Implementation of ComboPopup.getMouseMotionListener().
234      *
235      * @return a <code>MouseMotionListener</code> or null
236      * @see ComboPopup#getMouseMotionListener
237      */

238     public MouseMotionListener getMouseMotionListener() {
239     if (mouseMotionListener == null) {
240         mouseMotionListener = createMouseMotionListener();
241     }
242         return mouseMotionListener;
243     }
244
245     /**
246      * Implementation of ComboPopup.getKeyListener().
247      *
248      * @return a <code>KeyListener</code> or null
249      * @see ComboPopup#getKeyListener
250      */

251     public KeyListener getKeyListener() {
252     if (keyListener == null) {
253         keyListener = createKeyListener();
254     }
255     return keyListener;
256     }
257
258     /**
259      * Called when the UI is uninstalling. Since this popup isn't in the component
260      * tree, it won't get it's uninstallUI() called. It removes the listeners that
261      * were added in addComboBoxListeners().
262      */

263     public void uninstallingUI() {
264     if (propertyChangeListener != null) {
265         comboBox.removePropertyChangeListener( propertyChangeListener );
266     }
267     if (itemListener != null) {
268         comboBox.removeItemListener( itemListener );
269     }
270     uninstallComboBoxModelListeners(comboBox.getModel());
271         uninstallKeyboardActions();
272     uninstallListListeners();
273     // We do this, otherwise the listener the ui installs on
274
// the model (the combobox model in this case) will keep a
275
// reference to the list, causing the list (and us) to never get gced.
276
list.setModel(EmptyListModel);
277     }
278
279     //
280
// end ComboPopup method implementations
281
//======================================
282

283     /**
284      * Removes the listeners from the combo box model
285      *
286      * @param model The combo box model to install listeners
287      * @see #installComboBoxModelListeners
288      */

289     protected void uninstallComboBoxModelListeners( ComboBoxModel model ) {
290     if (model != null && listDataListener != null) {
291         model.removeListDataListener(listDataListener);
292     }
293     }
294
295     protected void uninstallKeyboardActions() {
296         // XXX - shouldn't call this method
297
// comboBox.unregisterKeyboardAction( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ) );
298
}
299
300
301
302     //===================================================================
303
// begin Initialization routines
304
//
305
public BasicComboPopup( JComboBox combo ) {
306         super();
307         setName("ComboPopup.popup");
308         comboBox = combo;
309
310         setLightWeightPopupEnabled( comboBox.isLightWeightPopupEnabled() );
311
312     // UI construction of the popup.
313
list = createList();
314         list.setName("ComboBox.list");
315     configureList();
316         scroller = createScroller();
317         scroller.setName("ComboBox.scrollPane");
318         configureScroller();
319         configurePopup();
320
321     installComboBoxListeners();
322         installKeyboardActions();
323     }
324
325     // Overriden PopupMenuListener notification methods to inform combo box
326
// PopupMenuListeners.
327

328     protected void firePopupMenuWillBecomeVisible() {
329     super.firePopupMenuWillBecomeVisible();
330     comboBox.firePopupMenuWillBecomeVisible();
331     }
332     
333     protected void firePopupMenuWillBecomeInvisible() {
334     super.firePopupMenuWillBecomeInvisible();
335     comboBox.firePopupMenuWillBecomeInvisible();
336     }
337     
338     protected void firePopupMenuCanceled() {
339     super.firePopupMenuCanceled();
340     comboBox.firePopupMenuCanceled();
341     }
342
343     /**
344      * Creates a listener
345      * that will watch for mouse-press and release events on the combo box.
346      *
347      * <strong>Warning:</strong>
348      * When overriding this method, make sure to maintain the existing
349      * behavior.
350      *
351      * @return a <code>MouseListener</code> which will be added to
352      * the combo box or null
353      */

354     protected MouseListener createMouseListener() {
355         return getHandler();
356     }
357
358     /**
359      * Creates the mouse motion listener which will be added to the combo
360      * box.
361      *
362      * <strong>Warning:</strong>
363      * When overriding this method, make sure to maintain the existing
364      * behavior.
365      *
366      * @return a <code>MouseMotionListener</code> which will be added to
367      * the combo box or null
368      */

369     protected MouseMotionListener createMouseMotionListener() {
370         return getHandler();
371     }
372
373     /**
374      * Creates the key listener that will be added to the combo box. If
375      * this method returns null then it will not be added to the combo box.
376      *
377      * @return a <code>KeyListener</code> or null
378      */

379     protected KeyListener createKeyListener() {
380     return null;
381     }
382
383     /**
384      * Creates a list selection listener that watches for selection changes in
385      * the popup's list. If this method returns null then it will not
386      * be added to the popup list.
387      *
388      * @return an instance of a <code>ListSelectionListener</code> or null
389      */

390     protected ListSelectionListener createListSelectionListener() {
391         return null;
392     }
393
394     /**
395      * Creates a list data listener which will be added to the
396      * <code>ComboBoxModel</code>. If this method returns null then
397      * it will not be added to the combo box model.
398      *
399      * @return an instance of a <code>ListDataListener</code> or null
400      */

401     protected ListDataListener createListDataListener() {
402         return null;
403     }
404
405     /**
406      * Creates a mouse listener that watches for mouse events in
407      * the popup's list. If this method returns null then it will
408      * not be added to the combo box.
409      *
410      * @return an instance of a <code>MouseListener</code> or null
411      */

412     protected MouseListener createListMouseListener() {
413         return getHandler();
414     }
415
416     /**
417      * Creates a mouse motion listener that watches for mouse motion
418      * events in the popup's list. If this method returns null then it will
419      * not be added to the combo box.
420      *
421      * @return an instance of a <code>MouseMotionListener</code> or null
422      */

423     protected MouseMotionListener createListMouseMotionListener() {
424         return getHandler();
425     }
426
427     /**
428      * Creates a <code>PropertyChangeListener</code> which will be added to
429      * the combo box. If this method returns null then it will not
430      * be added to the combo box.
431      *
432      * @return an instance of a <code>PropertyChangeListener</code> or null
433      */

434     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
435         return getHandler();
436     }
437
438     /**
439      * Creates an <code>ItemListener</code> which will be added to the
440      * combo box. If this method returns null then it will not
441      * be added to the combo box.
442      * <p>
443      * Subclasses may override this method to return instances of their own
444      * ItemEvent handlers.
445      *
446      * @return an instance of an <code>ItemListener</code> or null
447      */

448     protected ItemListener createItemListener() {
449         return getHandler();
450     }
451
452     private Handler getHandler() {
453         if (handler == null) {
454             handler = new Handler();
455         }
456         return handler;
457     }
458
459     /**
460      * Creates the JList used in the popup to display
461      * the items in the combo box model. This method is called when the UI class
462      * is created.
463      *
464      * @return a <code>JList</code> used to display the combo box items
465      */

466     protected JList createList() {
467     return new JList( comboBox.getModel() ) {
468             public void processMouseEvent(MouseEvent e) {
469                 if (e.isControlDown()) {
470                     // Fix for 4234053. Filter out the Control Key from the list.
471
// ie., don't allow CTRL key deselection.
472
e = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(),
473                                    e.getModifiers() ^ InputEvent.CTRL_MASK,
474                                    e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger());
475                 }
476                 super.processMouseEvent(e);
477             }
478         };
479     }
480
481     /**
482      * Configures the list which is used to hold the combo box items in the
483      * popup. This method is called when the UI class
484      * is created.
485      *
486      * @see #createList
487      */

488     protected void configureList() {
489         list.setFont( comboBox.getFont() );
490         list.setForeground( comboBox.getForeground() );
491         list.setBackground( comboBox.getBackground() );
492         list.setSelectionForeground( UIManager.getColor( "ComboBox.selectionForeground" ) );
493         list.setSelectionBackground( UIManager.getColor( "ComboBox.selectionBackground" ) );
494         list.setBorder( null );
495         list.setCellRenderer( comboBox.getRenderer() );
496         list.setFocusable( false );
497         list.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
498         setListSelection( comboBox.getSelectedIndex() );
499         installListListeners();
500     }
501
502     /**
503      * Adds the listeners to the list control.
504      */

505     protected void installListListeners() {
506         if ((listMouseListener = createListMouseListener()) != null) {
507         list.addMouseListener( listMouseListener );
508     }
509     if ((listMouseMotionListener = createListMouseMotionListener()) != null) {
510         list.addMouseMotionListener( listMouseMotionListener );
511     }
512     if ((listSelectionListener = createListSelectionListener()) != null) {
513         list.addListSelectionListener( listSelectionListener );
514     }
515     }
516
517     void uninstallListListeners() {
518     if (listMouseListener != null) {
519         list.removeMouseListener(listMouseListener);
520         listMouseListener = null;
521     }
522     if (listMouseMotionListener != null) {
523         list.removeMouseMotionListener(listMouseMotionListener);
524         listMouseMotionListener = null;
525     }
526     if (listSelectionListener != null) {
527         list.removeListSelectionListener(listSelectionListener);
528         listSelectionListener = null;
529     }
530         handler = null;
531     }
532
533     /**
534      * Creates the scroll pane which houses the scrollable list.
535      */

536     protected JScrollPane createScroller() {
537         JScrollPane sp = new JScrollPane( list,
538                 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
539                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
540         sp.setHorizontalScrollBar(null);
541         return sp;
542     }
543
544     /**
545      * Configures the scrollable portion which holds the list within
546      * the combo box popup. This method is called when the UI class
547      * is created.
548      */

549     protected void configureScroller() {
550         scroller.setFocusable( false );
551         scroller.getVerticalScrollBar().setFocusable( false );
552         scroller.setBorder( null );
553     }
554
555     /**
556      * Configures the popup portion of the combo box. This method is called
557      * when the UI class is created.
558      */

559     protected void configurePopup() {
560         setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
561         setBorderPainted( true );
562         setBorder(LIST_BORDER);
563         setOpaque( false );
564         add( scroller );
565         setDoubleBuffered( true );
566         setFocusable( false );
567     }
568
569     /**
570      * This method adds the necessary listeners to the JComboBox.
571      */

572     protected void installComboBoxListeners() {
573         if ((propertyChangeListener = createPropertyChangeListener()) != null) {
574         comboBox.addPropertyChangeListener(propertyChangeListener);
575     }
576     if ((itemListener = createItemListener()) != null) {
577         comboBox.addItemListener(itemListener);
578     }
579     installComboBoxModelListeners(comboBox.getModel());
580     }
581
582     /**
583      * Installs the listeners on the combo box model. Any listeners installed
584      * on the combo box model should be removed in
585      * <code>uninstallComboBoxModelListeners</code>.
586      *
587      * @param model The combo box model to install listeners
588      * @see #uninstallComboBoxModelListeners
589      */

590     protected void installComboBoxModelListeners( ComboBoxModel model ) {
591     if (model != null && (listDataListener = createListDataListener()) != null) {
592         model.addListDataListener(listDataListener);
593     }
594     }
595
596     protected void installKeyboardActions() {
597         
598         /* XXX - shouldn't call this method. take it out for testing.
599         ActionListener action = new ActionListener() {
600             public void actionPerformed(ActionEvent e){
601             }
602         };
603
604         comboBox.registerKeyboardAction( action,
605                                          KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ),
606                                          JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); */

607         
608     }
609
610     //
611
// end Initialization routines
612
//=================================================================
613

614
615     //===================================================================
616
// begin Event Listenters
617
//
618

619     /**
620      * A listener to be registered upon the combo box
621      * (<em>not</em> its popup menu)
622      * to handle mouse events
623      * that affect the state of the popup menu.
624      * The main purpose of this listener is to make the popup menu
625      * appear and disappear.
626      * This listener also helps
627      * with click-and-drag scenarios by setting the selection if the mouse was
628      * released over the list during a drag.
629      *
630      * <p>
631      * <strong>Warning:</strong>
632      * We recommend that you <em>not</em>
633      * create subclasses of this class.
634      * If you absolutely must create a subclass,
635      * be sure to invoke the superclass
636      * version of each method.
637      *
638      * @see BasicComboPopup#createMouseListener
639      */

640     protected class InvocationMouseHandler extends MouseAdapter {
641     /**
642      * Responds to mouse-pressed events on the combo box.
643      *
644      * @param e the mouse-press event to be handled
645      */

646         public void mousePressed( MouseEvent e ) {
647             getHandler().mousePressed(e);
648         }
649
650     /**
651      * Responds to the user terminating
652      * a click or drag that began on the combo box.
653      *
654      * @param e the mouse-release event to be handled
655      */

656         public void mouseReleased( MouseEvent e ) {
657             getHandler().mouseReleased(e);
658         }
659     }
660
661     /**
662      * This listener watches for dragging and updates the current selection in the
663      * list if it is dragging over the list.
664      */

665     protected class InvocationMouseMotionHandler extends MouseMotionAdapter {
666         public void mouseDragged( MouseEvent e ) {
667             getHandler().mouseDragged(e);
668         }
669     }
670
671     /**
672      * As of Java 2 platform v 1.4, this class is now obsolete and is only included for
673      * backwards API compatibility. Do not instantiate or subclass.
674      * <p>
675      * All the functionality of this class has been included in
676      * BasicComboBoxUI ActionMap/InputMap methods.
677      */

678     public class InvocationKeyHandler extends KeyAdapter {
679     public void keyReleased( KeyEvent e ) {}
680     }
681     
682     /**
683      * As of Java 2 platform v 1.4, this class is now obsolete, doesn't do anything, and
684      * is only included for backwards API compatibility. Do not call or
685      * override.
686      */

687     protected class ListSelectionHandler implements ListSelectionListener {
688         public void valueChanged( ListSelectionEvent e ) {}
689     }
690
691     /**
692      * As of 1.4, this class is now obsolete, doesn't do anything, and
693      * is only included for backwards API compatibility. Do not call or
694      * override.
695      * <p>
696      * The functionality has been migrated into <code>ItemHandler</code>.
697      *
698      * @see #createItemListener
699      */

700     public class ListDataHandler implements ListDataListener {
701         public void contentsChanged( ListDataEvent e ) {}
702
703         public void intervalAdded( ListDataEvent e ) {
704         }
705
706         public void intervalRemoved( ListDataEvent e ) {
707         }
708     }
709
710     /**
711      * This listener hides the popup when the mouse is released in the list.
712      */

713     protected class ListMouseHandler extends MouseAdapter {
714         public void mousePressed( MouseEvent e ) {
715         }
716         public void mouseReleased(MouseEvent anEvent) {
717             getHandler().mouseReleased(anEvent);
718         }
719     }
720
721     /**
722      * This listener changes the selected item as you move the mouse over the list.
723      * The selection change is not committed to the model, this is for user feedback only.
724      */

725     protected class ListMouseMotionHandler extends MouseMotionAdapter {
726         public void mouseMoved( MouseEvent anEvent ) {
727             getHandler().mouseMoved(anEvent);
728         }
729     }
730
731     /**
732      * This listener watches for changes to the selection in the
733      * combo box.
734      */

735     protected class ItemHandler implements ItemListener {
736         public void itemStateChanged( ItemEvent e ) {
737             getHandler().itemStateChanged(e);
738     }
739     }
740
741     /**
742      * This listener watches for bound properties that have changed in the
743      * combo box.
744      * <p>
745      * Subclasses which wish to listen to combo box property changes should
746      * call the superclass methods to ensure that the combo popup correctly
747      * handles property changes.
748      *
749      * @see #createPropertyChangeListener
750      */

751     protected class PropertyChangeHandler implements PropertyChangeListener JavaDoc {
752         public void propertyChange( PropertyChangeEvent JavaDoc e ) {
753             getHandler().propertyChange(e);
754         }
755     }
756
757
758     private class AutoScrollActionHandler implements ActionListener {
759         private int direction;
760
761         AutoScrollActionHandler(int direction) {
762             this.direction = direction;
763         }
764
765         public void actionPerformed(ActionEvent e) {
766             if (direction == SCROLL_UP) {
767                 autoScrollUp();
768             }
769             else {
770                 autoScrollDown();
771             }
772         }
773     }
774
775
776     private class Handler implements ItemListener, MouseListener,
777                           MouseMotionListener, PropertyChangeListener JavaDoc,
778                           Serializable JavaDoc {
779         //
780
// MouseListener
781
// NOTE: this is added to both the JList and JComboBox
782
//
783
public void mouseClicked(MouseEvent e) {
784         }
785
786         public void mousePressed(MouseEvent e) {
787             if (e.getSource() == list) {
788                 return;
789             }
790             if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
791                 return;
792
793         if ( comboBox.isEditable() ) {
794                 Component comp = comboBox.getEditor().getEditorComponent();
795                 if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
796             comp.requestFocus();
797                 }
798         }
799         else if (comboBox.isRequestFocusEnabled()) {
800         comboBox.requestFocus();
801         }
802         togglePopup();
803         }
804
805         public void mouseReleased(MouseEvent e) {
806             if (e.getSource() == list) {
807                 // JList mouse listener
808
comboBox.setSelectedIndex( list.getSelectedIndex() );
809                 comboBox.setPopupVisible(false);
810                 // workaround for cancelling an edited item (bug 4530953)
811
if (comboBox.isEditable() && comboBox.getEditor() != null) {
812                     comboBox.configureEditor(comboBox.getEditor(),
813                                              comboBox.getSelectedItem());
814                 }
815                 return;
816             }
817             // JComboBox mouse listener
818
Component source = (Component)e.getSource();
819             Dimension size = source.getSize();
820             Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
821             if ( !bounds.contains( e.getPoint() ) ) {
822                 MouseEvent newEvent = convertMouseEvent( e );
823                 Point location = newEvent.getPoint();
824                 Rectangle r = new Rectangle();
825         list.computeVisibleRect( r );
826         if ( r.contains( location ) ) {
827             comboBox.setSelectedIndex( list.getSelectedIndex() );
828                 }
829         comboBox.setPopupVisible(false);
830             }
831             hasEntered = false;
832             stopAutoScrolling();
833         }
834
835         public void mouseEntered(MouseEvent e) {
836         }
837
838         public void mouseExited(MouseEvent e) {
839         }
840
841         //
842
// MouseMotionListener:
843
// NOTE: this is added to both the List and ComboBox
844
//
845
public void mouseMoved(MouseEvent anEvent) {
846             if (anEvent.getSource() == list) {
847                 Point location = anEvent.getPoint();
848                 Rectangle r = new Rectangle();
849                 list.computeVisibleRect( r );
850                 if ( r.contains( location ) ) {
851                     updateListBoxSelectionForEvent( anEvent, false );
852                 }
853             }
854         }
855
856         public void mouseDragged( MouseEvent e ) {
857             if (e.getSource() == list) {
858                 return;
859             }
860             if ( isVisible() ) {
861                 MouseEvent newEvent = convertMouseEvent( e );
862                 Rectangle r = new Rectangle();
863                 list.computeVisibleRect( r );
864
865                 if ( newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1 ) {
866                     hasEntered = true;
867                     if ( isAutoScrolling ) {
868                         stopAutoScrolling();
869                     }
870                     Point location = newEvent.getPoint();
871                     if ( r.contains( location ) ) {
872                         updateListBoxSelectionForEvent( newEvent, false );
873                     }
874                 }
875                 else {
876                     if ( hasEntered ) {
877                         int directionToScroll = newEvent.getPoint().y < r.y ? SCROLL_UP : SCROLL_DOWN;
878                         if ( isAutoScrolling && scrollDirection != directionToScroll ) {
879                             stopAutoScrolling();
880                             startAutoScrolling( directionToScroll );
881                         }
882                         else if ( !isAutoScrolling ) {
883                             startAutoScrolling( directionToScroll );
884                         }
885                     }
886                     else {
887                         if ( e.getPoint().y < 0 ) {
888                             hasEntered = true;
889                             startAutoScrolling( SCROLL_UP );
890                         }
891                     }
892                 }
893             }
894         }
895
896         //
897
// PropertyChangeListener
898
//
899
public void propertyChange(PropertyChangeEvent JavaDoc e) {
900         JComboBox comboBox = (JComboBox)e.getSource();
901             String JavaDoc propertyName = e.getPropertyName();
902
903             if ( propertyName == "model" ) {
904                 ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
905                 ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
906         uninstallComboBoxModelListeners(oldModel);
907         installComboBoxModelListeners(newModel);
908
909                 list.setModel(newModel);
910
911                 if ( isVisible() ) {
912                     hide();
913                 }
914             }
915             else if ( propertyName == "renderer" ) {
916                 list.setCellRenderer( comboBox.getRenderer() );
917                 if ( isVisible() ) {
918                     hide();
919                 }
920             }
921         else if (propertyName == "componentOrientation") {
922                 // Pass along the new component orientation
923
// to the list and the scroller
924

925                 ComponentOrientation o =(ComponentOrientation)e.getNewValue();
926
927                 JList list = getList();
928                 if (list!=null && list.getComponentOrientation()!=o) {
929                     list.setComponentOrientation(o);
930                 }
931
932                 if (scroller!=null && scroller.getComponentOrientation()!=o) {
933                     scroller.setComponentOrientation(o);
934                 }
935
936                 if (o!=getComponentOrientation()) {
937                     setComponentOrientation(o);
938                 }
939             }
940         else if (propertyName == "lightWeightPopupEnabled") {
941         setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
942         }
943         }
944
945         //
946
// ItemListener
947
//
948
public void itemStateChanged( ItemEvent e ) {
949         if (e.getStateChange() == ItemEvent.SELECTED) {
950         JComboBox comboBox = (JComboBox)e.getSource();
951         setListSelection(comboBox.getSelectedIndex());
952         }
953     }
954     }
955
956     //
957
// end Event Listeners
958
//=================================================================
959

960
961     /**
962      * Overridden to unconditionally return false.
963      */

964     public boolean isFocusTraversable() {
965         return false;
966     }
967
968     //===================================================================
969
// begin Autoscroll methods
970
//
971

972     /**
973      * This protected method is implementation specific and should be private.
974      * do not call or override.
975      */

976     protected void startAutoScrolling( int direction ) {
977         // XXX - should be a private method within InvocationMouseMotionHandler
978
// if possible.
979
if ( isAutoScrolling ) {
980             autoscrollTimer.stop();
981         }
982
983         isAutoScrolling = true;
984
985         if ( direction == SCROLL_UP ) {
986             scrollDirection = SCROLL_UP;
987             Point convertedPoint = SwingUtilities.convertPoint( scroller, new Point( 1, 1 ), list );
988             int top = list.locationToIndex( convertedPoint );
989             list.setSelectedIndex( top );
990
991             autoscrollTimer = new Timer( 100, new AutoScrollActionHandler(
992                                              SCROLL_UP) );
993         }
994         else if ( direction == SCROLL_DOWN ) {
995             scrollDirection = SCROLL_DOWN;
996             Dimension size = scroller.getSize();
997             Point convertedPoint = SwingUtilities.convertPoint( scroller,
998                                                                 new Point( 1, (size.height - 1) - 2 ),
999                                                                 list );
1000            int bottom = list.locationToIndex( convertedPoint );
1001            list.setSelectedIndex( bottom );
1002
1003            autoscrollTimer = new Timer(100, new AutoScrollActionHandler(
1004                                            SCROLL_DOWN));
1005        }
1006        autoscrollTimer.start();
1007    }
1008
1009    /**
1010     * This protected method is implementation specific and should be private.
1011     * do not call or override.
1012     */

1013    protected void stopAutoScrolling() {
1014        isAutoScrolling = false;
1015
1016        if ( autoscrollTimer != null ) {
1017            autoscrollTimer.stop();
1018            autoscrollTimer = null;
1019        }
1020    }
1021
1022    /**
1023     * This protected method is implementation specific and should be private.
1024     * do not call or override.
1025     */

1026    protected void autoScrollUp() {
1027        int index = list.getSelectedIndex();
1028        if ( index > 0 ) {
1029            list.setSelectedIndex( index - 1 );
1030            list.ensureIndexIsVisible( index - 1 );
1031        }
1032    }
1033
1034    /**
1035     * This protected method is implementation specific and should be private.
1036     * do not call or override.
1037     */

1038    protected void autoScrollDown() {
1039        int index = list.getSelectedIndex();
1040        int lastItem = list.getModel().getSize() - 1;
1041        if ( index < lastItem ) {
1042            list.setSelectedIndex( index + 1 );
1043            list.ensureIndexIsVisible( index + 1 );
1044        }
1045    }
1046
1047    //
1048
// end Autoscroll methods
1049
//=================================================================
1050

1051
1052    //===================================================================
1053
// begin Utility methods
1054
//
1055

1056    /**
1057     * Gets the AccessibleContext associated with this BasicComboPopup.
1058     * The AccessibleContext will have its parent set to the ComboBox.
1059     *
1060     * @return an AccessibleContext for the BasicComboPopup
1061     * @since 1.5
1062     */

1063    public AccessibleContext JavaDoc getAccessibleContext() {
1064        AccessibleContext JavaDoc context = super.getAccessibleContext();
1065        context.setAccessibleParent(comboBox);
1066        return context;
1067    }
1068
1069
1070    /**
1071     * This is is a utility method that helps event handlers figure out where to
1072     * send the focus when the popup is brought up. The standard implementation
1073     * delegates the focus to the editor (if the combo box is editable) or to
1074     * the JComboBox if it is not editable.
1075     */

1076    protected void delegateFocus( MouseEvent e ) {
1077        if ( comboBox.isEditable() ) {
1078            Component comp = comboBox.getEditor().getEditorComponent();
1079            if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
1080                comp.requestFocus();
1081            }
1082        }
1083        else if (comboBox.isRequestFocusEnabled()) {
1084            comboBox.requestFocus();
1085        }
1086    }
1087
1088    /**
1089     * Makes the popup visible if it is hidden and makes it hidden if it is
1090     * visible.
1091     */

1092    protected void togglePopup() {
1093        if ( isVisible() ) {
1094            hide();
1095        }
1096        else {
1097            show();
1098        }
1099    }
1100
1101    /**
1102     * Sets the list selection index to the selectedIndex. This
1103     * method is used to synchronize the list selection with the
1104     * combo box selection.
1105     *
1106     * @param selectedIndex the index to set the list
1107     */

1108    private void setListSelection(int selectedIndex) {
1109        if ( selectedIndex == -1 ) {
1110            list.clearSelection();
1111        }
1112        else {
1113            list.setSelectedIndex( selectedIndex );
1114        list.ensureIndexIsVisible( selectedIndex );
1115        }
1116    }
1117
1118    protected MouseEvent convertMouseEvent( MouseEvent e ) {
1119        Point convertedPoint = SwingUtilities.convertPoint( (Component)e.getSource(),
1120                                                            e.getPoint(), list );
1121        MouseEvent newEvent = new MouseEvent( (Component)e.getSource(),
1122                                              e.getID(),
1123                                              e.getWhen(),
1124                                              e.getModifiers(),
1125                                              convertedPoint.x,
1126                                              convertedPoint.y,
1127                                              e.getClickCount(),
1128                                              e.isPopupTrigger() );
1129        return newEvent;
1130    }
1131
1132
1133    /**
1134     * Retrieves the height of the popup based on the current
1135     * ListCellRenderer and the maximum row count.
1136     */

1137    protected int getPopupHeightForRowCount(int maxRowCount) {
1138    // Set the cached value of the minimum row count
1139
int minRowCount = Math.min( maxRowCount, comboBox.getItemCount() );
1140        int height = 0;
1141        ListCellRenderer renderer = list.getCellRenderer();
1142        Object JavaDoc value = null;
1143
1144        for ( int i = 0; i < minRowCount; ++i ) {
1145            value = list.getModel().getElementAt( i );
1146            Component c = renderer.getListCellRendererComponent( list, value, i, false, false );
1147            height += c.getPreferredSize().height;
1148        }
1149
1150        return height == 0 ? 100 : height;
1151    }
1152
1153    /**
1154     * Calculate the placement and size of the popup portion of the combo box based
1155     * on the combo box location and the enclosing screen bounds. If
1156     * no transformations are required, then the returned rectangle will
1157     * have the same values as the parameters.
1158     *
1159     * @param px starting x location
1160     * @param py starting y location
1161     * @param pw starting width
1162     * @param ph starting height
1163     * @return a rectangle which represents the placement and size of the popup
1164     */

1165    protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
1166    Toolkit toolkit = Toolkit.getDefaultToolkit();
1167        Rectangle screenBounds;
1168
1169    // Calculate the desktop dimensions relative to the combo box.
1170
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
1171        Point p = new Point();
1172        SwingUtilities.convertPointFromScreen(p, comboBox);
1173        if (gc != null) {
1174        Insets screenInsets = toolkit.getScreenInsets(gc);
1175            screenBounds = gc.getBounds();
1176        screenBounds.width -= (screenInsets.left + screenInsets.right);
1177        screenBounds.height -= (screenInsets.top + screenInsets.bottom);
1178            screenBounds.x += (p.x + screenInsets.left);
1179            screenBounds.y += (p.y + screenInsets.top);
1180        }
1181        else {
1182            screenBounds = new Rectangle(p, toolkit.getScreenSize());
1183        }
1184
1185        Rectangle rect = new Rectangle(px,py,pw,ph);
1186        if (py+ph > screenBounds.y+screenBounds.height
1187        && ph < screenBounds.height) {
1188        rect.y = -rect.height;
1189    }
1190    return rect;
1191    }
1192
1193    /**
1194     * Calculates the upper left location of the Popup.
1195     */

1196    private Point getPopupLocation() {
1197    Dimension popupSize = comboBox.getSize();
1198    Insets insets = getInsets();
1199
1200    // reduce the width of the scrollpane by the insets so that the popup
1201
// is the same width as the combo box.
1202
popupSize.setSize(popupSize.width - (insets.right + insets.left),
1203              getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
1204    Rectangle popupBounds = computePopupBounds( 0, comboBox.getBounds().height,
1205                                                    popupSize.width, popupSize.height);
1206    Dimension scrollSize = popupBounds.getSize();
1207    Point popupLocation = popupBounds.getLocation();
1208        
1209    scroller.setMaximumSize( scrollSize );
1210    scroller.setPreferredSize( scrollSize );
1211    scroller.setMinimumSize( scrollSize );
1212    
1213    list.revalidate();
1214
1215    return popupLocation;
1216    }
1217
1218    /**
1219     * A utility method used by the event listeners. Given a mouse event, it changes
1220     * the list selection to the list item below the mouse.
1221     */

1222    protected void updateListBoxSelectionForEvent(MouseEvent anEvent,boolean shouldScroll) {
1223    // XXX - only seems to be called from this class. shouldScroll flag is
1224
// never true
1225
Point location = anEvent.getPoint();
1226        if ( list == null )
1227            return;
1228        int index = list.locationToIndex(location);
1229        if ( index == -1 ) {
1230            if ( location.y < 0 )
1231                index = 0;
1232            else
1233                index = comboBox.getModel().getSize() - 1;
1234        }
1235        if ( list.getSelectedIndex() != index ) {
1236            list.setSelectedIndex(index);
1237            if ( shouldScroll )
1238                list.ensureIndexIsVisible(index);
1239        }
1240    }
1241
1242    //
1243
// end Utility methods
1244
//=================================================================
1245
}
1246
1247
1248
Popular Tags