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
26package javax.swing.plaf.basic;
27
28import javax.accessibility.AccessibleContext;
29import javax.swing.*;
30import javax.swing.border.Border;
31import javax.swing.border.LineBorder;
32import javax.swing.event.*;
33import java.awt.*;
34import java.awt.event.*;
35import java.beans.PropertyChangeListener;
36import java.beans.PropertyChangeEvent;
37import 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 */
63public 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