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:
59: import ;
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:
76:
80: public class BasicListUI extends ListUI
81: {
82:
83:
87: private class ComponentHandler extends ComponentAdapter {
88:
89:
93: public void componentResized(ComponentEvent ev) {
94: BasicListUI.this.damageLayout();
95: }
96: }
97:
98:
102: public class FocusHandler implements FocusListener
103: {
104:
109: public void focusGained(FocusEvent e)
110: {
111: repaintCellFocus();
112: }
113:
114:
119: public void focusLost(FocusEvent e)
120: {
121: repaintCellFocus();
122: }
123:
124:
128: void repaintCellFocus()
129: {
130: }
131: }
132:
133:
139: public class ListDataHandler implements ListDataListener
140: {
141:
147: public void contentsChanged(ListDataEvent e)
148: {
149: BasicListUI.this.damageLayout();
150: }
151:
152:
157: public void intervalAdded(ListDataEvent e)
158: {
159: BasicListUI.this.damageLayout();
160: }
161:
162:
167: public void intervalRemoved(ListDataEvent e)
168: {
169: BasicListUI.this.damageLayout();
170: }
171: }
172:
173:
177: public class ListSelectionHandler implements ListSelectionListener
178: {
179:
184: public void valueChanged(ListSelectionEvent e)
185: {
186: }
187: }
188:
189:
193: private class KeyHandler extends KeyAdapter
194: {
195: public KeyHandler()
196: {
197: }
198:
199: public void keyPressed( KeyEvent evt )
200: {
201: int lead = BasicListUI.this.list.getLeadSelectionIndex();
202: int max = BasicListUI.this.list.getModel().getSize() - 1;
203:
204: if (max == -1)
205: return;
206:
207:
208:
209: if ((evt.getKeyCode() == KeyEvent.VK_DOWN)
210: || (evt.getKeyCode() == KeyEvent.VK_KP_DOWN))
211: {
212: if (evt.getModifiers() == 0)
213: {
214: BasicListUI.this.list.clearSelection();
215: BasicListUI.this.list.setSelectedIndex(Math.min(lead+1,max));
216: }
217: else if (evt.getModifiers() == InputEvent.SHIFT_MASK)
218: {
219: BasicListUI.this.list.getSelectionModel().
220: setLeadSelectionIndex(Math.min(lead+1,max));
221: }
222: }
223: else if ((evt.getKeyCode() == KeyEvent.VK_UP)
224: || (evt.getKeyCode() == KeyEvent.VK_KP_UP))
225: {
226: if (evt.getModifiers() == 0)
227: {
228: BasicListUI.this.list.clearSelection();
229: BasicListUI.this.list.setSelectedIndex(Math.max(lead-1,0));
230: }
231: else if (evt.getModifiers() == InputEvent.SHIFT_MASK)
232: {
233: BasicListUI.this.list.getSelectionModel().
234: setLeadSelectionIndex(Math.max(lead-1,0));
235: }
236: }
237: else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP)
238: {
239: int target;
240: if (lead == BasicListUI.this.list.getFirstVisibleIndex())
241: {
242: target = Math.max
243: (0, lead - (BasicListUI.this.list.getLastVisibleIndex() -
244: BasicListUI.this.list.getFirstVisibleIndex() + 1));
245: }
246: else
247: {
248: target = BasicListUI.this.list.getFirstVisibleIndex();
249: }
250: if (evt.getModifiers() == 0)
251: BasicListUI.this.list.setSelectedIndex(target);
252: else if (evt.getModifiers() == InputEvent.SHIFT_MASK)
253: BasicListUI.this.list.getSelectionModel().
254: setLeadSelectionIndex(target);
255: }
256: else if (evt.getKeyCode() == KeyEvent.VK_PAGE_DOWN)
257: {
258: int target;
259: if (lead == BasicListUI.this.list.getLastVisibleIndex())
260: {
261: target = Math.min
262: (max, lead + (BasicListUI.this.list.getLastVisibleIndex() -
263: BasicListUI.this.list.getFirstVisibleIndex() + 1));
264: }
265: else
266: {
267: target = BasicListUI.this.list.getLastVisibleIndex();
268: }
269: if (evt.getModifiers() == 0)
270: BasicListUI.this.list.setSelectedIndex(target);
271: else if (evt.getModifiers() == InputEvent.SHIFT_MASK)
272: BasicListUI.this.list.getSelectionModel().
273: setLeadSelectionIndex(target);
274: }
275: else if (evt.getKeyCode() == KeyEvent.VK_BACK_SLASH
276: && (evt.getModifiers() == InputEvent.CTRL_MASK))
277: {
278: BasicListUI.this.list.clearSelection();
279: }
280: else if ((evt.getKeyCode() == KeyEvent.VK_HOME)
281: || evt.getKeyCode() == KeyEvent.VK_END)
282: {
283: if (evt.getModifiers() != 0 &&
284: evt.getModifiers() != InputEvent.SHIFT_MASK)
285: return;
286:
287: int index = (evt.getKeyCode() == KeyEvent.VK_HOME) ? 0 : max;
288:
289: if (!evt.isShiftDown() ||(BasicListUI.this.list.getSelectionMode()
290: == ListSelectionModel.SINGLE_SELECTION))
291: BasicListUI.this.list.setSelectedIndex(index);
292: else if (BasicListUI.this.list.getSelectionMode() ==
293: ListSelectionModel.SINGLE_INTERVAL_SELECTION)
294: BasicListUI.this.list.setSelectionInterval
295: (BasicListUI.this.list.getAnchorSelectionIndex(), index);
296: else
297: BasicListUI.this.list.getSelectionModel().
298: setLeadSelectionIndex(index);
299: }
300: else if ((evt.getKeyCode() == KeyEvent.VK_A || evt.getKeyCode()
301: == KeyEvent.VK_SLASH) && (evt.getModifiers() ==
302: InputEvent.CTRL_MASK))
303: {
304: BasicListUI.this.list.setSelectionInterval(0, max);
305:
306:
307: BasicListUI.this.list.addSelectionInterval(lead, lead);
308: }
309: else if (evt.getKeyCode() == KeyEvent.VK_SPACE &&
310: (evt.getModifiers() == InputEvent.CTRL_MASK))
311: {
312: BasicListUI.this.list.getSelectionModel().
313: setLeadSelectionIndex(Math.min(lead+1,max));
314: }
315:
316: BasicListUI.this.list.ensureIndexIsVisible
317: (BasicListUI.this.list.getLeadSelectionIndex());
318: }
319: }
320:
321:
325: public class MouseInputHandler implements MouseInputListener
326: {
327:
333: public void mouseClicked(MouseEvent event)
334: {
335: Point click = event.getPoint();
336: int index = BasicListUI.this.locationToIndex(list, click);
337: if (index == -1)
338: return;
339: if (event.isShiftDown())
340: {
341: if (BasicListUI.this.list.getSelectionMode() ==
342: ListSelectionModel.SINGLE_SELECTION)
343: BasicListUI.this.list.setSelectedIndex(index);
344: else if (BasicListUI.this.list.getSelectionMode() ==
345: ListSelectionModel.SINGLE_INTERVAL_SELECTION)
346:
347:
348:
349:
350:
351: BasicListUI.this.list.setSelectionInterval
352: (BasicListUI.this.list.getAnchorSelectionIndex(), index);
353: else
354:
355:
356:
357:
358:
359:
360: BasicListUI.this.list.getSelectionModel().
361: setLeadSelectionIndex(index);
362: }
363: else if (event.isControlDown())
364: {
365: if (BasicListUI.this.list.getSelectionMode() ==
366: ListSelectionModel.SINGLE_SELECTION)
367: BasicListUI.this.list.setSelectedIndex(index);
368: else if (BasicListUI.this.list.isSelectedIndex(index))
369: BasicListUI.this.list.removeSelectionInterval(index,index);
370: else
371: BasicListUI.this.list.addSelectionInterval(index,index);
372: }
373: else
374: BasicListUI.this.list.setSelectedIndex(index);
375:
376: BasicListUI.this.list.ensureIndexIsVisible
377: (BasicListUI.this.list.getLeadSelectionIndex());
378: }
379:
380:
386: public void mousePressed(MouseEvent event)
387: {
388: }
389:
390:
396: public void mouseReleased(MouseEvent event)
397: {
398: }
399:
400:
406: public void mouseEntered(MouseEvent event)
407: {
408: }
409:
410:
416: public void mouseExited(MouseEvent event)
417: {
418: }
419:
420:
426: public void mouseDragged(MouseEvent event)
427: {
428: }
429:
430:
436: public void mouseMoved(MouseEvent event)
437: {
438: }
439: }
440:
441:
445: public class PropertyChangeHandler implements PropertyChangeListener
446: {
447:
452: public void propertyChange(PropertyChangeEvent e)
453: {
454: if (e.getSource() == BasicListUI.this.list)
455: {
456: if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
457: ((ListModel) e.getOldValue()).removeListDataListener(BasicListUI.this.listDataListener);
458:
459: if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
460: ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener);
461: }
462: BasicListUI.this.damageLayout();
463: }
464: }
465:
466:
473: public static ComponentUI createUI(final JComponent c)
474: {
475: return new BasicListUI();
476: }
477:
478:
479: protected FocusListener focusListener;
480:
481:
482: protected ListDataListener listDataListener;
483:
484:
485: protected ListSelectionListener listSelectionListener;
486:
487:
488: protected MouseInputListener mouseInputListener;
489:
490:
491: private KeyHandler keyListener;
492:
493:
494: protected PropertyChangeListener propertyChangeListener;
495:
496:
497:
499: private ComponentListener componentListener;
500:
501:
502: protected JList list;
503:
504:
505: protected int cellHeight;
506:
507:
508: protected int cellWidth;
509:
510:
514: protected int[] cellHeights;
515:
516:
521: protected int updateLayoutStateNeeded;
522:
523:
526: protected CellRendererPane rendererPane;
527:
528:
538: protected int getRowHeight(int row)
539: {
540: if (row < 0 || row >= cellHeights.length)
541: return -1;
542: else if (cellHeight != -1)
543: return cellHeight;
544: else
545: return cellHeights[row];
546: }
547:
548:
559: public Rectangle getCellBounds(JList l, int index1, int index2)
560: {
561: maybeUpdateLayoutState();
562:
563: if (l != list || cellWidth == -1)
564: return null;
565:
566: int minIndex = Math.min(index1, index2);
567: int maxIndex = Math.max(index1, index2);
568: Point loc = indexToLocation(list, minIndex);
569: Rectangle bounds = new Rectangle(loc.x, loc.y, cellWidth,
570: getRowHeight(minIndex));
571:
572: for (int i = minIndex + 1; i <= maxIndex; i++)
573: {
574: Point hiLoc = indexToLocation(list, i);
575: Rectangle hibounds = new Rectangle(hiLoc.x, hiLoc.y, cellWidth,
576: getRowHeight(i));
577: bounds = bounds.union(hibounds);
578: }
579:
580: return bounds;
581: }
582:
583:
593: protected int convertRowToY(int row)
594: {
595: int y = 0;
596: for (int i = 0; i < row; ++i)
597: {
598: int h = getRowHeight(i);
599: if (h == -1)
600: return -1;
601: y += h;
602: }
603: return y;
604: }
605:
606:
616: protected int convertYToRow(int y0)
617: {
618: for (int row = 0; row < cellHeights.length; ++row)
619: {
620: int h = getRowHeight(row);
621:
622: if (y0 < h)
623: return row;
624: y0 -= h;
625: }
626: return -1;
627: }
628:
629:
634: protected void updateLayoutState()
635: {
636: int nrows = list.getModel().getSize();
637: cellHeight = -1;
638: cellWidth = -1;
639: if (cellHeights == null || cellHeights.length != nrows)
640: cellHeights = new int[nrows];
641: if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1)
642: {
643: ListCellRenderer rend = list.getCellRenderer();
644: for (int i = 0; i < nrows; ++i)
645: {
646: Component flyweight = rend.getListCellRendererComponent(list,
647: list.getModel()
648: .getElementAt(i),
649: 0, false,
650: false);
651: Dimension dim = flyweight.getPreferredSize();
652: cellHeights[i] = dim.height;
653:
654: cellHeight = (cellHeight * i + cellHeights[i]) / (i + 1);
655: cellWidth = Math.max(cellWidth, dim.width);
656: if (list.getLayoutOrientation() == JList.VERTICAL)
657: cellWidth = Math.max(cellWidth, list.getSize().width);
658: }
659: }
660: else
661: {
662: cellHeight = list.getFixedCellHeight();
663: cellWidth = list.getFixedCellWidth();
664: }
665: }
666:
667:
674: void damageLayout()
675: {
676: updateLayoutStateNeeded = 1;
677: }
678:
679:
683: protected void maybeUpdateLayoutState()
684: {
685: if (updateLayoutStateNeeded != 0)
686: {
687: updateLayoutState();
688: updateLayoutStateNeeded = 0;
689: }
690: }
691:
692:
695: public BasicListUI()
696: {
697: focusListener = new FocusHandler();
698: listDataListener = new ListDataHandler();
699: listSelectionListener = new ListSelectionHandler();
700: mouseInputListener = new MouseInputHandler();
701: keyListener = new KeyHandler();
702: propertyChangeListener = new PropertyChangeHandler();
703: componentListener = new ComponentHandler();
704: updateLayoutStateNeeded = 1;
705: rendererPane = new CellRendererPane();
706: }
707:
708:
714: protected void installDefaults()
715: {
716: UIDefaults defaults = UIManager.getLookAndFeelDefaults();
717: list.setForeground(defaults.getColor("List.foreground"));
718: list.setBackground(defaults.getColor("List.background"));
719: list.setSelectionForeground(defaults.getColor("List.selectionForeground"));
720: list.setSelectionBackground(defaults.getColor("List.selectionBackground"));
721: list.setOpaque(true);
722: }
723:
724:
728: protected void uninstallDefaults()
729: {
730: UIDefaults defaults = UIManager.getLookAndFeelDefaults();
731: list.setForeground(null);
732: list.setBackground(null);
733: list.setSelectionForeground(null);
734: list.setSelectionBackground(null);
735: }
736:
737:
743: protected void installListeners()
744: {
745: list.addFocusListener(focusListener);
746: list.getModel().addListDataListener(listDataListener);
747: list.addListSelectionListener(listSelectionListener);
748: list.addMouseListener(mouseInputListener);
749: list.addKeyListener(keyListener);
750: list.addMouseMotionListener(mouseInputListener);
751: list.addPropertyChangeListener(propertyChangeListener);
752: list.addComponentListener(componentListener);
753: }
754:
755:
758: protected void uninstallListeners()
759: {
760: list.removeFocusListener(focusListener);
761: list.getModel().removeListDataListener(listDataListener);
762: list.removeListSelectionListener(listSelectionListener);
763: list.removeMouseListener(mouseInputListener);
764: list.removeKeyListener(keyListener);
765: list.removeMouseMotionListener(mouseInputListener);
766: list.removePropertyChangeListener(propertyChangeListener);
767: }
768:
769:
772: protected void installKeyboardActions()
773: {
774: }
775:
776:
779: protected void uninstallKeyboardActions()
780: {
781: }
782:
783:
791: public void installUI(final JComponent c)
792: {
793: super.installUI(c);
794: list = (JList) c;
795: installDefaults();
796: installListeners();
797: installKeyboardActions();
798: maybeUpdateLayoutState();
799: }
800:
801:
809: public void uninstallUI(final JComponent c)
810: {
811: uninstallKeyboardActions();
812: uninstallListeners();
813: uninstallDefaults();
814: list = null;
815: }
816:
817:
825: public Dimension getPreferredSize(JComponent c)
826: {
827: int size = list.getModel().getSize();
828: if (size == 0)
829: return new Dimension(0, 0);
830: int visibleRows = list.getVisibleRowCount();
831: int layoutOrientation = list.getLayoutOrientation();
832: Rectangle bounds = getCellBounds(list, 0, list.getModel().getSize() - 1);
833: Dimension retVal = bounds.getSize();
834: Component parent = list.getParent();
835: if ((visibleRows == -1) && (parent instanceof JViewport))
836: {
837: JViewport viewport = (JViewport) parent;
838:
839: if (layoutOrientation == JList.HORIZONTAL_WRAP)
840: {
841: int h = viewport.getSize().height;
842: int cellsPerCol = h / cellHeight;
843: int w = size / cellsPerCol * cellWidth;
844: retVal = new Dimension(w, h);
845: }
846: else if (layoutOrientation == JList.VERTICAL_WRAP)
847: {
848: int w = viewport.getSize().width;
849: int cellsPerRow = Math.max(w / cellWidth, 1);
850: int h = size / cellsPerRow * cellHeight;
851: retVal = new Dimension(w, h);
852: }
853: }
854: return retVal;
855: }
856:
857:
864: private void paintBackground(Graphics g, JComponent c)
865: {
866: Dimension size = getPreferredSize(c);
867: Color save = g.getColor();
868: g.setColor(c.getBackground());
869: g.fillRect(0, 0, size.width, size.height);
870: g.setColor(save);
871: }
872:
873:
886: protected void paintCell(Graphics g, int row, Rectangle bounds,
887: ListCellRenderer rend, ListModel data,
888: ListSelectionModel sel, int lead)
889: {
890: boolean isSel = list.isSelectedIndex(row);
891: boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus();
892: Component comp = rend.getListCellRendererComponent(list,
893: data.getElementAt(row),
894: 0, isSel, hasFocus);
895:
896:
897: rendererPane.paintComponent(g, comp, list, bounds);
898: }
899:
900:
907: public void paint(Graphics g, JComponent c)
908: {
909: int nrows = list.getModel().getSize();
910: if (nrows == 0)
911: return;
912:
913: maybeUpdateLayoutState();
914: ListCellRenderer render = list.getCellRenderer();
915: ListModel model = list.getModel();
916: ListSelectionModel sel = list.getSelectionModel();
917: int lead = sel.getLeadSelectionIndex();
918: Rectangle clip = g.getClipBounds();
919: paintBackground(g, list);
920:
921: for (int row = 0; row < nrows; ++row)
922: {
923: Rectangle bounds = getCellBounds(list, row, row);
924: if (bounds.intersects(clip))
925: paintCell(g, row, bounds, render, model, sel, lead);
926: }
927: }
928:
929:
938: public int locationToIndex(JList list, Point location)
939: {
940: int layoutOrientation = list.getLayoutOrientation();
941: int index = -1;
942: switch (layoutOrientation)
943: {
944: case JList.VERTICAL:
945: index = convertYToRow(location.y);
946: break;
947: case JList.HORIZONTAL_WRAP:
948:
949: int visibleRows = list.getVisibleRowCount();
950: int cellsPerRow = -1;
951: int numberOfItems = list.getModel().getSize();
952: Dimension listDim = list.getSize();
953: if (visibleRows <= 0)
954: {
955: try
956: {
957: cellsPerRow = listDim.width / cellWidth;
958: }
959: catch (ArithmeticException ex)
960: {
961: cellsPerRow = 1;
962: }
963: }
964: else
965: {
966: cellsPerRow = numberOfItems / visibleRows + 1;
967: }
968:
969:
970: int cellsPerColumn = numberOfItems / cellsPerRow + 1;
971: int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1);
972: int gridY = Math.min(location.y / cellHeight, cellsPerColumn);
973: index = gridX + gridY * cellsPerRow;
974: break;
975: case JList.VERTICAL_WRAP:
976:
977: int visibleRows2 = list.getVisibleRowCount();
978: if (visibleRows2 <= 0)
979: {
980: Dimension listDim2 = list.getSize();
981: visibleRows2 = listDim2.height / cellHeight;
982: }
983: int numberOfItems2 = list.getModel().getSize();
984: int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1;
985:
986: Dimension listDim2 = list.getSize();
987: int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1);
988: int gridY2 = Math.min(location.y / cellHeight, visibleRows2);
989: index = gridY2 + gridX2 * visibleRows2;
990: break;
991: }
992: return index;
993: }
994:
995: public Point indexToLocation(JList list, int index)
996: {
997: int layoutOrientation = list.getLayoutOrientation();
998: Point loc = null;
999: switch (layoutOrientation)
1000: {
1001: case JList.VERTICAL:
1002: loc = new Point(0, convertRowToY(index));
1003: break;
1004: case JList.HORIZONTAL_WRAP:
1005:
1006: int visibleRows = list.getVisibleRowCount();
1007: int numberOfCellsPerRow = -1;
1008: if (visibleRows <= 0)
1009: {
1010: Dimension listDim = list.getSize();
1011: numberOfCellsPerRow = Math.max(listDim.width / cellWidth, 1);
1012: }
1013: else
1014: {
1015: int numberOfItems = list.getModel().getSize();
1016: numberOfCellsPerRow = numberOfItems / visibleRows + 1;
1017: }
1018:
1019: int gridX = index % numberOfCellsPerRow;
1020: int gridY = index / numberOfCellsPerRow;
1021: int locX = gridX * cellWidth;
1022: int locY = gridY * cellHeight;
1023: loc = new Point(locX, locY);
1024: break;
1025: case JList.VERTICAL_WRAP:
1026:
1027: int visibleRows2 = list.getVisibleRowCount();
1028: if (visibleRows2 <= 0)
1029: {
1030: Dimension listDim2 = list.getSize();
1031: visibleRows2 = listDim2.height / cellHeight;
1032: }
1033:
1034: if (visibleRows2 > 0)
1035: {
1036: int gridY2 = index % visibleRows2;
1037: int gridX2 = index / visibleRows2;
1038: int locX2 = gridX2 * cellWidth;
1039: int locY2 = gridY2 * cellHeight;
1040: loc = new Point(locX2, locY2);
1041: }
1042: else
1043: loc = new Point(0, convertRowToY(index));
1044: break;
1045: }
1046: return loc;
1047: }
1048: }