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: if (propertyChangeListener != null)
275: {
276: comboBox.removePropertyChangeListener(propertyChangeListener);
277: }
278: if (itemListener != null)
279: {
280: comboBox.removeItemListener(itemListener);
281: }
282: uninstallComboBoxModelListeners(comboBox.getModel());
283: uninstallKeyboardActions();
284: uninstallListListeners();
285: }
286:
287:
294: protected void uninstallComboBoxModelListeners(ComboBoxModel model)
295: {
296: model.removeListDataListener(listDataListener);
297: }
298:
299:
302: protected void uninstallKeyboardActions()
303: {
304:
305: }
306:
307:
311: protected void firePopupMenuWillBecomeVisible()
312: {
313: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
314:
315: for (int i = 0; i < ll.length; i++)
316: ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox));
317: }
318:
319:
323: protected void firePopupMenuWillBecomeInvisible()
324: {
325: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
326:
327: for (int i = 0; i < ll.length; i++)
328: ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox));
329: }
330:
331:
335: protected void firePopupMenuCanceled()
336: {
337: PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
338:
339: for (int i = 0; i < ll.length; i++)
340: ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox));
341: }
342:
343:
352: protected MouseListener createMouseListener()
353: {
354: return new InvocationMouseHandler();
355: }
356:
357:
366: protected MouseMotionListener createMouseMotionListener()
367: {
368: return new InvocationMouseMotionHandler();
369: }
370:
371:
376: protected KeyListener createKeyListener()
377: {
378: return new InvocationKeyHandler();
379: }
380:
381:
386: protected ListSelectionListener createListSelectionListener()
387: {
388: return new ListSelectionHandler();
389: }
390:
391:
397: protected ListDataListener createListDataListener()
398: {
399: return null;
400: }
401:
402:
409: protected MouseListener createListMouseListener()
410: {
411: return new ListMouseHandler();
412: }
413:
414:
422: protected MouseMotionListener createListMouseMotionListener()
423: {
424: return new ListMouseMotionHandler();
425: }
426:
427:
434: protected PropertyChangeListener createPropertyChangeListener()
435: {
436: return new PropertyChangeHandler();
437: }
438:
439:
445: protected ItemListener createItemListener()
446: {
447: return new ItemHandler();
448: }
449:
450:
455: protected JList createList()
456: {
457: JList l = new JList(comboBox.getModel());
458: return l;
459: }
460:
461:
465: protected void configureList()
466: {
467: list.setFont(comboBox.getFont());
468: list.setForeground(comboBox.getForeground());
469: list.setBackground(comboBox.getBackground());
470: Color sfg = UIManager.getColor("ComboBox.selectionForeground");
471: list.setSelectionForeground(sfg);
472: Color sbg = UIManager.getColor("ComboBox.selectionBackground");
473: list.setSelectionBackground(sbg);
474: list.setBorder(null);
475: list.setCellRenderer(comboBox.getRenderer());
476: list.setFocusable(false);
477: list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
478: installListListeners();
479: }
480:
481:
484: protected void installListListeners()
485: {
486:
487:
488: listMouseListener = createListMouseListener();
489: list.addMouseListener(listMouseListener);
490:
491:
492:
493: listMouseMotionListener = createListMouseMotionListener();
494: list.addMouseMotionListener(listMouseMotionListener);
495:
496: listSelectionListener = createListSelectionListener();
497: list.addListSelectionListener(listSelectionListener);
498: }
499:
500:
506: protected JScrollPane createScroller()
507: {
508: return new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
509: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
510: }
511:
512:
515: protected void configureScroller()
516: {
517: scroller.setBorder(null);
518: scroller.setFocusable(false);
519: scroller.getVerticalScrollBar().setFocusable(false);
520: }
521:
522:
526: protected void configurePopup()
527: {
528: setBorderPainted(true);
529: setBorder(BorderFactory.createLineBorder(Color.BLACK));
530: setOpaque(false);
531: add(scroller);
532: setFocusable(false);
533: }
534:
535:
539: protected void installComboBoxListeners()
540: {
541:
542: itemListener = createItemListener();
543: comboBox.addItemListener(itemListener);
544:
545: propertyChangeListener = createPropertyChangeListener();
546: comboBox.addPropertyChangeListener(propertyChangeListener);
547:
548: installComboBoxModelListeners(comboBox.getModel());
549: }
550:
551:
557: protected void installComboBoxModelListeners(ComboBoxModel model)
558: {
559:
560:
561: listDataListener = createListDataListener();
562: comboBox.getModel().addListDataListener(listDataListener);
563: }
564:
565:
568: protected void installKeyboardActions()
569: {
570:
571: }
572:
573:
579: public boolean isFocusTraversable()
580: {
581: return false;
582: }
583:
584:
590: protected void startAutoScrolling(int direction)
591: {
592:
593: isAutoScrolling = true;
594:
595: if (direction == SCROLL_UP)
596: autoScrollUp();
597: else
598: autoScrollDown();
599: }
600:
601:
604: protected void stopAutoScrolling()
605: {
606:
607: isAutoScrolling = false;
608: }
609:
610:
614: protected void autoScrollUp()
615: {
616:
617: JScrollBar scrollbar = scroller.getVerticalScrollBar();
618: int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
619: SwingConstants.VERTICAL,
620: SCROLL_UP);
621:
622: scrollbar.setValue(scrollbar.getValue() - scrollToNext);
623:
624:
625:
626: if (list.getSelectedIndex() != 0)
627: list.setSelectedIndex(list.getSelectedIndex() - 1);
628: }
629:
630:
634: protected void autoScrollDown()
635: {
636:
637: JScrollBar scrollbar = scroller.getVerticalScrollBar();
638: int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
639: SwingConstants.VERTICAL,
640: SCROLL_DOWN);
641: scrollbar.setValue(scrollbar.getValue() + scrollToNext);
642:
643:
644:
645: if (list.getSelectedIndex() + 1 != comboBox.getItemCount())
646: list.setSelectedIndex(list.getSelectedIndex() + 1);
647: }
648:
649:
656: protected void delegateFocus(MouseEvent e)
657: {
658: if (comboBox.isEditable())
659: comboBox.getEditor().getEditorComponent().requestFocus();
660: else
661: comboBox.requestFocus();
662: }
663:
664:
668: protected void togglePopup()
669: {
670: if (isVisible())
671: hide();
672: else
673: show();
674: }
675:
676:
683: protected MouseEvent convertMouseEvent(MouseEvent e)
684: {
685: Point point = SwingUtilities.convertPoint((Component) e.getSource(),
686: e.getPoint(), list);
687: MouseEvent newEvent = new MouseEvent((Component) e.getSource(),
688: e.getID(), e.getWhen(),
689: e.getModifiers(), point.x, point.y,
690: e.getModifiers(),
691: e.isPopupTrigger());
692: return newEvent;
693: }
694:
695:
706: protected int getPopupHeightForRowCount(int maxRowCount)
707: {
708: int totalHeight = 0;
709: ListCellRenderer rend = list.getCellRenderer();
710:
711: if (comboBox.getItemCount() < maxRowCount)
712: maxRowCount = comboBox.getItemCount();
713:
714: for (int i = 0; i < maxRowCount; i++)
715: {
716: Component comp = rend.getListCellRendererComponent(list,
717: comboBox.getModel()
718: .getElementAt(i),
719: -1, false, false);
720: Dimension dim = comp.getPreferredSize();
721: totalHeight += dim.height;
722: }
723:
724: return totalHeight == 0 ? 100 : totalHeight;
725: }
726:
727:
737: protected Rectangle computePopupBounds(int px, int py, int pw, int ph)
738: {
739: return new Rectangle(px, py, pw, ph);
740: }
741:
742:
749: protected void updateListBoxSelectionForEvent(MouseEvent anEvent,
750: boolean shouldScroll)
751: {
752: Point point = anEvent.getPoint();
753: if (list != null)
754: {
755: int index = list.locationToIndex(point);
756: if (index == -1)
757: {
758: if (point.y < 0)
759: index = 0;
760: else
761: index = comboBox.getModel().getSize() - 1;
762: }
763: if (list.getSelectedIndex() != index)
764: {
765: list.setSelectedIndex(index);
766: if (shouldScroll)
767: list.ensureIndexIsVisible(index);
768: }
769: }
770: }
771:
772:
780: protected class InvocationMouseHandler extends MouseAdapter
781: {
782:
785: protected InvocationMouseHandler()
786: {
787:
788: }
789:
790:
797: public void mousePressed(MouseEvent e)
798: {
799: if (SwingUtilities.isLeftMouseButton(e) && comboBox.isEnabled())
800: {
801: delegateFocus(e);
802: togglePopup();
803: }
804: }
805:
806:
813: public void mouseReleased(MouseEvent e)
814: {
815: Component component = (Component) e.getSource();
816: Dimension size = component.getSize();
817: Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1);
818:
819:
820:
821: if (! bounds.contains(e.getPoint()))
822: {
823: MouseEvent convEvent = convertMouseEvent(e);
824: Point point = convEvent.getPoint();
825: Rectangle visRect = new Rectangle();
826: list.computeVisibleRect(visRect);
827: if (visRect.contains(point))
828: {
829: updateListBoxSelectionForEvent(convEvent, false);
830: comboBox.setSelectedIndex(list.getSelectedIndex());
831: }
832: hide();
833: }
834: hasEntered = false;
835: stopAutoScrolling();
836: }
837: }
838:
839:
843: protected class InvocationMouseMotionHandler extends MouseMotionAdapter
844: {
845:
848: protected InvocationMouseMotionHandler()
849: {
850:
851: }
852:
853:
857: public void mouseDragged(MouseEvent e)
858: {
859: if (isVisible())
860: {
861: MouseEvent convEvent = convertMouseEvent(e);
862: Rectangle visRect = new Rectangle();
863: list.computeVisibleRect(visRect);
864: if (convEvent.getPoint().y >= visRect.y
865: && (convEvent.getPoint().y <= visRect.y + visRect.height - 1))
866: {
867: hasEntered = true;
868: if (isAutoScrolling)
869: stopAutoScrolling();
870: Point point = convEvent.getPoint();
871: if (visRect.contains(point))
872: {
873: valueIsAdjusting = true;
874: updateListBoxSelectionForEvent(convEvent, false);
875: valueIsAdjusting = false;
876: }
877: }
878: else if (hasEntered)
879: {
880: int dir = convEvent.getPoint().y < visRect.y ? SCROLL_UP
881: : SCROLL_DOWN;
882: if (isAutoScrolling && scrollDirection != dir)
883: {
884: stopAutoScrolling();
885: startAutoScrolling(dir);
886: }
887: else if (!isAutoScrolling)
888: startAutoScrolling(dir);
889: }
890: else if (e.getPoint().y < 0)
891: {
892: hasEntered = true;
893: startAutoScrolling(SCROLL_UP);
894: }
895: }
896: }
897: }
898:
899:
904: protected class ItemHandler extends Object implements ItemListener
905: {
906:
909: protected ItemHandler()
910: {
911:
912: }
913:
914:
919: public void itemStateChanged(ItemEvent e)
920: {
921: if (e.getStateChange() == ItemEvent.SELECTED && ! valueIsAdjusting)
922: {
923: valueIsAdjusting = true;
924: syncListSelection();
925: valueIsAdjusting = false;
926: list.ensureIndexIsVisible(comboBox.getSelectedIndex());
927: }
928: }
929: }
930:
931:
937: protected class ListMouseHandler extends MouseAdapter
938: {
939: protected ListMouseHandler()
940: {
941:
942: }
943:
944: public void mousePressed(MouseEvent e)
945: {
946:
947: }
948:
949: public void mouseReleased(MouseEvent anEvent)
950: {
951: comboBox.setSelectedIndex(list.getSelectedIndex());
952: hide();
953: }
954: }
955:
956:
961: protected class ListMouseMotionHandler extends MouseMotionAdapter
962: {
963: protected ListMouseMotionHandler()
964: {
965:
966: }
967:
968: public void mouseMoved(MouseEvent anEvent)
969: {
970: Point point = anEvent.getPoint();
971: Rectangle visRect = new Rectangle();
972: list.computeVisibleRect(visRect);
973: if (visRect.contains(point))
974: {
975: valueIsAdjusting = true;
976: updateListBoxSelectionForEvent(anEvent, false);
977: valueIsAdjusting = false;
978: }
979: }
980: }
981:
982:
986: protected class PropertyChangeHandler extends Object
987: implements PropertyChangeListener
988: {
989: protected PropertyChangeHandler()
990: {
991:
992: }
993:
994: public void propertyChange(PropertyChangeEvent e)
995: {
996: if (e.getPropertyName().equals("renderer"))
997: {
998: list.setCellRenderer(comboBox.getRenderer());
999: if (isVisible())
1000: hide();
1001: }
1002: if (e.getPropertyName().equals("model"))
1003: {
1004: ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
1005: uninstallComboBoxModelListeners(oldModel);
1006: ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
1007: list.setModel(newModel);
1008: installComboBoxModelListeners(newModel);
1009: if (comboBox.getItemCount() > 0)
1010: comboBox.setSelectedIndex(0);
1011: if (isVisible())
1012: hide();
1013: }
1014: }
1015: }
1016:
1017:
1018:
1019:
1023: private void uninstallListListeners()
1024: {
1025: list.removeMouseListener(listMouseListener);
1026: listMouseListener = null;
1027:
1028: list.removeMouseMotionListener(listMouseMotionListener);
1029: listMouseMotionListener = null;
1030: }
1031:
1032: void syncListSelection()
1033: {
1034: int index = comboBox.getSelectedIndex();
1035: if (index == -1)
1036: list.clearSelection();
1037: else
1038: list.setSelectedIndex(index);
1039: }
1040:
1041:
1042:
1043:
1044:
1045:
1046:
1049: public class ListDataHandler extends Object implements ListDataListener
1050: {
1051: public ListDataHandler()
1052: {
1053:
1054: }
1055:
1056: public void contentsChanged(ListDataEvent e)
1057: {
1058:
1059: }
1060:
1061: public void intervalAdded(ListDataEvent e)
1062: {
1063:
1064: }
1065:
1066: public void intervalRemoved(ListDataEvent e)
1067: {
1068:
1069: }
1070: }
1071:
1072:
1075: protected class ListSelectionHandler extends Object
1076: implements ListSelectionListener
1077: {
1078: protected ListSelectionHandler()
1079: {
1080:
1081: }
1082:
1083: public void valueChanged(ListSelectionEvent e)
1084: {
1085:
1086: }
1087: }
1088:
1089:
1092: public class InvocationKeyHandler extends KeyAdapter
1093: {
1094: public InvocationKeyHandler()
1095: {
1096:
1097: }
1098:
1099: public void keyReleased(KeyEvent e)
1100: {
1101:
1102: }
1103: }
1104: }