1:
37:
38:
39: package ;
40:
41: import ;
42:
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: import ;
60: import ;
61:
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: import ;
81: import ;
82:
83:
88: public class BasicComboPopup extends JPopupMenu implements ComboPopup
89: {
90:
91: protected Timer autoscrollTimer;
92:
93:
94: protected JComboBox comboBox;
95:
96:
97: protected boolean hasEntered;
98:
99:
104: protected boolean isAutoScrolling;
105:
106:
107: protected ItemListener itemListener;
108:
109:
110: protected KeyListener keyListener;
111:
112:
113: protected JList list;
114:
115:
116: protected ListDataListener listDataListener;
117:
118:
122: protected MouseListener listMouseListener;
123:
124:
128: protected MouseMotionListener listMouseMotionListener;
129:
130:
131: protected ListSelectionListener listSelectionListener;
132:
133:
134: protected MouseListener mouseListener;
135:
136:
140: protected MouseMotionListener mouseMotionListener;
141:
142:
146: protected PropertyChangeListener propertyChangeListener;
147:
148:
149: protected static final int SCROLL_DOWN = 1;
150:
151:
152: protected static final int SCROLL_UP = 0;
153:
154:
155: protected int scrollDirection;
156:
157:
158: protected JScrollPane scroller;
159:
160:
161: protected boolean valueIsAdjusting;
162:
163:
168: public BasicComboPopup(JComboBox comboBox)
169: {
170: this.comboBox = comboBox;
171: mouseListener = createMouseListener();
172: mouseMotionListener = createMouseMotionListener();
173: keyListener = createKeyListener();
174:
175: list = createList();
176: configureList();
177: scroller = createScroller();
178: configureScroller();
179: configurePopup();
180: installComboBoxListeners();
181: installKeyboardActions();
182: }
183:
184:
187: public void show()
188: {
189: Dimension size = comboBox.getSize();
190: size.height = getPopupHeightForRowCount(comboBox.getMaximumRowCount());
191: Insets i = getInsets();
192: size.width -= i.left + i.right;
193: Rectangle bounds = computePopupBounds(0, comboBox.getBounds().height,
194: size.width, size.height);
195:
196: scroller.setMaximumSize(bounds.getSize());
197: scroller.setPreferredSize(bounds.getSize());
198: scroller.setMinimumSize(bounds.getSize());
199: list.invalidate();
200:
201: syncListSelection();
202:
203: list.ensureIndexIsVisible(list.getSelectedIndex());
204: setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
205: show(comboBox, bounds.x, bounds.y);
206: }
207:
208:
211: public void hide()
212: {
213: MenuSelectionManager menuSelectionManager =
214: MenuSelectionManager.defaultManager();
215: javax.swing.MenuElement[] menuElements =
216: menuSelectionManager.getSelectedPath();
217: for (int i = 0; i < menuElements.length; i++)
218: {
219: if (menuElements[i] == this)
220: {
221: menuSelectionManager.clearSelectedPath();
222: break;
223: }
224: }
225: comboBox.repaint();
226: }
227:
228:
233: public JList getList()
234: {
235: return list;
236: }
237:
238:
244: public MouseListener getMouseListener()
245: {
246: return mouseListener;
247: }
248:
249:
255: public MouseMotionListener getMouseMotionListener()
256: {
257: return mouseMotionListener;
258: }
259:
260:
266: public KeyListener getKeyListener()
267: {
268: return keyListener;
269: }
270:
271:
274: public void uninstallingUI()
275: {
276: uninstallComboBoxModelListeners(comboBox.getModel());
277: uninstallListeners();
278: uninstallKeyboardActions();
279: }
280:
281:
288: protected void uninstallComboBoxModelListeners(ComboBoxModel model)
289: {
290: model.removeListDataListener(listDataListener);
291: }
292:
293:
296: protected void uninstallKeyboardActions()
297: throws NotImplementedException
298: {
299:
300: }
301:
302:
306: protected void firePopupMenuWillBecomeVisible()
307: {
308: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
309:
310: for (int i = 0; i < ll.length; i++)
311: ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox));
312: }
313:
314:
318: protected void firePopupMenuWillBecomeInvisible()
319: {
320: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
321:
322: for (int i = 0; i < ll.length; i++)
323: ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox));
324: }
325:
326:
330: protected void firePopupMenuCanceled()
331: {
332: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
333:
334: for (int i = 0; i < ll.length; i++)
335: ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox));
336: }
337:
338:
347: protected MouseListener createMouseListener()
348: {
349: return new InvocationMouseHandler();
350: }
351:
352:
361: protected MouseMotionListener createMouseMotionListener()
362: {
363: return new InvocationMouseMotionHandler();
364: }
365:
366:
371: protected KeyListener createKeyListener()
372: {
373: return new InvocationKeyHandler();
374: }
375:
376:
381: protected ListSelectionListener createListSelectionListener()
382: {
383: return new ListSelectionHandler();
384: }
385:
386:
392: protected ListDataListener createListDataListener()
393: {
394: return null;
395: }
396:
397:
404: protected MouseListener createListMouseListener()
405: {
406: return new ListMouseHandler();
407: }
408:
409:
417: protected MouseMotionListener createListMouseMotionListener()
418: {
419: return new ListMouseMotionHandler();
420: }
421:
422:
429: protected PropertyChangeListener createPropertyChangeListener()
430: {
431: return new PropertyChangeHandler();
432: }
433:
434:
440: protected ItemListener createItemListener()
441: {
442: return new ItemHandler();
443: }
444:
445:
450: protected JList createList()
451: {
452: JList l = new JList(comboBox.getModel());
453: return l;
454: }
455:
456:
460: protected void configureList()
461: {
462: list.setFont(comboBox.getFont());
463: list.setForeground(comboBox.getForeground());
464: list.setBackground(comboBox.getBackground());
465: Color sfg = UIManager.getColor("ComboBox.selectionForeground");
466: list.setSelectionForeground(sfg);
467: Color sbg = UIManager.getColor("ComboBox.selectionBackground");
468: list.setSelectionBackground(sbg);
469: list.setBorder(null);
470: list.setCellRenderer(comboBox.getRenderer());
471: list.setFocusable(false);
472: syncListSelection();
473: list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
474: installListListeners();
475: }
476:
477:
480: protected void installListListeners()
481: {
482:
483:
484: listMouseListener = createListMouseListener();
485: list.addMouseListener(listMouseListener);
486:
487:
488:
489: listMouseMotionListener = createListMouseMotionListener();
490: list.addMouseMotionListener(listMouseMotionListener);
491:
492: listSelectionListener = createListSelectionListener();
493: list.addListSelectionListener(listSelectionListener);
494: }
495:
496:
502: protected JScrollPane createScroller()
503: {
504: return new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
505: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
506: }
507:
508:
511: protected void configureScroller()
512: {
513: scroller.setBorder(null);
514: scroller.setFocusable(false);
515: scroller.getVerticalScrollBar().setFocusable(false);
516: }
517:
518:
522: protected void configurePopup()
523: {
524: setBorderPainted(true);
525: setBorder(BorderFactory.createLineBorder(Color.BLACK));
526: setOpaque(false);
527: add(scroller);
528: setFocusable(false);
529: }
530:
531:
535: protected void installComboBoxListeners()
536: {
537:
538: itemListener = createItemListener();
539: comboBox.addItemListener(itemListener);
540:
541: propertyChangeListener = createPropertyChangeListener();
542: comboBox.addPropertyChangeListener(propertyChangeListener);
543:
544: installComboBoxModelListeners(comboBox.getModel());
545: }
546:
547:
553: protected void installComboBoxModelListeners(ComboBoxModel model)
554: {
555:
556:
557: listDataListener = createListDataListener();
558: comboBox.getModel().addListDataListener(listDataListener);
559: }
560:
561:
564: protected void installKeyboardActions()
565: throws NotImplementedException
566: {
567:
568: }
569:
570:
576: public boolean isFocusTraversable()
577: {
578: return false;
579: }
580:
581:
587: protected void startAutoScrolling(int direction)
588: {
589:
590: isAutoScrolling = true;
591:
592: if (direction == SCROLL_UP)
593: autoScrollUp();
594: else
595: autoScrollDown();
596: }
597:
598:
601: protected void stopAutoScrolling()
602: {
603:
604: isAutoScrolling = false;
605: }
606:
607:
611: protected void autoScrollUp()
612: {
613:
614: JScrollBar scrollbar = scroller.getVerticalScrollBar();
615: int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
616: SwingConstants.VERTICAL,
617: SCROLL_UP);
618:
619: scrollbar.setValue(scrollbar.getValue() - scrollToNext);
620:
621:
622:
623: if (list.getSelectedIndex() != 0)
624: list.setSelectedIndex(list.getSelectedIndex() - 1);
625: }
626:
627:
631: protected void autoScrollDown()
632: {
633:
634: JScrollBar scrollbar = scroller.getVerticalScrollBar();
635: int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
636: SwingConstants.VERTICAL,
637: SCROLL_DOWN);
638: scrollbar.setValue(scrollbar.getValue() + scrollToNext);
639:
640:
641:
642: if (list.getSelectedIndex() + 1 != comboBox.getItemCount())
643: list.setSelectedIndex(list.getSelectedIndex() + 1);
644: }
645:
646:
653: protected void delegateFocus(MouseEvent e)
654: {
655: if (comboBox.isEditable())
656: comboBox.getEditor().getEditorComponent().requestFocus();
657: else
658: comboBox.requestFocus();
659: }
660:
661:
665: protected void togglePopup()
666: {
667: if (isVisible())
668: hide();
669: else
670: show();
671: }
672:
673:
680: protected MouseEvent convertMouseEvent(MouseEvent e)
681: {
682: Point point = SwingUtilities.convertPoint((Component) e.getSource(),
683: e.getPoint(), list);
684: MouseEvent newEvent= new MouseEvent((Component) e.getSource(),
685: e.getID(), e.getWhen(),
686: e.getModifiers(), point.x, point.y,
687: e.getModifiers(),
688: e.isPopupTrigger());
689: return newEvent;
690: }
691:
692:
703: protected int getPopupHeightForRowCount(int maxRowCount)
704: {
705: int totalHeight = 0;
706: ListCellRenderer rend = list.getCellRenderer();
707:
708: if (comboBox.getItemCount() < maxRowCount)
709: maxRowCount = comboBox.getItemCount();
710:
711: for (int i = 0; i < maxRowCount; i++)
712: {
713: Component comp = rend.getListCellRendererComponent(list,
714: comboBox.getModel()
715: .getElementAt(i),
716: -1, false, false);
717: Dimension dim = comp.getPreferredSize();
718: totalHeight += dim.height;
719: }
720:
721: return totalHeight == 0 ? 100 : totalHeight;
722: }
723:
724:
734: protected Rectangle computePopupBounds(int px, int py, int pw, int ph)
735: {
736: return new Rectangle(px, py, pw, ph);
737: }
738:
739:
746: protected void updateListBoxSelectionForEvent(MouseEvent anEvent,
747: boolean shouldScroll)
748: {
749: Point point = anEvent.getPoint();
750: if (list != null)
751: {
752: int index = list.locationToIndex(point);
753: if (index == -1)
754: {
755: if (point.y < 0)
756: index = 0;
757: else
758: index = comboBox.getModel().getSize() - 1;
759: }
760: if (list.getSelectedIndex() != index)
761: {
762: list.setSelectedIndex(index);
763: if (shouldScroll)
764: list.ensureIndexIsVisible(index);
765: }
766: }
767: }
768:
769:
777: protected class InvocationMouseHandler extends MouseAdapter
778: {
779:
782: protected InvocationMouseHandler()
783: {
784:
785: }
786:
787:
794: public void mousePressed(MouseEvent e)
795: {
796: if (SwingUtilities.isLeftMouseButton(e) && comboBox.isEnabled())
797: {
798: delegateFocus(e);
799: togglePopup();
800: }
801: }
802:
803:
810: public void mouseReleased(MouseEvent e)
811: {
812: Component component = (Component) e.getSource();
813: Dimension size = component.getSize();
814: Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1);
815:
816:
817:
818: if (! bounds.contains(e.getPoint()))
819: {
820: MouseEvent convEvent = convertMouseEvent(e);
821: Point point = convEvent.getPoint();
822: Rectangle visRect = new Rectangle();
823: list.computeVisibleRect(visRect);
824: if (visRect.contains(point))
825: {
826: updateListBoxSelectionForEvent(convEvent, false);
827: comboBox.setSelectedIndex(list.getSelectedIndex());
828: }
829: hide();
830: }
831: hasEntered = false;
832: stopAutoScrolling();
833: }
834: }
835:
836:
840: protected class InvocationMouseMotionHandler extends MouseMotionAdapter
841: {
842:
845: protected InvocationMouseMotionHandler()
846: {
847:
848: }
849:
850:
854: public void mouseDragged(MouseEvent e)
855: {
856: if (isVisible())
857: {
858: MouseEvent convEvent = convertMouseEvent(e);
859: Rectangle visRect = new Rectangle();
860: list.computeVisibleRect(visRect);
861: if (convEvent.getPoint().y >= visRect.y
862: && (convEvent.getPoint().y <= visRect.y + visRect.height - 1))
863: {
864: hasEntered = true;
865: if (isAutoScrolling)
866: stopAutoScrolling();
867: Point point = convEvent.getPoint();
868: if (visRect.contains(point))
869: {
870: valueIsAdjusting = true;
871: updateListBoxSelectionForEvent(convEvent, false);
872: valueIsAdjusting = false;
873: }
874: }
875: else if (hasEntered)
876: {
877: int dir = convEvent.getPoint().y < visRect.y ? SCROLL_UP
878: : SCROLL_DOWN;
879: if (isAutoScrolling && scrollDirection != dir)
880: {
881: stopAutoScrolling();
882: startAutoScrolling(dir);
883: }
884: else if (!isAutoScrolling)
885: startAutoScrolling(dir);
886: }
887: else if (e.getPoint().y < 0)
888: {
889: hasEntered = true;
890: startAutoScrolling(SCROLL_UP);
891: }
892: }
893: }
894: }
895:
896:
901: protected class ItemHandler extends Object implements ItemListener
902: {
903:
906: protected ItemHandler()
907: {
908:
909: }
910:
911:
916: public void itemStateChanged(ItemEvent e)
917: {
918: if (e.getStateChange() == ItemEvent.SELECTED && ! valueIsAdjusting)
919: {
920: valueIsAdjusting = true;
921: syncListSelection();
922: valueIsAdjusting = false;
923: list.ensureIndexIsVisible(comboBox.getSelectedIndex());
924: }
925: }
926: }
927:
928:
934: protected class ListMouseHandler extends MouseAdapter
935: {
936: protected ListMouseHandler()
937: {
938:
939: }
940:
941: public void mousePressed(MouseEvent e)
942: {
943:
944: }
945:
946: public void mouseReleased(MouseEvent anEvent)
947: {
948: comboBox.setSelectedIndex(list.getSelectedIndex());
949: hide();
950: }
951: }
952:
953:
958: protected class ListMouseMotionHandler extends MouseMotionAdapter
959: {
960: protected ListMouseMotionHandler()
961: {
962:
963: }
964:
965: public void mouseMoved(MouseEvent anEvent)
966: {
967: Point point = anEvent.getPoint();
968: Rectangle visRect = new Rectangle();
969: list.computeVisibleRect(visRect);
970: if (visRect.contains(point))
971: {
972: valueIsAdjusting = true;
973: updateListBoxSelectionForEvent(anEvent, false);
974: valueIsAdjusting = false;
975: }
976: }
977: }
978:
979:
983: protected class PropertyChangeHandler extends Object
984: implements PropertyChangeListener
985: {
986: protected PropertyChangeHandler()
987: {
988:
989: }
990:
991: public void propertyChange(PropertyChangeEvent e)
992: {
993: if (e.getPropertyName().equals("renderer"))
994: {
995: list.setCellRenderer(comboBox.getRenderer());
996: if (isVisible())
997: hide();
998: }
999: if (e.getPropertyName().equals("model"))
1000: {
1001: ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
1002: uninstallComboBoxModelListeners(oldModel);
1003: ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
1004: list.setModel(newModel);
1005: installComboBoxModelListeners(newModel);
1006: if (comboBox.getItemCount() > 0)
1007: comboBox.setSelectedIndex(0);
1008: if (isVisible())
1009: hide();
1010: }
1011: }
1012: }
1013:
1014:
1015:
1016:
1019: private void uninstallListeners()
1020: {
1021: uninstallComboBoxListeners();
1022: uninstallComboBoxModelListeners(comboBox.getModel());
1023: }
1024:
1025:
1029: private void uninstallListListeners()
1030: {
1031: list.removeMouseListener(listMouseListener);
1032: listMouseListener = null;
1033:
1034: list.removeMouseMotionListener(listMouseMotionListener);
1035: listMouseMotionListener = null;
1036: }
1037:
1038:
1042: private void uninstallComboBoxListeners()
1043: {
1044: comboBox.removeItemListener(itemListener);
1045: itemListener = null;
1046:
1047: comboBox.removePropertyChangeListener(propertyChangeListener);
1048: propertyChangeListener = null;
1049: }
1050:
1051: void syncListSelection()
1052: {
1053: int index = comboBox.getSelectedIndex();
1054: if (index == -1)
1055: list.clearSelection();
1056: else
1057: list.setSelectedIndex(index);
1058: }
1059:
1060:
1061:
1062:
1063:
1064:
1065:
1068: public class ListDataHandler extends Object implements ListDataListener
1069: {
1070: public ListDataHandler()
1071: {
1072:
1073: }
1074:
1075: public void contentsChanged(ListDataEvent e)
1076: {
1077:
1078: }
1079:
1080: public void intervalAdded(ListDataEvent e)
1081: {
1082:
1083: }
1084:
1085: public void intervalRemoved(ListDataEvent e)
1086: {
1087:
1088: }
1089: }
1090:
1091:
1094: protected class ListSelectionHandler extends Object
1095: implements ListSelectionListener
1096: {
1097: protected ListSelectionHandler()
1098: {
1099:
1100: }
1101:
1102: public void valueChanged(ListSelectionEvent e)
1103: {
1104:
1105: }
1106: }
1107:
1108:
1111: public class InvocationKeyHandler extends KeyAdapter
1112: {
1113: public InvocationKeyHandler()
1114: {
1115:
1116: }
1117:
1118: public void keyReleased(KeyEvent e)
1119: {
1120:
1121: }
1122: }
1123: }