History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicComboPopup.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicComboPopup.java+1295−0
@@ -0,0 +1,1295 @@
1+/*
2+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package javax.swing.plaf.basic;
27+
28+import javax.accessibility.AccessibleContext;
29+import javax.swing.*;
30+import javax.swing.border.Border;
31+import javax.swing.border.LineBorder;
32+import javax.swing.event.*;
33+import java.awt.*;
34+import java.awt.event.*;
35+import java.beans.PropertyChangeListener;
36+import java.beans.PropertyChangeEvent;
37+import java.io.Serializable;
38+
39+
40+/**
41+ * This is a basic implementation of the <code>ComboPopup</code> interface.
42+ *
43+ * This class represents the ui for the popup portion of the combo box.
44+ * <p>
45+ * All event handling is handled by listener classes created with the
46+ * <code>createxxxListener()</code> methods and internal classes.
47+ * You can change the behavior of this class by overriding the
48+ * <code>createxxxListener()</code> methods and supplying your own
49+ * event listeners or subclassing from the ones supplied in this class.
50+ * <p>
51+ * <strong>Warning:</strong>
52+ * Serialized objects of this class will not be compatible with
53+ * future Swing releases. The current serialization support is
54+ * appropriate for short term storage or RMI between applications running
55+ * the same version of Swing. As of 1.4, support for long term storage
56+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
57+ * has been added to the <code>java.beans</code> package.
58+ * Please see {@link java.beans.XMLEncoder}.
59+ *
60+ * @author Tom Santos
61+ * @author Mark Davidson
62+ */
63+public class BasicComboPopup extends JPopupMenu implements ComboPopup {
64+ // An empty ListMode, this is used when the UI changes to allow
65+ // the JList to be gc'ed.
66+ private static class EmptyListModelClass implements ListModel,
67+ Serializable {
68+ public int getSize() { return 0; }
69+ public Object getElementAt(int index) { return null; }
70+ public void addListDataListener(ListDataListener l) {}
71+ public void removeListDataListener(ListDataListener l) {}
72+ };
73+
74+ static final ListModel EmptyListModel = new EmptyListModelClass();
75+
76+ private static Border LIST_BORDER = new LineBorder(Color.BLACK, 1);
77+
78+ protected JComboBox comboBox;
79+ /**
80+ * This protected field is implementation specific. Do not access directly
81+ * or override. Use the accessor methods instead.
82+ *
83+ * @see #getList
84+ * @see #createList
85+ */
86+ protected JList list;
87+ /**
88+ * This protected field is implementation specific. Do not access directly
89+ * or override. Use the create method instead
90+ *
91+ * @see #createScroller
92+ */
93+ protected JScrollPane scroller;
94+
95+ /**
96+ * As of Java 2 platform v1.4 this previously undocumented field is no
97+ * longer used.
98+ */
99+ protected boolean valueIsAdjusting = false;
100+
101+ // Listeners that are required by the ComboPopup interface
102+
103+ /**
104+ * Implementation of all the listener classes.
105+ */
106+ private Handler handler;
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 #getMouseMotionListener
113+ * @see #createMouseMotionListener
114+ */
115+ protected MouseMotionListener mouseMotionListener;
116+ /**
117+ * This protected field is implementation specific. Do not access directly
118+ * or override. Use the accessor or create methods instead.
119+ *
120+ * @see #getMouseListener
121+ * @see #createMouseListener
122+ */
123+ protected MouseListener mouseListener;
124+
125+ /**
126+ * This protected field is implementation specific. Do not access directly
127+ * or override. Use the accessor or create methods instead.
128+ *
129+ * @see #getKeyListener
130+ * @see #createKeyListener
131+ */
132+ protected KeyListener keyListener;
133+
134+ /**
135+ * This protected field is implementation specific. Do not access directly
136+ * or override. Use the create method instead.
137+ *
138+ * @see #createListSelectionListener
139+ */
140+ protected ListSelectionListener listSelectionListener;
141+
142+ // Listeners that are attached to the list
143+ /**
144+ * This protected field is implementation specific. Do not access directly
145+ * or override. Use the create method instead.
146+ *
147+ * @see #createListMouseListener
148+ */
149+ protected MouseListener listMouseListener;
150+ /**
151+ * This protected field is implementation specific. Do not access directly
152+ * or override. Use the create method instead
153+ *
154+ * @see #createListMouseMotionListener
155+ */
156+ protected MouseMotionListener listMouseMotionListener;
157+
158+ // Added to the combo box for bound properties
159+ /**
160+ * This protected field is implementation specific. Do not access directly
161+ * or override. Use the create method instead
162+ *
163+ * @see #createPropertyChangeListener
164+ */
165+ protected PropertyChangeListener propertyChangeListener;
166+
167+ // Added to the combo box model
168+ /**
169+ * This protected field is implementation specific. Do not access directly
170+ * or override. Use the create method instead
171+ *
172+ * @see #createListDataListener
173+ */
174+ protected ListDataListener listDataListener;
175+
176+ /**
177+ * This protected field is implementation specific. Do not access directly
178+ * or override. Use the create method instead
179+ *
180+ * @see #createItemListener
181+ */
182+ protected ItemListener itemListener;
183+
184+ /**
185+ * This protected field is implementation specific. Do not access directly
186+ * or override.
187+ */
188+ protected Timer autoscrollTimer;
189+ protected boolean hasEntered = false;
190+ protected boolean isAutoScrolling = false;
191+ protected int scrollDirection = SCROLL_UP;
192+
193+ protected static final int SCROLL_UP = 0;
194+ protected static final int SCROLL_DOWN = 1;
195+
196+
197+ //========================================
198+ // begin ComboPopup method implementations
199+ //
200+
201+ /**
202+ * Implementation of ComboPopup.show().
203+ */
204+ public void show() {
205+ comboBox.firePopupMenuWillBecomeVisible();
206+ setListSelection(comboBox.getSelectedIndex());
207+ Point location = getPopupLocation();
208+ show( comboBox, location.x, location.y );
209+ }
210+
211+
212+ /**
213+ * Implementation of ComboPopup.hide().
214+ */
215+ public void hide() {
216+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
217+ MenuElement [] selection = manager.getSelectedPath();
218+ for ( int i = 0 ; i < selection.length ; i++ ) {
219+ if ( selection[i] == this ) {
220+ manager.clearSelectedPath();
221+ break;
222+ }
223+ }
224+ if (selection.length > 0) {
225+ comboBox.repaint();
226+ }
227+ }
228+
229+ /**
230+ * Implementation of ComboPopup.getList().
231+ */
232+ public JList getList() {
233+ return list;
234+ }
235+
236+ /**
237+ * Implementation of ComboPopup.getMouseListener().
238+ *
239+ * @return a <code>MouseListener</code> or null
240+ * @see ComboPopup#getMouseListener
241+ */
242+ public MouseListener getMouseListener() {
243+ if (mouseListener == null) {
244+ mouseListener = createMouseListener();
245+ }
246+ return mouseListener;
247+ }
248+
249+ /**
250+ * Implementation of ComboPopup.getMouseMotionListener().
251+ *
252+ * @return a <code>MouseMotionListener</code> or null
253+ * @see ComboPopup#getMouseMotionListener
254+ */
255+ public MouseMotionListener getMouseMotionListener() {
256+ if (mouseMotionListener == null) {
257+ mouseMotionListener = createMouseMotionListener();
258+ }
259+ return mouseMotionListener;
260+ }
261+
262+ /**
263+ * Implementation of ComboPopup.getKeyListener().
264+ *
265+ * @return a <code>KeyListener</code> or null
266+ * @see ComboPopup#getKeyListener
267+ */
268+ public KeyListener getKeyListener() {
269+ if (keyListener == null) {
270+ keyListener = createKeyListener();
271+ }
272+ return keyListener;
273+ }
274+
275+ /**
276+ * Called when the UI is uninstalling. Since this popup isn't in the component
277+ * tree, it won't get it's uninstallUI() called. It removes the listeners that
278+ * were added in addComboBoxListeners().
279+ */
280+ public void uninstallingUI() {
281+ if (propertyChangeListener != null) {
282+ comboBox.removePropertyChangeListener( propertyChangeListener );
283+ }
284+ if (itemListener != null) {
285+ comboBox.removeItemListener( itemListener );
286+ }
287+ uninstallComboBoxModelListeners(comboBox.getModel());
288+ uninstallKeyboardActions();
289+ uninstallListListeners();
290+ // We do this, otherwise the listener the ui installs on
291+ // the model (the combobox model in this case) will keep a
292+ // reference to the list, causing the list (and us) to never get gced.
293+ list.setModel(EmptyListModel);
294+ }
295+
296+ //
297+ // end ComboPopup method implementations
298+ //======================================
299+
300+ /**
301+ * Removes the listeners from the combo box model
302+ *
303+ * @param model The combo box model to install listeners
304+ * @see #installComboBoxModelListeners
305+ */
306+ protected void uninstallComboBoxModelListeners( ComboBoxModel model ) {
307+ if (model != null && listDataListener != null) {
308+ model.removeListDataListener(listDataListener);
309+ }
310+ }
311+
312+ protected void uninstallKeyboardActions() {
313+ // XXX - shouldn't call this method
314+// comboBox.unregisterKeyboardAction( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ) );
315+ }
316+
317+
318+
319+ //===================================================================
320+ // begin Initialization routines
321+ //
322+ public BasicComboPopup( JComboBox combo ) {
323+ super();
324+ setName("ComboPopup.popup");
325+ comboBox = combo;
326+
327+ setLightWeightPopupEnabled( comboBox.isLightWeightPopupEnabled() );
328+
329+ // UI construction of the popup.
330+ list = createList();
331+ list.setName("ComboBox.list");
332+ configureList();
333+ scroller = createScroller();
334+ scroller.setName("ComboBox.scrollPane");
335+ configureScroller();
336+ configurePopup();
337+
338+ installComboBoxListeners();
339+ installKeyboardActions();
340+ }
341+
342+ // Overriden PopupMenuListener notification methods to inform combo box
343+ // PopupMenuListeners.
344+
345+ protected void firePopupMenuWillBecomeVisible() {
346+ super.firePopupMenuWillBecomeVisible();
347+ // comboBox.firePopupMenuWillBecomeVisible() is called from BasicComboPopup.show() method
348+ // to let the user change the popup menu from the PopupMenuListener.popupMenuWillBecomeVisible()
349+ }
350+
351+ protected void firePopupMenuWillBecomeInvisible() {
352+ super.firePopupMenuWillBecomeInvisible();
353+ comboBox.firePopupMenuWillBecomeInvisible();
354+ }
355+
356+ protected void firePopupMenuCanceled() {
357+ super.firePopupMenuCanceled();
358+ comboBox.firePopupMenuCanceled();
359+ }
360+
361+ /**
362+ * Creates a listener
363+ * that will watch for mouse-press and release events on the combo box.
364+ *
365+ * <strong>Warning:</strong>
366+ * When overriding this method, make sure to maintain the existing
367+ * behavior.
368+ *
369+ * @return a <code>MouseListener</code> which will be added to
370+ * the combo box or null
371+ */
372+ protected MouseListener createMouseListener() {
373+ return getHandler();
374+ }
375+
376+ /**
377+ * Creates the mouse motion listener which will be added to the combo
378+ * box.
379+ *
380+ * <strong>Warning:</strong>
381+ * When overriding this method, make sure to maintain the existing
382+ * behavior.
383+ *
384+ * @return a <code>MouseMotionListener</code> which will be added to
385+ * the combo box or null
386+ */
387+ protected MouseMotionListener createMouseMotionListener() {
388+ return getHandler();
389+ }
390+
391+ /**
392+ * Creates the key listener that will be added to the combo box. If
393+ * this method returns null then it will not be added to the combo box.
394+ *
395+ * @return a <code>KeyListener</code> or null
396+ */
397+ protected KeyListener createKeyListener() {
398+ return null;
399+ }
400+
401+ /**
402+ * Creates a list selection listener that watches for selection changes in
403+ * the popup's list. If this method returns null then it will not
404+ * be added to the popup list.
405+ *
406+ * @return an instance of a <code>ListSelectionListener</code> or null
407+ */
408+ protected ListSelectionListener createListSelectionListener() {
409+ return null;
410+ }
411+
412+ /**
413+ * Creates a list data listener which will be added to the
414+ * <code>ComboBoxModel</code>. If this method returns null then
415+ * it will not be added to the combo box model.
416+ *
417+ * @return an instance of a <code>ListDataListener</code> or null
418+ */
419+ protected ListDataListener createListDataListener() {
420+ return null;
421+ }
422+
423+ /**
424+ * Creates a mouse listener that watches for mouse events in
425+ * the popup's list. If this method returns null then it will
426+ * not be added to the combo box.
427+ *
428+ * @return an instance of a <code>MouseListener</code> or null
429+ */
430+ protected MouseListener createListMouseListener() {
431+ return getHandler();
432+ }
433+
434+ /**
435+ * Creates a mouse motion listener that watches for mouse motion
436+ * events in the popup's list. If this method returns null then it will
437+ * not be added to the combo box.
438+ *
439+ * @return an instance of a <code>MouseMotionListener</code> or null
440+ */
441+ protected MouseMotionListener createListMouseMotionListener() {
442+ return getHandler();
443+ }
444+
445+ /**
446+ * Creates a <code>PropertyChangeListener</code> which will be added to
447+ * the combo box. If this method returns null then it will not
448+ * be added to the combo box.
449+ *
450+ * @return an instance of a <code>PropertyChangeListener</code> or null
451+ */
452+ protected PropertyChangeListener createPropertyChangeListener() {
453+ return getHandler();
454+ }
455+
456+ /**
457+ * Creates an <code>ItemListener</code> which will be added to the
458+ * combo box. If this method returns null then it will not
459+ * be added to the combo box.
460+ * <p>
461+ * Subclasses may override this method to return instances of their own
462+ * ItemEvent handlers.
463+ *
464+ * @return an instance of an <code>ItemListener</code> or null
465+ */
466+ protected ItemListener createItemListener() {
467+ return getHandler();
468+ }
469+
470+ private Handler getHandler() {
471+ if (handler == null) {
472+ handler = new Handler();
473+ }
474+ return handler;
475+ }
476+
477+ /**
478+ * Creates the JList used in the popup to display
479+ * the items in the combo box model. This method is called when the UI class
480+ * is created.
481+ *
482+ * @return a <code>JList</code> used to display the combo box items
483+ */
484+ protected JList createList() {
485+ return new JList( comboBox.getModel() ) {
486+ public void processMouseEvent(MouseEvent e) {
487+ if (BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
488+ // Fix for 4234053. Filter out the Control Key from the list.
489+ // ie., don't allow CTRL key deselection.
490+ Toolkit toolkit = Toolkit.getDefaultToolkit();
491+ e = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(),
492+ e.getModifiers() ^ toolkit.getMenuShortcutKeyMask(),
493+ e.getX(), e.getY(),
494+ e.getXOnScreen(), e.getYOnScreen(),
495+ e.getClickCount(),
496+ e.isPopupTrigger(),
497+ MouseEvent.NOBUTTON);
498+ }
499+ super.processMouseEvent(e);
500+ }
501+ };
502+ }
503+
504+ /**
505+ * Configures the list which is used to hold the combo box items in the
506+ * popup. This method is called when the UI class
507+ * is created.
508+ *
509+ * @see #createList
510+ */
511+ protected void configureList() {
512+ list.setFont( comboBox.getFont() );
513+ list.setForeground( comboBox.getForeground() );
514+ list.setBackground( comboBox.getBackground() );
515+ list.setSelectionForeground( UIManager.getColor( "ComboBox.selectionForeground" ) );
516+ list.setSelectionBackground( UIManager.getColor( "ComboBox.selectionBackground" ) );
517+ list.setBorder( null );
518+ list.setCellRenderer( comboBox.getRenderer() );
519+ list.setFocusable( false );
520+ list.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
521+ setListSelection( comboBox.getSelectedIndex() );
522+ installListListeners();
523+ }
524+
525+ /**
526+ * Adds the listeners to the list control.
527+ */
528+ protected void installListListeners() {
529+ if ((listMouseListener = createListMouseListener()) != null) {
530+ list.addMouseListener( listMouseListener );
531+ }
532+ if ((listMouseMotionListener = createListMouseMotionListener()) != null) {
533+ list.addMouseMotionListener( listMouseMotionListener );
534+ }
535+ if ((listSelectionListener = createListSelectionListener()) != null) {
536+ list.addListSelectionListener( listSelectionListener );
537+ }
538+ }
539+
540+ void uninstallListListeners() {
541+ if (listMouseListener != null) {
542+ list.removeMouseListener(listMouseListener);
543+ listMouseListener = null;
544+ }
545+ if (listMouseMotionListener != null) {
546+ list.removeMouseMotionListener(listMouseMotionListener);
547+ listMouseMotionListener = null;
548+ }
549+ if (listSelectionListener != null) {
550+ list.removeListSelectionListener(listSelectionListener);
551+ listSelectionListener = null;
552+ }
553+ handler = null;
554+ }
555+
556+ /**
557+ * Creates the scroll pane which houses the scrollable list.
558+ */
559+ protected JScrollPane createScroller() {
560+ JScrollPane sp = new JScrollPane( list,
561+ ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
562+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
563+ sp.setHorizontalScrollBar(null);
564+ return sp;
565+ }
566+
567+ /**
568+ * Configures the scrollable portion which holds the list within
569+ * the combo box popup. This method is called when the UI class
570+ * is created.
571+ */
572+ protected void configureScroller() {
573+ scroller.setFocusable( false );
574+ scroller.getVerticalScrollBar().setFocusable( false );
575+ scroller.setBorder( null );
576+ }
577+
578+ /**
579+ * Configures the popup portion of the combo box. This method is called
580+ * when the UI class is created.
581+ */
582+ protected void configurePopup() {
583+ setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
584+ setBorderPainted( true );
585+ setBorder(LIST_BORDER);
586+ setOpaque( false );
587+ add( scroller );
588+ setDoubleBuffered( true );
589+ setFocusable( false );
590+ }
591+
592+ /**
593+ * This method adds the necessary listeners to the JComboBox.
594+ */
595+ protected void installComboBoxListeners() {
596+ if ((propertyChangeListener = createPropertyChangeListener()) != null) {
597+ comboBox.addPropertyChangeListener(propertyChangeListener);
598+ }
599+ if ((itemListener = createItemListener()) != null) {
600+ comboBox.addItemListener(itemListener);
601+ }
602+ installComboBoxModelListeners(comboBox.getModel());
603+ }
604+
605+ /**
606+ * Installs the listeners on the combo box model. Any listeners installed
607+ * on the combo box model should be removed in
608+ * <code>uninstallComboBoxModelListeners</code>.
609+ *
610+ * @param model The combo box model to install listeners
611+ * @see #uninstallComboBoxModelListeners
612+ */
613+ protected void installComboBoxModelListeners( ComboBoxModel model ) {
614+ if (model != null && (listDataListener = createListDataListener()) != null) {
615+ model.addListDataListener(listDataListener);
616+ }
617+ }
618+
619+ protected void installKeyboardActions() {
620+
621+ /* XXX - shouldn't call this method. take it out for testing.
622+ ActionListener action = new ActionListener() {
623+ public void actionPerformed(ActionEvent e){
624+ }
625+ };
626+
627+ comboBox.registerKeyboardAction( action,
628+ KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ),
629+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); */
630+
631+ }
632+
633+ //
634+ // end Initialization routines
635+ //=================================================================
636+
637+
638+ //===================================================================
639+ // begin Event Listenters
640+ //
641+
642+ /**
643+ * A listener to be registered upon the combo box
644+ * (<em>not</em> its popup menu)
645+ * to handle mouse events
646+ * that affect the state of the popup menu.
647+ * The main purpose of this listener is to make the popup menu
648+ * appear and disappear.
649+ * This listener also helps
650+ * with click-and-drag scenarios by setting the selection if the mouse was
651+ * released over the list during a drag.
652+ *
653+ * <p>
654+ * <strong>Warning:</strong>
655+ * We recommend that you <em>not</em>
656+ * create subclasses of this class.
657+ * If you absolutely must create a subclass,
658+ * be sure to invoke the superclass
659+ * version of each method.
660+ *
661+ * @see BasicComboPopup#createMouseListener
662+ */
663+ protected class InvocationMouseHandler extends MouseAdapter {
664+ /**
665+ * Responds to mouse-pressed events on the combo box.
666+ *
667+ * @param e the mouse-press event to be handled
668+ */
669+ public void mousePressed( MouseEvent e ) {
670+ getHandler().mousePressed(e);
671+ }
672+
673+ /**
674+ * Responds to the user terminating
675+ * a click or drag that began on the combo box.
676+ *
677+ * @param e the mouse-release event to be handled
678+ */
679+ public void mouseReleased( MouseEvent e ) {
680+ getHandler().mouseReleased(e);
681+ }
682+ }
683+
684+ /**
685+ * This listener watches for dragging and updates the current selection in the
686+ * list if it is dragging over the list.
687+ */
688+ protected class InvocationMouseMotionHandler extends MouseMotionAdapter {
689+ public void mouseDragged( MouseEvent e ) {
690+ getHandler().mouseDragged(e);
691+ }
692+ }
693+
694+ /**
695+ * As of Java 2 platform v 1.4, this class is now obsolete and is only included for
696+ * backwards API compatibility. Do not instantiate or subclass.
697+ * <p>
698+ * All the functionality of this class has been included in
699+ * BasicComboBoxUI ActionMap/InputMap methods.
700+ */
701+ public class InvocationKeyHandler extends KeyAdapter {
702+ public void keyReleased( KeyEvent e ) {}
703+ }
704+
705+ /**
706+ * As of Java 2 platform v 1.4, this class is now obsolete, doesn't do anything, and
707+ * is only included for backwards API compatibility. Do not call or
708+ * override.
709+ */
710+ protected class ListSelectionHandler implements ListSelectionListener {
711+ public void valueChanged( ListSelectionEvent e ) {}
712+ }
713+
714+ /**
715+ * As of 1.4, this class is now obsolete, doesn't do anything, and
716+ * is only included for backwards API compatibility. Do not call or
717+ * override.
718+ * <p>
719+ * The functionality has been migrated into <code>ItemHandler</code>.
720+ *
721+ * @see #createItemListener
722+ */
723+ public class ListDataHandler implements ListDataListener {
724+ public void contentsChanged( ListDataEvent e ) {}
725+
726+ public void intervalAdded( ListDataEvent e ) {
727+ }
728+
729+ public void intervalRemoved( ListDataEvent e ) {
730+ }
731+ }
732+
733+ /**
734+ * This listener hides the popup when the mouse is released in the list.
735+ */
736+ protected class ListMouseHandler extends MouseAdapter {
737+ public void mousePressed( MouseEvent e ) {
738+ }
739+ public void mouseReleased(MouseEvent anEvent) {
740+ getHandler().mouseReleased(anEvent);
741+ }
742+ }
743+
744+ /**
745+ * This listener changes the selected item as you move the mouse over the list.
746+ * The selection change is not committed to the model, this is for user feedback only.
747+ */
748+ protected class ListMouseMotionHandler extends MouseMotionAdapter {
749+ public void mouseMoved( MouseEvent anEvent ) {
750+ getHandler().mouseMoved(anEvent);
751+ }
752+ }
753+
754+ /**
755+ * This listener watches for changes to the selection in the
756+ * combo box.
757+ */
758+ protected class ItemHandler implements ItemListener {
759+ public void itemStateChanged( ItemEvent e ) {
760+ getHandler().itemStateChanged(e);
761+ }
762+ }
763+
764+ /**
765+ * This listener watches for bound properties that have changed in the
766+ * combo box.
767+ * <p>
768+ * Subclasses which wish to listen to combo box property changes should
769+ * call the superclass methods to ensure that the combo popup correctly
770+ * handles property changes.
771+ *
772+ * @see #createPropertyChangeListener
773+ */
774+ protected class PropertyChangeHandler implements PropertyChangeListener {
775+ public void propertyChange( PropertyChangeEvent e ) {
776+ getHandler().propertyChange(e);
777+ }
778+ }
779+
780+
781+ private class AutoScrollActionHandler implements ActionListener {
782+ private int direction;
783+
784+ AutoScrollActionHandler(int direction) {
785+ this.direction = direction;
786+ }
787+
788+ public void actionPerformed(ActionEvent e) {
789+ if (direction == SCROLL_UP) {
790+ autoScrollUp();
791+ }
792+ else {
793+ autoScrollDown();
794+ }
795+ }
796+ }
797+
798+
799+ private class Handler implements ItemListener, MouseListener,
800+ MouseMotionListener, PropertyChangeListener,
801+ Serializable {
802+ //
803+ // MouseListener
804+ // NOTE: this is added to both the JList and JComboBox
805+ //
806+ public void mouseClicked(MouseEvent e) {
807+ }
808+
809+ public void mousePressed(MouseEvent e) {
810+ if (e.getSource() == list) {
811+ return;
812+ }
813+ if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
814+ return;
815+
816+ if ( comboBox.isEditable() ) {
817+ Component comp = comboBox.getEditor().getEditorComponent();
818+ if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
819+ comp.requestFocus();
820+ }
821+ }
822+ else if (comboBox.isRequestFocusEnabled()) {
823+ comboBox.requestFocus();
824+ }
825+ togglePopup();
826+ }
827+
828+ public void mouseReleased(MouseEvent e) {
829+ if (e.getSource() == list) {
830+ if (list.getModel().getSize() > 0) {
831+ // JList mouse listener
832+ if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
833+ comboBox.getEditor().setItem(list.getSelectedValue());
834+ }
835+ comboBox.setSelectedIndex(list.getSelectedIndex());
836+ }
837+ comboBox.setPopupVisible(false);
838+ // workaround for cancelling an edited item (bug 4530953)
839+ if (comboBox.isEditable() && comboBox.getEditor() != null) {
840+ comboBox.configureEditor(comboBox.getEditor(),
841+ comboBox.getSelectedItem());
842+ }
843+ return;
844+ }
845+ // JComboBox mouse listener
846+ Component source = (Component)e.getSource();
847+ Dimension size = source.getSize();
848+ Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
849+ if ( !bounds.contains( e.getPoint() ) ) {
850+ MouseEvent newEvent = convertMouseEvent( e );
851+ Point location = newEvent.getPoint();
852+ Rectangle r = new Rectangle();
853+ list.computeVisibleRect( r );
854+ if ( r.contains( location ) ) {
855+ if (comboBox.getSelectedIndex() == list.getSelectedIndex()) {
856+ comboBox.getEditor().setItem(list.getSelectedValue());
857+ }
858+ comboBox.setSelectedIndex(list.getSelectedIndex());
859+ }
860+ comboBox.setPopupVisible(false);
861+ }
862+ hasEntered = false;
863+ stopAutoScrolling();
864+ }
865+
866+ public void mouseEntered(MouseEvent e) {
867+ }
868+
869+ public void mouseExited(MouseEvent e) {
870+ }
871+
872+ //
873+ // MouseMotionListener:
874+ // NOTE: this is added to both the List and ComboBox
875+ //
876+ public void mouseMoved(MouseEvent anEvent) {
877+ if (anEvent.getSource() == list) {
878+ Point location = anEvent.getPoint();
879+ Rectangle r = new Rectangle();
880+ list.computeVisibleRect( r );
881+ if ( r.contains( location ) ) {
882+ updateListBoxSelectionForEvent( anEvent, false );
883+ }
884+ }
885+ }
886+
887+ public void mouseDragged( MouseEvent e ) {
888+ if (e.getSource() == list) {
889+ return;
890+ }
891+ if ( isVisible() ) {
892+ MouseEvent newEvent = convertMouseEvent( e );
893+ Rectangle r = new Rectangle();
894+ list.computeVisibleRect( r );
895+
896+ if ( newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1 ) {
897+ hasEntered = true;
898+ if ( isAutoScrolling ) {
899+ stopAutoScrolling();
900+ }
901+ Point location = newEvent.getPoint();
902+ if ( r.contains( location ) ) {
903+ updateListBoxSelectionForEvent( newEvent, false );
904+ }
905+ }
906+ else {
907+ if ( hasEntered ) {
908+ int directionToScroll = newEvent.getPoint().y < r.y ? SCROLL_UP : SCROLL_DOWN;
909+ if ( isAutoScrolling && scrollDirection != directionToScroll ) {
910+ stopAutoScrolling();
911+ startAutoScrolling( directionToScroll );
912+ }
913+ else if ( !isAutoScrolling ) {
914+ startAutoScrolling( directionToScroll );
915+ }
916+ }
917+ else {
918+ if ( e.getPoint().y < 0 ) {
919+ hasEntered = true;
920+ startAutoScrolling( SCROLL_UP );
921+ }
922+ }
923+ }
924+ }
925+ }
926+
927+ //
928+ // PropertyChangeListener
929+ //
930+ public void propertyChange(PropertyChangeEvent e) {
931+ JComboBox comboBox = (JComboBox)e.getSource();
932+ String propertyName = e.getPropertyName();
933+
934+ if ( propertyName == "model" ) {
935+ ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
936+ ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
937+ uninstallComboBoxModelListeners(oldModel);
938+ installComboBoxModelListeners(newModel);
939+
940+ list.setModel(newModel);
941+
942+ if ( isVisible() ) {
943+ hide();
944+ }
945+ }
946+ else if ( propertyName == "renderer" ) {
947+ list.setCellRenderer( comboBox.getRenderer() );
948+ if ( isVisible() ) {
949+ hide();
950+ }
951+ }
952+ else if (propertyName == "componentOrientation") {
953+ // Pass along the new component orientation
954+ // to the list and the scroller
955+
956+ ComponentOrientation o =(ComponentOrientation)e.getNewValue();
957+
958+ JList list = getList();
959+ if (list!=null && list.getComponentOrientation()!=o) {
960+ list.setComponentOrientation(o);
961+ }
962+
963+ if (scroller!=null && scroller.getComponentOrientation()!=o) {
964+ scroller.setComponentOrientation(o);
965+ }
966+
967+ if (o!=getComponentOrientation()) {
968+ setComponentOrientation(o);
969+ }
970+ }
971+ else if (propertyName == "lightWeightPopupEnabled") {
972+ setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
973+ }
974+ }
975+
976+ //
977+ // ItemListener
978+ //
979+ public void itemStateChanged( ItemEvent e ) {
980+ if (e.getStateChange() == ItemEvent.SELECTED) {
981+ JComboBox comboBox = (JComboBox)e.getSource();
982+ setListSelection(comboBox.getSelectedIndex());
983+ }
984+ }
985+ }
986+
987+ //
988+ // end Event Listeners
989+ //=================================================================
990+
991+
992+ /**
993+ * Overridden to unconditionally return false.
994+ */
995+ public boolean isFocusTraversable() {
996+ return false;
997+ }
998+
999+ //===================================================================
1000+ // begin Autoscroll methods
1001+ //
1002+
1003+ /**
1004+ * This protected method is implementation specific and should be private.
1005+ * do not call or override.
1006+ */
1007+ protected void startAutoScrolling( int direction ) {
1008+ // XXX - should be a private method within InvocationMouseMotionHandler
1009+ // if possible.
1010+ if ( isAutoScrolling ) {
1011+ autoscrollTimer.stop();
1012+ }
1013+
1014+ isAutoScrolling = true;
1015+
1016+ if ( direction == SCROLL_UP ) {
1017+ scrollDirection = SCROLL_UP;
1018+ Point convertedPoint = SwingUtilities.convertPoint( scroller, new Point( 1, 1 ), list );
1019+ int top = list.locationToIndex( convertedPoint );
1020+ list.setSelectedIndex( top );
1021+
1022+ autoscrollTimer = new Timer( 100, new AutoScrollActionHandler(
1023+ SCROLL_UP) );
1024+ }
1025+ else if ( direction == SCROLL_DOWN ) {
1026+ scrollDirection = SCROLL_DOWN;
1027+ Dimension size = scroller.getSize();
1028+ Point convertedPoint = SwingUtilities.convertPoint( scroller,
1029+ new Point( 1, (size.height - 1) - 2 ),
1030+ list );
1031+ int bottom = list.locationToIndex( convertedPoint );
1032+ list.setSelectedIndex( bottom );
1033+
1034+ autoscrollTimer = new Timer(100, new AutoScrollActionHandler(
1035+ SCROLL_DOWN));
1036+ }
1037+ autoscrollTimer.start();
1038+ }
1039+
1040+ /**
1041+ * This protected method is implementation specific and should be private.
1042+ * do not call or override.
1043+ */
1044+ protected void stopAutoScrolling() {
1045+ isAutoScrolling = false;
1046+
1047+ if ( autoscrollTimer != null ) {
1048+ autoscrollTimer.stop();
1049+ autoscrollTimer = null;
1050+ }
1051+ }
1052+
1053+ /**
1054+ * This protected method is implementation specific and should be private.
1055+ * do not call or override.
1056+ */
1057+ protected void autoScrollUp() {
1058+ int index = list.getSelectedIndex();
1059+ if ( index > 0 ) {
1060+ list.setSelectedIndex( index - 1 );
1061+ list.ensureIndexIsVisible( index - 1 );
1062+ }
1063+ }
1064+
1065+ /**
1066+ * This protected method is implementation specific and should be private.
1067+ * do not call or override.
1068+ */
1069+ protected void autoScrollDown() {
1070+ int index = list.getSelectedIndex();
1071+ int lastItem = list.getModel().getSize() - 1;
1072+ if ( index < lastItem ) {
1073+ list.setSelectedIndex( index + 1 );
1074+ list.ensureIndexIsVisible( index + 1 );
1075+ }
1076+ }
1077+
1078+ //
1079+ // end Autoscroll methods
1080+ //=================================================================
1081+
1082+
1083+ //===================================================================
1084+ // begin Utility methods
1085+ //
1086+
1087+ /**
1088+ * Gets the AccessibleContext associated with this BasicComboPopup.
1089+ * The AccessibleContext will have its parent set to the ComboBox.
1090+ *
1091+ * @return an AccessibleContext for the BasicComboPopup
1092+ * @since 1.5
1093+ */
1094+ public AccessibleContext getAccessibleContext() {
1095+ AccessibleContext context = super.getAccessibleContext();
1096+ context.setAccessibleParent(comboBox);
1097+ return context;
1098+ }
1099+
1100+
1101+ /**
1102+ * This is is a utility method that helps event handlers figure out where to
1103+ * send the focus when the popup is brought up. The standard implementation
1104+ * delegates the focus to the editor (if the combo box is editable) or to
1105+ * the JComboBox if it is not editable.
1106+ */
1107+ protected void delegateFocus( MouseEvent e ) {
1108+ if ( comboBox.isEditable() ) {
1109+ Component comp = comboBox.getEditor().getEditorComponent();
1110+ if ((!(comp instanceof JComponent)) || ((JComponent)comp).isRequestFocusEnabled()) {
1111+ comp.requestFocus();
1112+ }
1113+ }
1114+ else if (comboBox.isRequestFocusEnabled()) {
1115+ comboBox.requestFocus();
1116+ }
1117+ }
1118+
1119+ /**
1120+ * Makes the popup visible if it is hidden and makes it hidden if it is
1121+ * visible.
1122+ */
1123+ protected void togglePopup() {
1124+ if ( isVisible() ) {
1125+ hide();
1126+ }
1127+ else {
1128+ show();
1129+ }
1130+ }
1131+
1132+ /**
1133+ * Sets the list selection index to the selectedIndex. This
1134+ * method is used to synchronize the list selection with the
1135+ * combo box selection.
1136+ *
1137+ * @param selectedIndex the index to set the list
1138+ */
1139+ private void setListSelection(int selectedIndex) {
1140+ if ( selectedIndex == -1 ) {
1141+ list.clearSelection();
1142+ }
1143+ else {
1144+ list.setSelectedIndex( selectedIndex );
1145+ list.ensureIndexIsVisible( selectedIndex );
1146+ }
1147+ }
1148+
1149+ protected MouseEvent convertMouseEvent( MouseEvent e ) {
1150+ Point convertedPoint = SwingUtilities.convertPoint( (Component)e.getSource(),
1151+ e.getPoint(), list );
1152+ MouseEvent newEvent = new MouseEvent( (Component)e.getSource(),
1153+ e.getID(),
1154+ e.getWhen(),
1155+ e.getModifiers(),
1156+ convertedPoint.x,
1157+ convertedPoint.y,
1158+ e.getXOnScreen(),
1159+ e.getYOnScreen(),
1160+ e.getClickCount(),
1161+ e.isPopupTrigger(),
1162+ MouseEvent.NOBUTTON );
1163+ return newEvent;
1164+ }
1165+
1166+
1167+ /**
1168+ * Retrieves the height of the popup based on the current
1169+ * ListCellRenderer and the maximum row count.
1170+ */
1171+ protected int getPopupHeightForRowCount(int maxRowCount) {
1172+ // Set the cached value of the minimum row count
1173+ int minRowCount = Math.min( maxRowCount, comboBox.getItemCount() );
1174+ int height = 0;
1175+ ListCellRenderer renderer = list.getCellRenderer();
1176+ Object value = null;
1177+
1178+ for ( int i = 0; i < minRowCount; ++i ) {
1179+ value = list.getModel().getElementAt( i );
1180+ Component c = renderer.getListCellRendererComponent( list, value, i, false, false );
1181+ height += c.getPreferredSize().height;
1182+ }
1183+
1184+ if (height == 0) {
1185+ height = comboBox.getHeight();
1186+ }
1187+
1188+ Border border = scroller.getViewportBorder();
1189+ if (border != null) {
1190+ Insets insets = border.getBorderInsets(null);
1191+ height += insets.top + insets.bottom;
1192+ }
1193+
1194+ border = scroller.getBorder();
1195+ if (border != null) {
1196+ Insets insets = border.getBorderInsets(null);
1197+ height += insets.top + insets.bottom;
1198+ }
1199+
1200+ return height;
1201+ }
1202+
1203+ /**
1204+ * Calculate the placement and size of the popup portion of the combo box based
1205+ * on the combo box location and the enclosing screen bounds. If
1206+ * no transformations are required, then the returned rectangle will
1207+ * have the same values as the parameters.
1208+ *
1209+ * @param px starting x location
1210+ * @param py starting y location
1211+ * @param pw starting width
1212+ * @param ph starting height
1213+ * @return a rectangle which represents the placement and size of the popup
1214+ */
1215+ protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
1216+ Toolkit toolkit = Toolkit.getDefaultToolkit();
1217+ Rectangle screenBounds;
1218+
1219+ // Calculate the desktop dimensions relative to the combo box.
1220+ GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
1221+ Point p = new Point();
1222+ SwingUtilities.convertPointFromScreen(p, comboBox);
1223+ if (gc != null) {
1224+ Insets screenInsets = toolkit.getScreenInsets(gc);
1225+ screenBounds = gc.getBounds();
1226+ screenBounds.width -= (screenInsets.left + screenInsets.right);
1227+ screenBounds.height -= (screenInsets.top + screenInsets.bottom);
1228+ screenBounds.x += (p.x + screenInsets.left);
1229+ screenBounds.y += (p.y + screenInsets.top);
1230+ }
1231+ else {
1232+ screenBounds = new Rectangle(p, toolkit.getScreenSize());
1233+ }
1234+
1235+ Rectangle rect = new Rectangle(px,py,pw,ph);
1236+ if (py+ph > screenBounds.y+screenBounds.height
1237+ && ph < screenBounds.height) {
1238+ rect.y = -rect.height;
1239+ }
1240+ return rect;
1241+ }
1242+
1243+ /**
1244+ * Calculates the upper left location of the Popup.
1245+ */
1246+ private Point getPopupLocation() {
1247+ Dimension popupSize = comboBox.getSize();
1248+ Insets insets = getInsets();
1249+
1250+ // reduce the width of the scrollpane by the insets so that the popup
1251+ // is the same width as the combo box.
1252+ popupSize.setSize(popupSize.width - (insets.right + insets.left),
1253+ getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
1254+ Rectangle popupBounds = computePopupBounds( 0, comboBox.getBounds().height,
1255+ popupSize.width, popupSize.height);
1256+ Dimension scrollSize = popupBounds.getSize();
1257+ Point popupLocation = popupBounds.getLocation();
1258+
1259+ scroller.setMaximumSize( scrollSize );
1260+ scroller.setPreferredSize( scrollSize );
1261+ scroller.setMinimumSize( scrollSize );
1262+
1263+ list.revalidate();
1264+
1265+ return popupLocation;
1266+ }
1267+
1268+ /**
1269+ * A utility method used by the event listeners. Given a mouse event, it changes
1270+ * the list selection to the list item below the mouse.
1271+ */
1272+ protected void updateListBoxSelectionForEvent(MouseEvent anEvent,boolean shouldScroll) {
1273+ // XXX - only seems to be called from this class. shouldScroll flag is
1274+ // never true
1275+ Point location = anEvent.getPoint();
1276+ if ( list == null )
1277+ return;
1278+ int index = list.locationToIndex(location);
1279+ if ( index == -1 ) {
1280+ if ( location.y < 0 )
1281+ index = 0;
1282+ else
1283+ index = comboBox.getModel().getSize() - 1;
1284+ }
1285+ if ( list.getSelectedIndex() != index ) {
1286+ list.setSelectedIndex(index);
1287+ if ( shouldScroll )
1288+ list.ensureIndexIsVisible(index);
1289+ }
1290+ }
1291+
1292+ //
1293+ // end Utility methods
1294+ //=================================================================
1295+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t