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:
58: import ;
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:
75:
79: public class BasicListUI extends ListUI
80: {
81:
82:
86: private class ComponentHandler extends ComponentAdapter {
87:
88:
92: public void componentResized(ComponentEvent ev) {
93: BasicListUI.this.damageLayout();
94: }
95: }
96:
97:
101: public class FocusHandler implements FocusListener
102: {
103:
108: public void focusGained(FocusEvent e)
109: {
110: repaintCellFocus();
111: }
112:
113:
118: public void focusLost(FocusEvent e)
119: {
120: repaintCellFocus();
121: }
122:
123:
127: void repaintCellFocus()
128: {
129: }
130: }
131:
132:
138: public class ListDataHandler implements ListDataListener
139: {
140:
146: public void contentsChanged(ListDataEvent e)
147: {
148: BasicListUI.this.damageLayout();
149: }
150:
151:
156: public void intervalAdded(ListDataEvent e)
157: {
158: BasicListUI.this.damageLayout();
159: }
160:
161:
166: public void intervalRemoved(ListDataEvent e)
167: {
168: BasicListUI.this.damageLayout();
169: }
170: }
171:
172:
176: public class ListSelectionHandler implements ListSelectionListener
177: {
178:
183: public void valueChanged(ListSelectionEvent e)
184: {
185: }
186: }
187:
188:
192: private class KeyHandler extends KeyAdapter
193: {
194: public KeyHandler()
195: {
196: }
197:
198: public void keyPressed( KeyEvent evt )
199: {
200: int lead = BasicListUI.this.list.getLeadSelectionIndex();
201: int max = BasicListUI.this.list.getModel().getSize() - 1;
202:
203: if (max == -1)
204: return;
205:
206:
207:
208: if ((evt.getKeyCode() == KeyEvent.VK_DOWN)
209: || (evt.getKeyCode() == KeyEvent.VK_KP_DOWN))
210: {
211: if (!evt.isShiftDown())
212: {
213: BasicListUI.this.list.clearSelection();
214: BasicListUI.this.list.setSelectedIndex(Math.min(lead+1,max));
215: }
216: else
217: {
218: BasicListUI.this.list.getSelectionModel().
219: setLeadSelectionIndex(Math.min(lead+1,max));
220: }
221: }
222: else if ((evt.getKeyCode() == KeyEvent.VK_UP)
223: || (evt.getKeyCode() == KeyEvent.VK_KP_UP))
224: {
225: if (!evt.isShiftDown())
226: {
227: BasicListUI.this.list.clearSelection();
228: BasicListUI.this.list.setSelectedIndex(Math.max(lead-1,0));
229: }
230: else
231: {
232: BasicListUI.this.list.getSelectionModel().
233: setLeadSelectionIndex(Math.max(lead-1,0));
234: }
235: }
236: else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP)
237: {
238:
239: }
240: else if (evt.getKeyCode() == KeyEvent.VK_PAGE_DOWN)
241: {
242:
243: }
244: else if (evt.getKeyCode() == KeyEvent.VK_BACK_SLASH
245: && evt.isControlDown())
246: {
247: BasicListUI.this.list.clearSelection();
248: }
249: else if ((evt.getKeyCode() == KeyEvent.VK_HOME)
250: || evt.getKeyCode() == KeyEvent.VK_END)
251: {
252:
253: int index = (evt.getKeyCode() == KeyEvent.VK_HOME) ? 0 : max;
254:
255: if (!evt.isShiftDown() ||(BasicListUI.this.list.getSelectionMode()
256: == ListSelectionModel.SINGLE_SELECTION))
257: BasicListUI.this.list.setSelectedIndex(index);
258: else if (BasicListUI.this.list.getSelectionMode() ==
259: ListSelectionModel.SINGLE_INTERVAL_SELECTION)
260: BasicListUI.this.list.setSelectionInterval
261: (BasicListUI.this.list.getAnchorSelectionIndex(), index);
262: else
263: BasicListUI.this.list.getSelectionModel().
264: setLeadSelectionIndex(index);
265: }
266: else if ((evt.getKeyCode() == KeyEvent.VK_A || evt.getKeyCode()
267: == KeyEvent.VK_SLASH) && evt.isControlDown())
268: {
269: BasicListUI.this.list.setSelectionInterval(0, max);
270: }
271: else if (evt.getKeyCode() == KeyEvent.VK_SPACE && evt.isControlDown())
272: {
273: BasicListUI.this.list.getSelectionModel().
274: setLeadSelectionIndex(Math.min(lead+1,max));
275: }
276:
277: }
278: }
279:
280:
284: public class MouseInputHandler implements MouseInputListener
285: {
286:
292: public void mouseClicked(MouseEvent event)
293: {
294: Point click = event.getPoint();
295: int index = BasicListUI.this.locationToIndex(list, click);
296: if (index == -1)
297: return;
298: if (event.isControlDown())
299: {
300: if (BasicListUI.this.list.getSelectionMode() ==
301: ListSelectionModel.SINGLE_SELECTION)
302: BasicListUI.this.list.setSelectedIndex(index);
303: else if (BasicListUI.this.list.isSelectedIndex(index))
304: BasicListUI.this.list.removeSelectionInterval(index,index);
305: else
306: BasicListUI.this.list.addSelectionInterval(index,index);
307: }
308: else if (event.isShiftDown())
309: {
310: if (BasicListUI.this.list.getSelectionMode() ==
311: ListSelectionModel.SINGLE_SELECTION)
312: BasicListUI.this.list.setSelectedIndex(index);
313: else if (BasicListUI.this.list.getSelectionMode() ==
314: ListSelectionModel.SINGLE_INTERVAL_SELECTION)
315:
316:
317:
318:
319:
320: BasicListUI.this.list.setSelectionInterval
321: (BasicListUI.this.list.getAnchorSelectionIndex(), index);
322: else
323:
324:
325:
326:
327:
328:
329: BasicListUI.this.list.getSelectionModel().
330: setLeadSelectionIndex(index);
331: }
332: else
333: BasicListUI.this.list.setSelectedIndex(index);
334: }
335:
336:
342: public void mousePressed(MouseEvent event)
343: {
344: }
345:
346:
352: public void mouseReleased(MouseEvent event)
353: {
354: }
355:
356:
362: public void mouseEntered(MouseEvent event)
363: {
364: }
365:
366:
372: public void mouseExited(MouseEvent event)
373: {
374: }
375:
376:
382: public void mouseDragged(MouseEvent event)
383: {
384: }
385:
386:
392: public void mouseMoved(MouseEvent event)
393: {
394: }
395: }
396:
397:
401: public class PropertyChangeHandler implements PropertyChangeListener
402: {
403:
408: public void propertyChange(PropertyChangeEvent e)
409: {
410: if (e.getSource() == BasicListUI.this.list)
411: {
412: if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
413: ((ListModel) e.getOldValue()).removeListDataListener(BasicListUI.this.listDataListener);
414:
415: if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
416: ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener);
417: }
418: BasicListUI.this.damageLayout();
419: }
420: }
421:
422:
429: public static ComponentUI createUI(final JComponent c)
430: {
431: return new BasicListUI();
432: }
433:
434:
435: protected FocusListener focusListener;
436:
437:
438: protected ListDataListener listDataListener;
439:
440:
441: protected ListSelectionListener listSelectionListener;
442:
443:
444: protected MouseInputListener mouseInputListener;
445:
446:
447: private KeyHandler keyListener;
448:
449:
450: protected PropertyChangeListener propertyChangeListener;
451:
452:
453:
455: private ComponentListener componentListener;
456:
457:
458: protected JList list;
459:
460:
461: protected int cellHeight;
462:
463:
464: protected int cellWidth;
465:
466:
470: protected int[] cellHeights;
471:
472:
477: protected int updateLayoutStateNeeded;
478:
479:
482: protected CellRendererPane rendererPane;
483:
484:
494: protected int getRowHeight(int row)
495: {
496: if (row < 0 || row >= cellHeights.length)
497: return -1;
498: else if (cellHeight != -1)
499: return cellHeight;
500: else
501: return cellHeights[row];
502: }
503:
504:
515: public Rectangle getCellBounds(JList l, int index1, int index2)
516: {
517: maybeUpdateLayoutState();
518:
519: if (l != list || cellWidth == -1)
520: return null;
521:
522: int minIndex = Math.min(index1, index2);
523: int maxIndex = Math.max(index1, index2);
524: Point loc = indexToLocation(list, minIndex);
525: Rectangle bounds = new Rectangle(loc.x, loc.y, cellWidth,
526: getRowHeight(minIndex));
527:
528: for (int i = minIndex + 1; i <= maxIndex; i++)
529: {
530: Point hiLoc = indexToLocation(list, i);
531: Rectangle hibounds = new Rectangle(hiLoc.x, hiLoc.y, cellWidth,
532: getRowHeight(i));
533: bounds = bounds.union(hibounds);
534: }
535:
536: return bounds;
537: }
538:
539:
549: protected int convertRowToY(int row)
550: {
551: int y = 0;
552: for (int i = 0; i < row; ++i)
553: {
554: int h = getRowHeight(i);
555: if (h == -1)
556: return -1;
557: y += h;
558: }
559: return y;
560: }
561:
562:
572: protected int convertYToRow(int y0)
573: {
574: for (int row = 0; row < cellHeights.length; ++row)
575: {
576: int h = getRowHeight(row);
577:
578: if (y0 < h)
579: return row;
580: y0 -= h;
581: }
582: return -1;
583: }
584:
585:
590: protected void updateLayoutState()
591: {
592: int nrows = list.getModel().getSize();
593: cellHeight = -1;
594: cellWidth = -1;
595: if (cellHeights == null || cellHeights.length != nrows)
596: cellHeights = new int[nrows];
597: if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1)
598: {
599: ListCellRenderer rend = list.getCellRenderer();
600: for (int i = 0; i < nrows; ++i)
601: {
602: Component flyweight = rend.getListCellRendererComponent(list,
603: list.getModel()
604: .getElementAt(i),
605: 0, false,
606: false);
607: Dimension dim = flyweight.getPreferredSize();
608: cellHeights[i] = dim.height;
609:
610: cellHeight = (cellHeight * i + cellHeights[i]) / (i + 1);
611: cellWidth = Math.max(cellWidth, dim.width);
612: if (list.getLayoutOrientation() == JList.VERTICAL)
613: cellWidth = Math.max(cellWidth, list.getSize().width);
614: }
615: }
616: else
617: {
618: cellHeight = list.getFixedCellHeight();
619: cellWidth = list.getFixedCellWidth();
620: }
621: }
622:
623:
630: void damageLayout()
631: {
632: updateLayoutStateNeeded = 1;
633: }
634:
635:
639: protected void maybeUpdateLayoutState()
640: {
641: if (updateLayoutStateNeeded != 0)
642: {
643: updateLayoutState();
644: updateLayoutStateNeeded = 0;
645: }
646: }
647:
648:
651: public BasicListUI()
652: {
653: focusListener = new FocusHandler();
654: listDataListener = new ListDataHandler();
655: listSelectionListener = new ListSelectionHandler();
656: mouseInputListener = new MouseInputHandler();
657: keyListener = new KeyHandler();
658: propertyChangeListener = new PropertyChangeHandler();
659: componentListener = new ComponentHandler();
660: updateLayoutStateNeeded = 1;
661: rendererPane = new CellRendererPane();
662: }
663:
664:
670: protected void installDefaults()
671: {
672: UIDefaults defaults = UIManager.getLookAndFeelDefaults();
673: list.setForeground(defaults.getColor("List.foreground"));
674: list.setBackground(defaults.getColor("List.background"));
675: list.setSelectionForeground(defaults.getColor("List.selectionForeground"));
676: list.setSelectionBackground(defaults.getColor("List.selectionBackground"));
677: list.setOpaque(true);
678: }
679:
680:
684: protected void uninstallDefaults()
685: {
686: UIDefaults defaults = UIManager.getLookAndFeelDefaults();
687: list.setForeground(null);
688: list.setBackground(null);
689: list.setSelectionForeground(null);
690: list.setSelectionBackground(null);
691: }
692:
693:
699: protected void installListeners()
700: {
701: list.addFocusListener(focusListener);
702: list.getModel().addListDataListener(listDataListener);
703: list.addListSelectionListener(listSelectionListener);
704: list.addMouseListener(mouseInputListener);
705: list.addKeyListener(keyListener);
706: list.addMouseMotionListener(mouseInputListener);
707: list.addPropertyChangeListener(propertyChangeListener);
708: list.addComponentListener(componentListener);
709: }
710:
711:
714: protected void uninstallListeners()
715: {
716: list.removeFocusListener(focusListener);
717: list.getModel().removeListDataListener(listDataListener);
718: list.removeListSelectionListener(listSelectionListener);
719: list.removeMouseListener(mouseInputListener);
720: list.removeKeyListener(keyListener);
721: list.removeMouseMotionListener(mouseInputListener);
722: list.removePropertyChangeListener(propertyChangeListener);
723: }
724:
725:
728: protected void installKeyboardActions()
729: {
730: }
731:
732:
735: protected void uninstallKeyboardActions()
736: {
737: }
738:
739:
747: public void installUI(final JComponent c)
748: {
749: super.installUI(c);
750: list = (JList) c;
751: installDefaults();
752: installListeners();
753: installKeyboardActions();
754: maybeUpdateLayoutState();
755: }
756:
757:
765: public void uninstallUI(final JComponent c)
766: {
767: uninstallKeyboardActions();
768: uninstallListeners();
769: uninstallDefaults();
770: list = null;
771: }
772:
773:
781: public Dimension getPreferredSize(JComponent c)
782: {
783: int size = list.getModel().getSize();
784: if (size == 0)
785: return new Dimension(0, 0);
786: int visibleRows = list.getVisibleRowCount();
787: int layoutOrientation = list.getLayoutOrientation();
788: Rectangle bounds = getCellBounds(list, 0, list.getModel().getSize() - 1);
789: Dimension retVal = bounds.getSize();
790: Component parent = list.getParent();
791: if ((visibleRows == -1) && (parent instanceof JViewport))
792: {
793: JViewport viewport = (JViewport) parent;
794:
795: if (layoutOrientation == JList.HORIZONTAL_WRAP)
796: {
797: int h = viewport.getSize().height;
798: int cellsPerCol = h / cellHeight;
799: int w = size / cellsPerCol * cellWidth;
800: retVal = new Dimension(w, h);
801: }
802: else if (layoutOrientation == JList.VERTICAL_WRAP)
803: {
804: int w = viewport.getSize().width;
805: int cellsPerRow = Math.max(w / cellWidth, 1);
806: int h = size / cellsPerRow * cellHeight;
807: retVal = new Dimension(w, h);
808: }
809: }
810: return retVal;
811: }
812:
813:
820: private void paintBackground(Graphics g, JComponent c)
821: {
822: Dimension size = getPreferredSize(c);
823: Color save = g.getColor();
824: g.setColor(c.getBackground());
825: g.fillRect(0, 0, size.width, size.height);
826: g.setColor(save);
827: }
828:
829:
842: protected void paintCell(Graphics g, int row, Rectangle bounds,
843: ListCellRenderer rend, ListModel data,
844: ListSelectionModel sel, int lead)
845: {
846: boolean is_sel = list.isSelectedIndex(row);
847: boolean has_focus = false;
848: Component comp = rend.getListCellRendererComponent(list,
849: data.getElementAt(row),
850: 0, is_sel, has_focus);
851:
852:
853: rendererPane.paintComponent(g, comp, list, bounds);
854: }
855:
856:
863: public void paint(Graphics g, JComponent c)
864: {
865: int nrows = list.getModel().getSize();
866: if (nrows == 0)
867: return;
868:
869: maybeUpdateLayoutState();
870: ListCellRenderer render = list.getCellRenderer();
871: ListModel model = list.getModel();
872: ListSelectionModel sel = list.getSelectionModel();
873: int lead = sel.getLeadSelectionIndex();
874: Rectangle clip = g.getClipBounds();
875: paintBackground(g, list);
876:
877: for (int row = 0; row < nrows; ++row)
878: {
879: Rectangle bounds = getCellBounds(list, row, row);
880: if (bounds.intersects(clip))
881: paintCell(g, row, bounds, render, model, sel, lead);
882: }
883: }
884:
885:
894: public int locationToIndex(JList list, Point location)
895: {
896: int layoutOrientation = list.getLayoutOrientation();
897: int index = -1;
898: switch (layoutOrientation)
899: {
900: case JList.VERTICAL:
901: index = convertYToRow(location.y);
902: break;
903: case JList.HORIZONTAL_WRAP:
904:
905: int visibleRows = list.getVisibleRowCount();
906: int cellsPerRow = -1;
907: int numberOfItems = list.getModel().getSize();
908: Dimension listDim = list.getSize();
909: if (visibleRows <= 0)
910: {
911: try
912: {
913: cellsPerRow = listDim.width / cellWidth;
914: }
915: catch (ArithmeticException ex)
916: {
917: cellsPerRow = 1;
918: }
919: }
920: else
921: {
922: cellsPerRow = numberOfItems / visibleRows + 1;
923: }
924:
925:
926: int cellsPerColumn = numberOfItems / cellsPerRow + 1;
927: int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1);
928: int gridY = Math.min(location.y / cellHeight, cellsPerColumn);
929: index = gridX + gridY * cellsPerRow;
930: break;
931: case JList.VERTICAL_WRAP:
932:
933: int visibleRows2 = list.getVisibleRowCount();
934: if (visibleRows2 <= 0)
935: {
936: Dimension listDim2 = list.getSize();
937: visibleRows2 = listDim2.height / cellHeight;
938: }
939: int numberOfItems2 = list.getModel().getSize();
940: int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1;
941:
942: Dimension listDim2 = list.getSize();
943: int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1);
944: int gridY2 = Math.min(location.y / cellHeight, visibleRows2);
945: index = gridY2 + gridX2 * visibleRows2;
946: break;
947: }
948: return index;
949: }
950:
951: public Point indexToLocation(JList list, int index)
952: {
953: int layoutOrientation = list.getLayoutOrientation();
954: Point loc = null;
955: switch (layoutOrientation)
956: {
957: case JList.VERTICAL:
958: loc = new Point(0, convertRowToY(index));
959: break;
960: case JList.HORIZONTAL_WRAP:
961:
962: int visibleRows = list.getVisibleRowCount();
963: int numberOfCellsPerRow = -1;
964: if (visibleRows <= 0)
965: {
966: Dimension listDim = list.getSize();
967: numberOfCellsPerRow = Math.max(listDim.width / cellWidth, 1);
968: }
969: else
970: {
971: int numberOfItems = list.getModel().getSize();
972: numberOfCellsPerRow = numberOfItems / visibleRows + 1;
973: }
974:
975: int gridX = index % numberOfCellsPerRow;
976: int gridY = index / numberOfCellsPerRow;
977: int locX = gridX * cellWidth;
978: int locY = gridY * cellHeight;
979: loc = new Point(locX, locY);
980: break;
981: case JList.VERTICAL_WRAP:
982:
983: int visibleRows2 = list.getVisibleRowCount();
984: if (visibleRows2 <= 0)
985: {
986: Dimension listDim2 = list.getSize();
987: visibleRows2 = listDim2.height / cellHeight;
988: }
989:
990: if (visibleRows2 > 0)
991: {
992: int gridY2 = index % visibleRows2;
993: int gridX2 = index / visibleRows2;
994: int locX2 = gridX2 * cellWidth;
995: int locY2 = gridY2 * cellHeight;
996: loc = new Point(locX2, locY2);
997: }
998: else
999: loc = new Point(0, convertRowToY(index));
1000: break;
1001: }
1002: return loc;
1003: }
1004: }