1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59:
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77: import ;
78: import ;
79: import ;
80:
81:
86: public class BasicComboPopup extends JPopupMenu implements ComboPopup
87: {
88:
89: protected Timer autoscrollTimer;
90:
91:
92: protected JComboBox comboBox;
93:
94:
95: protected boolean hasEntered;
96:
97:
102: protected boolean isAutoScrolling;
103:
104:
105: protected ItemListener itemListener;
106:
107:
108: protected KeyListener keyListener;
109:
110:
111: protected JList list;
112:
113:
114: protected ListDataListener listDataListener;
115:
116:
120: protected MouseListener listMouseListener;
121:
122:
126: protected MouseMotionListener listMouseMotionListener;
127:
128:
129: protected ListSelectionListener listSelectionListener;
130:
131:
132: protected MouseListener mouseListener;
133:
134:
138: protected MouseMotionListener mouseMotionListener;
139:
140:
144: protected PropertyChangeListener propertyChangeListener;
145:
146:
147: protected static final int SCROLL_DOWN = 1;
148:
149:
150: protected static final int SCROLL_UP = 0;
151:
152:
153: protected int scrollDirection;
154:
155:
156: protected JScrollPane scroller;
157:
158:
159: protected boolean valueIsAdjusting;
160:
161:
166: public BasicComboPopup(JComboBox comboBox)
167: {
168: this.comboBox = comboBox;
169: mouseListener = createMouseListener();
170: mouseMotionListener = createMouseMotionListener();
171: keyListener = createKeyListener();
172:
173: list = createList();
174: configureList();
175: scroller = createScroller();
176: configureScroller();
177: configurePopup();
178: installComboBoxListeners();
179: installKeyboardActions();
180: }
181:
182:
185: public void show()
186: {
187: Dimension size = comboBox.getSize();
188: size.height = getPopupHeightForRowCount(comboBox.getMaximumRowCount());
189: Insets i = getInsets();
190: size.width -= i.left + i.right;
191: Rectangle bounds = computePopupBounds(0, comboBox.getBounds().height,
192: size.width, size.height);
193:
194: scroller.setMaximumSize(bounds.getSize());
195: scroller.setPreferredSize(bounds.getSize());
196: scroller.setMinimumSize(bounds.getSize());
197: list.invalidate();
198:
199: syncListSelection();
200:
201: list.ensureIndexIsVisible(list.getSelectedIndex());
202: setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
203: show(comboBox, bounds.x, bounds.y);
204: }
205:
206:
209: public void hide()
210: {
211: MenuSelectionManager menuSelectionManager =
212: MenuSelectionManager.defaultManager();
213: javax.swing.MenuElement[] menuElements =
214: menuSelectionManager.getSelectedPath();
215: for (int i = 0; i < menuElements.length; i++)
216: {
217: if (menuElements[i] == this)
218: {
219: menuSelectionManager.clearSelectedPath();
220: break;
221: }
222: }
223: comboBox.repaint();
224: }
225:
226:
231: public JList getList()
232: {
233: return list;
234: }
235:
236:
242: public MouseListener getMouseListener()
243: {
244: return mouseListener;
245: }
246:
247:
253: public MouseMotionListener getMouseMotionListener()
254: {
255: return mouseMotionListener;
256: }
257:
258:
264: public KeyListener getKeyListener()
265: {
266: return keyListener;
267: }
268:
269:
272: public void uninstallingUI()
273: {
274: uninstallComboBoxModelListeners(comboBox.getModel());
275: uninstallListeners();
276: uninstallKeyboardActions();
277: }
278:
279:
286: protected void uninstallComboBoxModelListeners(ComboBoxModel model)
287: {
288: model.removeListDataListener(listDataListener);
289: }
290:
291:
294: protected void uninstallKeyboardActions()
295: {
296:
297: }
298:
299:
303: protected void firePopupMenuWillBecomeVisible()
304: {
305: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
306:
307: for (int i = 0; i < ll.length; i++)
308: ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox));
309: }
310:
311:
315: protected void firePopupMenuWillBecomeInvisible()
316: {
317: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
318:
319: for (int i = 0; i < ll.length; i++)
320: ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox));
321: }
322:
323:
327: protected void firePopupMenuCanceled()
328: {
329: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
330:
331: for (int i = 0; i < ll.length; i++)
332: ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox));
333: }
334:
335:
344: protected MouseListener createMouseListener()
345: {
346: return new InvocationMouseHandler();
347: }
348:
349:
358: protected MouseMotionListener createMouseMotionListener()
359: {
360: return new InvocationMouseMotionHandler();
361: }
362:
363:
368: protected KeyListener createKeyListener()
369: {
370: return new InvocationKeyHandler();
371: }
372:
373:
378: protected ListSelectionListener createListSelectionListener()
379: {
380: return new ListSelectionHandler();
381: }
382:
383:
389: protected ListDataListener createListDataListener()
390: {
391: return null;
392: }
393:
394:
401: protected MouseListener createListMouseListener()
402: {
403: return new ListMouseHandler();
404: }
405:
406:
414: protected MouseMotionListener createListMouseMotionListener()
415: {
416: return new ListMouseMotionHandler();
417: }
418:
419:
426: protected PropertyChangeListener createPropertyChangeListener()
427: {
428: return new PropertyChangeHandler();
429: }
430:
431:
437: protected ItemListener createItemListener()
438: {
439: return new ItemHandler();
440: }
441:
442:
447: protected JList createList()
448: {
449: JList l = new JList(comboBox.getModel());
450: return l;
451: }
452:
453:
457: protected void configureList()
458: {
459: list.setFont(comboBox.getFont());
460: list.setForeground(comboBox.getForeground());
461: list.setBackground(comboBox.getBackground());
462: Color sfg = UIManager.getColor("ComboBox.selectionForeground");
463: list.setSelectionForeground(sfg);
464: Color sbg = UIManager.getColor("ComboBox.selectionBackground");
465: list.setSelectionBackground(sbg);
466: list.setBorder(null);
467: list.setCellRenderer(comboBox.getRenderer());
468: list.setFocusable(false);
469: syncListSelection();
470: list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
471: installListListeners();
472: }
473:
474:
477: protected void installListListeners()
478: {
479:
480:
481: listMouseListener = createListMouseListener();
482: list.addMouseListener(listMouseListener);
483:
484:
485:
486: listMouseMotionListener = createListMouseMotionListener();
487: list.addMouseMotionListener(listMouseMotionListener);
488:
489: listSelectionListener = createListSelectionListener();
490: list.addListSelectionListener(listSelectionListener);
491: }
492:
493:
499: protected JScrollPane createScroller()
500: {
501: return new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
502: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
503: }
504:
505:
508: protected void configureScroller()
509: {
510: scroller.setBorder(null);
511: scroller.setFocusable(false);
512: scroller.getVerticalScrollBar().setFocusable(false);
513: }
514:
515:
519: protected void configurePopup()
520: {
521: setBorderPainted(true);
522: setBorder(BorderFactory.createLineBorder(Color.BLACK));
523: setOpaque(false);
524: add(scroller);
525: setFocusable(false);
526: }
527:
528:
532: protected void installComboBoxListeners()
533: {
534:
535: itemListener = createItemListener();
536: comboBox.addItemListener(itemListener);
537:
538: propertyChangeListener = createPropertyChangeListener();
539: comboBox.addPropertyChangeListener(propertyChangeListener);
540:
541: installComboBoxModelListeners(comboBox.getModel());
542: }
543:
544:
550: protected void installComboBoxModelListeners(ComboBoxModel model)
551: {
552:
553:
554: listDataListener = createListDataListener();
555: comboBox.getModel().addListDataListener(listDataListener);
556: }
557:
558:
561: protected void installKeyboardActions()
562: {
563:
564: }
565:
566:
572: public boolean isFocusTraversable()
573: {
574: return false;
575: }
576:
577:
583: protected void startAutoScrolling(int direction)
584: {
585:
586: isAutoScrolling = true;
587:
588: if (direction == SCROLL_UP)
589: autoScrollUp();
590: else
591: autoScrollDown();
592: }
593:
594:
597: protected void stopAutoScrolling()
598: {
599:
600: isAutoScrolling = false;
601: }
602:
603:
607: protected void autoScrollUp()
608: {
609:
610: JScrollBar scrollbar = scroller.getVerticalScrollBar();
611: int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
612: SwingConstants.VERTICAL,
613: SCROLL_UP);
614:
615: scrollbar.setValue(scrollbar.getValue() - scrollToNext);
616:
617:
618:
619: if (list.getSelectedIndex() != 0)
620: list.setSelectedIndex(list.getSelectedIndex() - 1);
621: }
622:
623:
627: protected void autoScrollDown()
628: {
629:
630: JScrollBar scrollbar = scroller.getVerticalScrollBar();
631: int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
632: SwingConstants.VERTICAL,
633: SCROLL_DOWN);
634: scrollbar.setValue(scrollbar.getValue() + scrollToNext);
635:
636:
637:
638: if (list.getSelectedIndex() + 1 != comboBox.getItemCount())
639: list.setSelectedIndex(list.getSelectedIndex() + 1);
640: }
641:
642:
649: protected void delegateFocus(MouseEvent e)
650: {
651: if (comboBox.isEditable())
652: comboBox.getEditor().getEditorComponent().requestFocus();
653: else
654: comboBox.requestFocus();
655: }
656:
657:
661: protected void togglePopup()
662: {
663: if (isVisible())
664: hide();
665: else
666: show();
667: }
668:
669:
676: protected MouseEvent convertMouseEvent(MouseEvent e)
677: {
678: Point point = SwingUtilities.convertPoint((Component) e.getSource(),
679: e.getPoint(), list);
680: MouseEvent newEvent = new MouseEvent((Component) e.getSource(),
681: e.getID(), e.getWhen(),
682: e.getModifiers(), point.x, point.y,
683: e.getModifiers(),
684: e.isPopupTrigger());
685: return newEvent;
686: }
687:
688:
699: protected int getPopupHeightForRowCount(int maxRowCount)
700: {
701: int totalHeight = 0;
702: ListCellRenderer rend = list.getCellRenderer();
703:
704: if (comboBox.getItemCount() < maxRowCount)
705: maxRowCount = comboBox.getItemCount();
706:
707: for (int i = 0; i < maxRowCount; i++)
708: {
709: Component comp = rend.getListCellRendererComponent(list,
710: comboBox.getModel()
711: .getElementAt(i),
712: -1, false, false);
713: Dimension dim = comp.getPreferredSize();
714: totalHeight += dim.height;
715: }
716:
717: return totalHeight == 0 ? 100 : totalHeight;
718: }
719:
720:
730: protected Rectangle computePopupBounds(int px, int py, int pw, int ph)
731: {
732: return new Rectangle(px, py, pw, ph);
733: }
734:
735:
742: protected void updateListBoxSelectionForEvent(MouseEvent anEvent,
743: boolean shouldScroll)
744: {
745: Point point = anEvent.getPoint();
746: if (list != null)
747: {
748: int index = list.locationToIndex(point);
749: if (index == -1)
750: {
751: if (point.y < 0)
752: index = 0;
753: else
754: index = comboBox.getModel().getSize() - 1;
755: }
756: if (list.getSelectedIndex() != index)
757: {
758: list.setSelectedIndex(index);
759: if (shouldScroll)
760: list.ensureIndexIsVisible(index);
761: }
762: }
763: }
764:
765:
773: protected class InvocationMouseHandler extends MouseAdapter
774: {
775:
778: protected InvocationMouseHandler()
779: {
780:
781: }
782:
783:
790: public void mousePressed(MouseEvent e)
791: {
792: if (SwingUtilities.isLeftMouseButton(e) && comboBox.isEnabled())
793: {
794: delegateFocus(e);
795: togglePopup();
796: }
797: }
798:
799:
806: public void mouseReleased(MouseEvent e)
807: {
808: Component component = (Component) e.getSource();
809: Dimension size = component.getSize();
810: Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1);
811:
812:
813:
814: if (! bounds.contains(e.getPoint()))
815: {
816: MouseEvent convEvent = convertMouseEvent(e);
817: Point point = convEvent.getPoint();
818: Rectangle visRect = new Rectangle();
819: list.computeVisibleRect(visRect);
820: if (visRect.contains(point))
821: {
822: updateListBoxSelectionForEvent(convEvent, false);
823: comboBox.setSelectedIndex(list.getSelectedIndex());
824: }
825: hide();
826: }
827: hasEntered = false;
828: stopAutoScrolling();
829: }
830: }
831:
832:
836: protected class InvocationMouseMotionHandler extends MouseMotionAdapter
837: {
838:
841: protected InvocationMouseMotionHandler()
842: {
843:
844: }
845:
846:
850: public void mouseDragged(MouseEvent e)
851: {
852: if (isVisible())
853: {
854: MouseEvent convEvent = convertMouseEvent(e);
855: Rectangle visRect = new Rectangle();
856: list.computeVisibleRect(visRect);
857: if (convEvent.getPoint().y >= visRect.y
858: && (convEvent.getPoint().y <= visRect.y + visRect.height - 1))
859: {
860: hasEntered = true;
861: if (isAutoScrolling)
862: stopAutoScrolling();
863: Point point = convEvent.getPoint();
864: if (visRect.contains(point))
865: {
866: valueIsAdjusting = true;
867: updateListBoxSelectionForEvent(convEvent, false);
868: valueIsAdjusting = false;
869: }
870: }
871: else if (hasEntered)
872: {
873: int dir = convEvent.getPoint().y < visRect.y ? SCROLL_UP
874: : SCROLL_DOWN;
875: if (isAutoScrolling && scrollDirection != dir)
876: {
877: stopAutoScrolling();
878: startAutoScrolling(dir);
879: }
880: else if (!isAutoScrolling)
881: startAutoScrolling(dir);
882: }
883: else if (e.getPoint().y < 0)
884: {
885: hasEntered = true;
886: startAutoScrolling(SCROLL_UP);
887: }
888: }
889: }
890: }
891:
892:
897: protected class ItemHandler extends Object implements ItemListener
898: {
899:
902: protected ItemHandler()
903: {
904:
905: }
906:
907:
912: public void itemStateChanged(ItemEvent e)
913: {
914: if (e.getStateChange() == ItemEvent.SELECTED && ! valueIsAdjusting)
915: {
916: valueIsAdjusting = true;
917: syncListSelection();
918: valueIsAdjusting = false;
919: list.ensureIndexIsVisible(comboBox.getSelectedIndex());
920: }
921: }
922: }
923:
924:
930: protected class ListMouseHandler extends MouseAdapter
931: {
932: protected ListMouseHandler()
933: {
934:
935: }
936:
937: public void mousePressed(MouseEvent e)
938: {
939:
940: }
941:
942: public void mouseReleased(MouseEvent anEvent)
943: {
944: comboBox.setSelectedIndex(list.getSelectedIndex());
945: hide();
946: }
947: }
948:
949:
954: protected class ListMouseMotionHandler extends MouseMotionAdapter
955: {
956: protected ListMouseMotionHandler()
957: {
958:
959: }
960:
961: public void mouseMoved(MouseEvent anEvent)
962: {
963: Point point = anEvent.getPoint();
964: Rectangle visRect = new Rectangle();
965: list.computeVisibleRect(visRect);
966: if (visRect.contains(point))
967: {
968: valueIsAdjusting = true;
969: updateListBoxSelectionForEvent(anEvent, false);
970: valueIsAdjusting = false;
971: }
972: }
973: }
974:
975:
979: protected class PropertyChangeHandler extends Object
980: implements PropertyChangeListener
981: {
982: protected PropertyChangeHandler()
983: {
984:
985: }
986:
987: public void propertyChange(PropertyChangeEvent e)
988: {
989: if (e.getPropertyName().equals("renderer"))
990: {
991: list.setCellRenderer(comboBox.getRenderer());
992: if (isVisible())
993: hide();
994: }
995: if (e.getPropertyName().equals("model"))
996: {
997: ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
998: uninstallComboBoxModelListeners(oldModel);
999: ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
1000: list.setModel(newModel);
1001: installComboBoxModelListeners(newModel);
1002: if (comboBox.getItemCount() > 0)
1003: comboBox.setSelectedIndex(0);
1004: if (isVisible())
1005: hide();
1006: }
1007: }
1008: }
1009:
1010:
1011:
1012:
1015: private void uninstallListeners()
1016: {
1017: uninstallComboBoxListeners();
1018: uninstallComboBoxModelListeners(comboBox.getModel());
1019: }
1020:
1021:
1025: private void uninstallListListeners()
1026: {
1027: list.removeMouseListener(listMouseListener);
1028: listMouseListener = null;
1029:
1030: list.removeMouseMotionListener(listMouseMotionListener);
1031: listMouseMotionListener = null;
1032: }
1033:
1034:
1038: private void uninstallComboBoxListeners()
1039: {
1040: comboBox.removeItemListener(itemListener);
1041: itemListener = null;
1042:
1043: comboBox.removePropertyChangeListener(propertyChangeListener);
1044: propertyChangeListener = null;
1045: }
1046:
1047: void syncListSelection()
1048: {
1049: int index = comboBox.getSelectedIndex();
1050: if (index == -1)
1051: list.clearSelection();
1052: else
1053: list.setSelectedIndex(index);
1054: }
1055:
1056:
1057:
1058:
1059:
1060:
1061:
1064: public class ListDataHandler extends Object implements ListDataListener
1065: {
1066: public ListDataHandler()
1067: {
1068:
1069: }
1070:
1071: public void contentsChanged(ListDataEvent e)
1072: {
1073:
1074: }
1075:
1076: public void intervalAdded(ListDataEvent e)
1077: {
1078:
1079: }
1080:
1081: public void intervalRemoved(ListDataEvent e)
1082: {
1083:
1084: }
1085: }
1086:
1087:
1090: protected class ListSelectionHandler extends Object
1091: implements ListSelectionListener
1092: {
1093: protected ListSelectionHandler()
1094: {
1095:
1096: }
1097:
1098: public void valueChanged(ListSelectionEvent e)
1099: {
1100:
1101: }
1102: }
1103:
1104:
1107: public class InvocationKeyHandler extends KeyAdapter
1108: {
1109: public InvocationKeyHandler()
1110: {
1111:
1112: }
1113:
1114: public void keyReleased(KeyEvent e)
1115: {
1116:
1117: }
1118: }
1119: }