0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package javax.swing.plaf.basic;
0018:
0019: import java.awt.Component;
0020: import java.awt.Dimension;
0021: import java.awt.Graphics;
0022: import java.awt.Insets;
0023: import java.awt.Point;
0024: import java.awt.Rectangle;
0025: import java.awt.datatransfer.StringSelection;
0026: import java.awt.datatransfer.Transferable;
0027: import java.awt.event.ActionEvent;
0028: import java.awt.event.ComponentAdapter;
0029: import java.awt.event.ComponentEvent;
0030: import java.awt.event.ComponentListener;
0031: import java.awt.event.FocusEvent;
0032: import java.awt.event.FocusListener;
0033: import java.awt.event.KeyAdapter;
0034: import java.awt.event.KeyEvent;
0035: import java.awt.event.KeyListener;
0036: import java.awt.event.MouseEvent;
0037: import java.beans.PropertyChangeEvent;
0038: import java.beans.PropertyChangeListener;
0039:
0040: import javax.swing.AbstractAction;
0041: import javax.swing.CellRendererPane;
0042: import javax.swing.JComponent;
0043: import javax.swing.JList;
0044: import javax.swing.ListCellRenderer;
0045: import javax.swing.ListModel;
0046: import javax.swing.ListSelectionModel;
0047: import javax.swing.LookAndFeel;
0048: import javax.swing.SwingUtilities;
0049: import javax.swing.Timer;
0050: import javax.swing.TransferHandler;
0051: import javax.swing.UIManager;
0052: import javax.swing.event.ListDataEvent;
0053: import javax.swing.event.ListDataListener;
0054: import javax.swing.event.ListSelectionEvent;
0055: import javax.swing.event.ListSelectionListener;
0056: import javax.swing.event.MouseInputListener;
0057: import javax.swing.plaf.ComponentUI;
0058: import javax.swing.plaf.ListUI;
0059: import javax.swing.plaf.UIResource;
0060: import javax.swing.text.Position;
0061:
0062: import org.apache.harmony.x.swing.ExtendedListElement;
0063: import org.apache.harmony.x.swing.ExtendedListFactory;
0064: import org.apache.harmony.x.swing.StringConstants;
0065: import org.apache.harmony.x.swing.Utilities;
0066:
0067: import org.apache.harmony.x.swing.internal.nls.Messages;
0068:
0069: public class BasicListUI extends ListUI {
0070: protected int cellHeight = -1;
0071: protected int cellWidth = -1;
0072: protected int[] cellHeights;
0073: protected JList list;
0074: protected CellRendererPane rendererPane;
0075: protected int updateLayoutStateNeeded = modelChanged;
0076:
0077: protected static final int modelChanged = 1;
0078: protected static final int selectionModelChanged = 2;
0079: protected static final int fontChanged = 4;
0080: protected static final int fixedCellWidthChanged = 8;
0081: protected static final int fixedCellHeightChanged = 16;
0082: protected static final int prototypeCellValueChanged = 32;
0083: protected static final int cellRendererChanged = 64;
0084: private static final int otherChanged = 128;
0085:
0086: protected FocusListener focusListener;
0087: protected ListDataListener listDataListener;
0088: protected ListSelectionListener listSelectionListener;
0089: protected MouseInputListener mouseInputListener;
0090: protected PropertyChangeListener propertyChangeListener;
0091: private ComponentListener componentListener;
0092: private KeyListener keyListener;
0093:
0094: boolean extendedSupportEnabled;
0095:
0096: final ListLayouter layouter = new ListLayouter();
0097:
0098: public class FocusHandler implements FocusListener {
0099: public void focusGained(final FocusEvent e) {
0100: repaintCellFocus();
0101: }
0102:
0103: public void focusLost(final FocusEvent e) {
0104: repaintCellFocus();
0105: }
0106:
0107: protected void repaintCellFocus() {
0108: if (list.getLeadSelectionIndex() != -1) {
0109: Rectangle bounds = getCellBounds(list, list
0110: .getLeadSelectionIndex(), list
0111: .getLeadSelectionIndex());
0112: if (bounds != null) {
0113: list.repaint(bounds);
0114: } else {
0115: list.repaint();
0116: }
0117: }
0118: }
0119: }
0120:
0121: public class ListDataHandler implements ListDataListener {
0122: public void contentsChanged(final ListDataEvent e) {
0123: layouter.reset();
0124: list.revalidate();
0125: list.repaint();
0126: }
0127:
0128: public void intervalAdded(final ListDataEvent e) {
0129: list.getSelectionModel().insertIndexInterval(e.getIndex0(),
0130: Math.abs(e.getIndex1() - e.getIndex0()) + 1, false);
0131: layouter.reset();
0132: list.revalidate();
0133: list.repaint();
0134: }
0135:
0136: public void intervalRemoved(final ListDataEvent e) {
0137: list.getSelectionModel().removeIndexInterval(e.getIndex0(),
0138: e.getIndex1());
0139: layouter.reset();
0140: list.revalidate();
0141: list.repaint();
0142: }
0143: }
0144:
0145: public class ListSelectionHandler implements ListSelectionListener {
0146: public void valueChanged(final ListSelectionEvent e) {
0147: repaintCells(e.getFirstIndex(), e.getLastIndex());
0148: }
0149: }
0150:
0151: public class MouseInputHandler implements MouseInputListener {
0152: private DnDMouseHelper dndhelper = new DnDMouseHelper(list);
0153:
0154: public void mousePressed(final MouseEvent e) {
0155: if (!SwingUtilities.isLeftMouseButton(e)) {
0156: return;
0157: }
0158: list.requestFocus();
0159:
0160: int cellIndex = locationToIndex(list, e.getPoint());
0161: dndhelper.mousePressed(e, list.getDragEnabled(),
0162: cellIndex != -1, list.isSelectedIndex(cellIndex));
0163: if (cellIndex == -1) {
0164: return;
0165: }
0166: if (!list.getDragEnabled()
0167: || !list.isSelectedIndex(cellIndex)) {
0168: list.getSelectionModel().setValueIsAdjusting(true);
0169: processSelection(e, cellIndex);
0170: }
0171: }
0172:
0173: public void mouseReleased(final MouseEvent e) {
0174: dndhelper.mouseReleased(e);
0175: if (dndhelper.shouldProcessOnRelease()) {
0176: int cellIndex = locationToIndex(list, e.getPoint());
0177: if (cellIndex != -1) {
0178: processSelection(e, cellIndex);
0179: }
0180: }
0181: list.getSelectionModel().setValueIsAdjusting(false);
0182: }
0183:
0184: public void mouseDragged(final MouseEvent e) {
0185: dndhelper.mouseDragged(e);
0186:
0187: if (!dndhelper.isDndStarted()
0188: && SwingUtilities.isLeftMouseButton(e)
0189: && !e.isControlDown() && !e.isShiftDown()) {
0190:
0191: int cellIndex = locationToIndex(list, e.getPoint());
0192: if (cellIndex != -1
0193: && list.getLeadSelectionIndex() != cellIndex) {
0194: list.getSelectionModel().setValueIsAdjusting(true);
0195: list.setSelectedIndex(cellIndex);
0196: list.ensureIndexIsVisible(cellIndex);
0197: }
0198: }
0199: }
0200:
0201: public void mouseClicked(final MouseEvent e) {
0202: }
0203:
0204: public void mouseEntered(final MouseEvent e) {
0205: }
0206:
0207: public void mouseExited(final MouseEvent e) {
0208: }
0209:
0210: public void mouseMoved(final MouseEvent e) {
0211: }
0212:
0213: private void processSelection(final MouseEvent e,
0214: final int cellIndex) {
0215: if (e.isControlDown()) {
0216: if (list.isSelectedIndex(cellIndex)) {
0217: list.removeSelectionInterval(cellIndex, cellIndex);
0218: } else {
0219: list.addSelectionInterval(cellIndex, cellIndex);
0220: }
0221: } else if (e.isShiftDown()
0222: && list.getAnchorSelectionIndex() != -1) {
0223: list.setSelectionInterval(list
0224: .getAnchorSelectionIndex(), cellIndex);
0225: } else {
0226: list.setSelectionInterval(cellIndex, cellIndex);
0227: }
0228: }
0229: }
0230:
0231: public class PropertyChangeHandler implements
0232: PropertyChangeListener {
0233: public void propertyChange(final PropertyChangeEvent event) {
0234: String changedProperty = event.getPropertyName();
0235: if ("cellRenderer".equals(changedProperty)) {
0236: updateLayoutStateNeeded = updateLayoutStateNeeded
0237: | cellRendererChanged;
0238: } else if ("fixedCellHeight".equals(changedProperty)) {
0239: updateLayoutStateNeeded = updateLayoutStateNeeded
0240: | fixedCellHeightChanged;
0241: } else if ("fixedCellWidth".equals(changedProperty)) {
0242: updateLayoutStateNeeded = updateLayoutStateNeeded
0243: | fixedCellWidthChanged;
0244: } else if ("model".equals(changedProperty)) {
0245: updateLayoutStateNeeded = updateLayoutStateNeeded
0246: | modelChanged;
0247: } else if ("selectionModel".equals(changedProperty)) {
0248: updateLayoutStateNeeded = updateLayoutStateNeeded
0249: | selectionModelChanged;
0250: } else if ("prototypeCellValue".equals(changedProperty)) {
0251: updateLayoutStateNeeded = updateLayoutStateNeeded
0252: | prototypeCellValueChanged;
0253: } else if ("font".equals(changedProperty)) {
0254: updateLayoutStateNeeded = updateLayoutStateNeeded
0255: | fontChanged;
0256: } else if ("visibleRowCount".equals(changedProperty)
0257: || "layoutOrientation".equals(changedProperty)
0258: || "componentOrientation".equals(changedProperty)
0259: || "width".equals(changedProperty)
0260: || "height".equals(changedProperty)) {
0261: updateLayoutStateNeeded = updateLayoutStateNeeded
0262: | otherChanged;
0263: } else if (StringConstants.COMPONENT_ORIENTATION
0264: .equals(changedProperty)) {
0265: uninstallKeyboardActions();
0266: installKeyboardActions();
0267: }
0268:
0269: if ("model".equals(changedProperty)) {
0270: ((ListModel) event.getOldValue())
0271: .removeListDataListener(listDataListener);
0272: ((ListModel) event.getNewValue())
0273: .addListDataListener(listDataListener);
0274: }
0275: if ("selectionModel".equals(changedProperty)) {
0276: ((ListSelectionModel) event.getOldValue())
0277: .removeListSelectionListener(listSelectionListener);
0278: ((ListSelectionModel) event.getNewValue())
0279: .addListSelectionListener(listSelectionListener);
0280: }
0281:
0282: if ("enabled".equals(changedProperty)) {
0283: if (((Boolean) event.getNewValue()).booleanValue()) {
0284: list.addMouseListener(mouseInputListener);
0285: list.addMouseMotionListener(mouseInputListener);
0286: } else {
0287: list.removeMouseListener(mouseInputListener);
0288: list.removeMouseMotionListener(mouseInputListener);
0289: }
0290: }
0291:
0292: if (StringConstants.EXTENDED_SUPPORT_ENABLED_PROPERTY
0293: .equals(changedProperty)) {
0294: extendedSupportEnabled = ((Boolean) event.getNewValue())
0295: .booleanValue();
0296: if (extendedSupportEnabled) {
0297: list.setCellRenderer(ExtendedListFactory
0298: .getFactory().createExtendedRenderer());
0299: } else {
0300: list.setCellRenderer((ListCellRenderer) UIManager
0301: .get("List.cellRenderer"));
0302: }
0303: updateLayoutStateNeeded = updateLayoutStateNeeded
0304: | otherChanged;
0305: }
0306:
0307: list.revalidate();
0308: list.repaint();
0309: }
0310: }
0311:
0312: private class ComponentReshapeHandler extends ComponentAdapter {
0313: @Override
0314: public void componentResized(final ComponentEvent e) {
0315: if (layouter.getLayoutStrategy().isSizeDependent()) {
0316: layouter.reset();
0317: list.repaint();
0318: }
0319: }
0320: }
0321:
0322: private class KeyHandler extends KeyAdapter {
0323: private StringBuffer searchPrefix = new StringBuffer();
0324: private Timer searchTimer = new Timer(1000,
0325: new AbstractAction() {
0326: /**
0327: * This class is not guaranteed to be correctly deserialized.
0328: */
0329: private static final long serialVersionUID = 1L;
0330:
0331: public void actionPerformed(final ActionEvent e) {
0332: resetSearch();
0333: }
0334: });
0335:
0336: @Override
0337: public void keyTyped(final KeyEvent e) {
0338: if (list.getModel().getSize() == 0) {
0339: return;
0340: }
0341:
0342: if (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
0343: return;
0344: }
0345: if ((e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) != 0
0346: || (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
0347:
0348: return;
0349: }
0350:
0351: searchPrefix.append(e.getKeyChar());
0352:
0353: int startIndex = list.getSelectedIndex();
0354: if (startIndex == -1) {
0355: startIndex = 0;
0356: }
0357:
0358: int nextIndex = getNextMatch(startIndex);
0359: if (nextIndex == -1) {
0360: if (searchPrefix.length() > 1) {
0361: resetSearch();
0362: searchPrefix.append(e.getKeyChar());
0363: startIndex++;
0364: if (startIndex >= list.getModel().getSize()) {
0365: startIndex = 0;
0366: }
0367: nextIndex = getNextMatch(startIndex);
0368: }
0369: }
0370: if (nextIndex != -1) {
0371: searchTimer.stop();
0372: list.setSelectedIndex(nextIndex);
0373: list.ensureIndexIsVisible(nextIndex);
0374: searchTimer.restart();
0375: } else {
0376: resetSearch();
0377: }
0378: }
0379:
0380: private int getNextMatch(final int startIndex) {
0381: int candidateIndex = list.getNextMatch(searchPrefix
0382: .toString(), startIndex, Position.Bias.Forward);
0383: if (candidateIndex == -1 || !extendedSupportEnabled) {
0384: return candidateIndex;
0385: }
0386:
0387: int firstFoundIndex = candidateIndex;
0388: do {
0389: Object value = list.getModel().getElementAt(
0390: candidateIndex);
0391: if (!(value instanceof ExtendedListElement)
0392: || ((ExtendedListElement) value).isChoosable()) {
0393:
0394: return candidateIndex;
0395: }
0396: int nextStartIndex = candidateIndex + 1;
0397: if (nextStartIndex == list.getModel().getSize()) {
0398: nextStartIndex = 0;
0399: }
0400: candidateIndex = list.getNextMatch(searchPrefix
0401: .toString(), nextStartIndex,
0402: Position.Bias.Forward);
0403: } while (firstFoundIndex != candidateIndex);
0404:
0405: return -1;
0406: }
0407:
0408: private void resetSearch() {
0409: searchPrefix.delete(0, searchPrefix.length());
0410: searchTimer.stop();
0411: }
0412: };
0413:
0414: private class ListTransferHandler extends TransferHandler {
0415: /**
0416: * This class is not guaranteed to be correctly deserialized.
0417: */
0418: private static final long serialVersionUID = -3865101058747175640L;
0419:
0420: private final String lineSeparator = System
0421: .getProperty("line.separator");
0422:
0423: @Override
0424: public int getSourceActions(final JComponent c) {
0425: return COPY;
0426: }
0427:
0428: @Override
0429: protected Transferable createTransferable(final JComponent c) {
0430: Object[] selectedValues = list.getSelectedValues();
0431: if (selectedValues == null || selectedValues.length == 0) {
0432: return null;
0433: }
0434:
0435: StringBuffer content = new StringBuffer();
0436: for (int i = 0; i < selectedValues.length; i++) {
0437: content.append(selectedValues[i]);
0438: if (i < selectedValues.length - 1) {
0439: content.append(lineSeparator);
0440: }
0441: }
0442: return new StringSelection(content.toString());
0443: }
0444: }
0445:
0446: public static ComponentUI createUI(final JComponent list) {
0447: return new BasicListUI();
0448: }
0449:
0450: @Override
0451: public Rectangle getCellBounds(final JList list, final int index1,
0452: final int index2) {
0453: layouter.setList(list);
0454: maybeUpdateLayoutState();
0455:
0456: Rectangle result = null;
0457: if (index1 < 0 || index1 >= list.getModel().getSize()
0458: || index2 < 0 || index2 >= list.getModel().getSize()) {
0459:
0460: return result;
0461: }
0462:
0463: if (index1 <= index2) {
0464: for (int i = index1; i <= index2; i++) {
0465: if (result == null) {
0466: result = layouter.getLayoutStrategy().getBounds(i);
0467: } else {
0468: result.add(layouter.getLayoutStrategy()
0469: .getBounds(i));
0470: }
0471: }
0472: } else {
0473: for (int i = index2; i <= index1; i++) {
0474: if (result == null) {
0475: result = layouter.getLayoutStrategy().getBounds(i);
0476: } else {
0477: result.add(layouter.getLayoutStrategy()
0478: .getBounds(i));
0479: }
0480: }
0481: }
0482:
0483: return result;
0484: }
0485:
0486: @Override
0487: public Dimension getMaximumSize(final JComponent c) {
0488: return getPreferredSize(c);
0489: }
0490:
0491: @Override
0492: public Dimension getMinimumSize(final JComponent c) {
0493: return getPreferredSize(c);
0494: }
0495:
0496: @Override
0497: public Dimension getPreferredSize(final JComponent c) {
0498: maybeUpdateLayoutState();
0499:
0500: JList list = (JList) c;
0501: layouter.setList(list);
0502: return layouter.getLayoutStrategy().getSize();
0503: }
0504:
0505: @Override
0506: public Point indexToLocation(final JList list, final int index) {
0507: if (index < 0 || index >= list.getModel().getSize()) {
0508: return null;
0509: }
0510:
0511: layouter.setList(list);
0512: maybeUpdateLayoutState();
0513:
0514: Rectangle bounds = getCellBounds(list, index, index);
0515: return new Point(bounds.x, bounds.y);
0516: }
0517:
0518: @Override
0519: public int locationToIndex(final JList list, final Point location) {
0520: if (location == null) {
0521: throw new NullPointerException();
0522: }
0523:
0524: layouter.setList(list);
0525: maybeUpdateLayoutState();
0526:
0527: return layouter.getNearestIndex(location);
0528: }
0529:
0530: @Override
0531: public void paint(final Graphics g, final JComponent c) {
0532: maybeUpdateLayoutState();
0533:
0534: Rectangle clipRect = g.getClipBounds();
0535: if (clipRect != null) {
0536: for (int i = 0; i < list.getModel().getSize(); i++) {
0537: Rectangle bounds = layouter.getLayoutStrategy()
0538: .getBounds(i);
0539: if (bounds.intersects(clipRect)) {
0540: paintCell(g, i, bounds, list.getCellRenderer(),
0541: list.getModel(), list.getSelectionModel(),
0542: i);
0543: }
0544: }
0545: } else {
0546: for (int i = 0; i < list.getModel().getSize(); i++) {
0547: Rectangle bounds = layouter.getLayoutStrategy()
0548: .getBounds(i);
0549: paintCell(g, i, bounds, list.getCellRenderer(), list
0550: .getModel(), list.getSelectionModel(), i);
0551: }
0552: }
0553: }
0554:
0555: @Override
0556: public void installUI(final JComponent c) {
0557: list = (JList) c;
0558: rendererPane = new CellRendererPane();
0559: rendererPane.setVisible(false);
0560: list.add(rendererPane);
0561: layouter.setList(list);
0562:
0563: installDefaults();
0564: installListeners();
0565: installKeyboardActions();
0566: }
0567:
0568: @Override
0569: public void uninstallUI(final JComponent c) {
0570: list = (JList) c;
0571: list.remove(rendererPane);
0572: rendererPane = null;
0573: layouter.setList(null);
0574:
0575: uninstallDefaults();
0576: uninstallListeners();
0577: uninstallKeyboardActions();
0578: }
0579:
0580: protected void installDefaults() {
0581: LookAndFeel.installColorsAndFont(list, "List.background",
0582: "List.foreground", "List.font");
0583: LookAndFeel.installBorder(list, "List.border");
0584: LookAndFeel.installProperty(list, "opaque", Boolean.TRUE);
0585:
0586: if ((list.getSelectionBackground() == null)
0587: || (list.getSelectionBackground() instanceof UIResource)) {
0588: list.setSelectionBackground(UIManager
0589: .getColor("List.selectionBackground"));
0590: }
0591: if ((list.getSelectionForeground() == null)
0592: || (list.getSelectionForeground() instanceof UIResource)) {
0593: list.setSelectionForeground(UIManager
0594: .getColor("List.selectionForeground"));
0595: }
0596:
0597: if ((list.getCellRenderer() == null)
0598: || (list.getCellRenderer() instanceof UIResource)) {
0599: list.setCellRenderer((ListCellRenderer) UIManager
0600: .get("List.cellRenderer"));
0601: }
0602:
0603: list.setTransferHandler(new ListTransferHandler());
0604: }
0605:
0606: protected void installKeyboardActions() {
0607: BasicListKeyboardActions.installKeyboardActions(list);
0608: }
0609:
0610: protected void installListeners() {
0611: focusListener = createFocusListener();
0612: list.addFocusListener(focusListener);
0613:
0614: listSelectionListener = createListSelectionListener();
0615: list.getSelectionModel().addListSelectionListener(
0616: listSelectionListener);
0617:
0618: listDataListener = createListDataListener();
0619: list.getModel().addListDataListener(listDataListener);
0620:
0621: propertyChangeListener = createPropertyChangeListener();
0622: list.addPropertyChangeListener(propertyChangeListener);
0623:
0624: mouseInputListener = createMouseInputListener();
0625: list.addMouseListener(mouseInputListener);
0626: list.addMouseMotionListener(mouseInputListener);
0627:
0628: componentListener = createComponentListener();
0629: list.addComponentListener(componentListener);
0630:
0631: keyListener = new KeyHandler();
0632: list.addKeyListener(keyListener);
0633: }
0634:
0635: protected void uninstallDefaults() {
0636: LookAndFeel.uninstallBorder(list);
0637: if (list.getCellRenderer() instanceof UIResource) {
0638: list.setCellRenderer(null);
0639: }
0640: Utilities.uninstallColorsAndFont(list);
0641:
0642: list.setTransferHandler(null);
0643: }
0644:
0645: protected void uninstallKeyboardActions() {
0646: BasicListKeyboardActions.uninstallKeyboardActions(list);
0647: }
0648:
0649: protected void uninstallListeners() {
0650: list.removeListSelectionListener(listSelectionListener);
0651: list.getSelectionModel().removeListSelectionListener(
0652: listSelectionListener);
0653: listSelectionListener = null;
0654:
0655: list.getModel().removeListDataListener(listDataListener);
0656: listDataListener = null;
0657:
0658: list.removeFocusListener(focusListener);
0659: focusListener = null;
0660:
0661: list.removePropertyChangeListener(propertyChangeListener);
0662: propertyChangeListener = null;
0663:
0664: list.removeMouseListener(mouseInputListener);
0665: list.removeMouseMotionListener(mouseInputListener);
0666: mouseInputListener = null;
0667:
0668: list.removeComponentListener(componentListener);
0669: componentListener = null;
0670:
0671: list.removeKeyListener(keyListener);
0672: keyListener = null;
0673: }
0674:
0675: protected void updateLayoutState() {
0676: layouter.reinit();
0677: }
0678:
0679: protected void maybeUpdateLayoutState() {
0680: if (updateLayoutStateNeeded > 0) {
0681: updateLayoutState();
0682: updateLayoutStateNeeded = 0;
0683: }
0684: }
0685:
0686: protected void paintCell(final Graphics g, final int row,
0687: final Rectangle rowBounds,
0688: final ListCellRenderer cellRenderer,
0689: final ListModel dataModel,
0690: final ListSelectionModel selModel, final int leadIndex) {
0691: if (list.getCellRenderer() != null) {
0692: Component renderer = list
0693: .getCellRenderer()
0694: .getListCellRendererComponent(
0695: list,
0696: dataModel.getElementAt(row),
0697: row,
0698: selModel.isSelectedIndex(row),
0699: list.isFocusOwner()
0700: && selModel.getLeadSelectionIndex() == row);
0701: rendererPane.paintComponent(g, renderer, list, rowBounds.x,
0702: rowBounds.y, rowBounds.width, rowBounds.height);
0703: }
0704: }
0705:
0706: protected void selectNextIndex() {
0707: int currectSelection = list.getMinSelectionIndex();
0708:
0709: if (currectSelection < list.getModel().getSize() - 1
0710: && list.getModel().getSize() > 0) {
0711: list.setSelectedIndex(currectSelection + 1);
0712: list.ensureIndexIsVisible(currectSelection + 1);
0713: }
0714: }
0715:
0716: protected void selectPreviousIndex() {
0717: int currectSelection = list.getMinSelectionIndex();
0718: if (currectSelection > 0) {
0719: list.setSelectedIndex(currectSelection - 1);
0720: list.ensureIndexIsVisible(currectSelection - 1);
0721: }
0722: }
0723:
0724: protected int getRowHeight(final int row) {
0725: if (row < 0 || row >= list.getModel().getSize()) {
0726: return -1;
0727: }
0728:
0729: return layouter.getLayoutStrategy().getBounds(row).height;
0730: }
0731:
0732: protected int convertRowToY(final int row) {
0733: if (row < 0 || row >= list.getModel().getSize()) {
0734: return -1;
0735: }
0736:
0737: return getCellBounds(list, row, row).y;
0738: }
0739:
0740: protected int convertYToRow(final int y) {
0741: for (int row = 0; row < list.getModel().getSize(); row++) {
0742: Rectangle bounds = getCellBounds(list, row, row);
0743: if (bounds.y <= y && bounds.y + bounds.height >= y) {
0744: return row;
0745: }
0746: }
0747:
0748: return -1;
0749: }
0750:
0751: protected FocusListener createFocusListener() {
0752: return new FocusHandler();
0753: }
0754:
0755: protected ListDataListener createListDataListener() {
0756: return new ListDataHandler();
0757: }
0758:
0759: protected ListSelectionListener createListSelectionListener() {
0760: return new ListSelectionHandler();
0761: }
0762:
0763: protected MouseInputListener createMouseInputListener() {
0764: return new MouseInputHandler();
0765: }
0766:
0767: protected PropertyChangeListener createPropertyChangeListener() {
0768: return new PropertyChangeHandler();
0769: }
0770:
0771: boolean isChoosable(final int index) {
0772: if (!extendedSupportEnabled) {
0773: return true;
0774: }
0775: Object value = list.getModel().getElementAt(index);
0776: return (value instanceof ExtendedListElement) ? ((ExtendedListElement) value)
0777: .isChoosable()
0778: : true;
0779: }
0780:
0781: private ComponentListener createComponentListener() {
0782: return new ComponentReshapeHandler();
0783: }
0784:
0785: private void repaintCells(final int firstIndex, final int lastIndex) {
0786: if (firstIndex == -1 || lastIndex == -1) {
0787: return;
0788: }
0789: int modelSize = list.getModel().getSize();
0790: int adjustedFirstIndex = firstIndex;
0791: if (adjustedFirstIndex >= modelSize) {
0792: adjustedFirstIndex = modelSize - 1;
0793: }
0794: int adjustedLastIndex = lastIndex;
0795: if (adjustedLastIndex >= modelSize) {
0796: adjustedLastIndex = modelSize - 1;
0797: }
0798:
0799: Rectangle bounds = getCellBounds(list, adjustedFirstIndex,
0800: adjustedLastIndex);
0801: if (bounds != null) {
0802: list.repaint(bounds);
0803: }
0804: }
0805:
0806: interface LayoutStrategy {
0807: Rectangle getBounds(final int index);
0808:
0809: int getRow(final int index);
0810:
0811: int getColumn(final int index);
0812:
0813: int getIndex(final int row, final int column);
0814:
0815: int getRowCount();
0816:
0817: int getColumnCount();
0818:
0819: Dimension getSize();
0820:
0821: boolean isSizeDependent();
0822: }
0823:
0824: final class ListLayouter {
0825: private JList list;
0826: private Insets insets;
0827: private LayoutStrategy strategy;
0828: private Insets cachedInsets = new Insets(0, 0, 0, 0);
0829:
0830: public abstract class AbstractLayoutStrategy implements
0831: LayoutStrategy {
0832: public abstract int getRowCount();
0833:
0834: public abstract int getColumnCount();
0835:
0836: public AbstractLayoutStrategy() {
0837: cellWidth = getMaximumWidth();
0838: cellHeight = getMaximumHeight();
0839: cellHeights = null;
0840: }
0841:
0842: public Rectangle getBounds(final int index) {
0843: return new Rectangle(insets.left + getColumn(index)
0844: * cellWidth, insets.top + getRow(index)
0845: * cellHeight, cellWidth, cellHeight);
0846: }
0847:
0848: public Dimension getSize() {
0849: Dimension result = new Dimension(getColumnCount()
0850: * cellWidth, getRowCount() * cellHeight);
0851: return Utilities.addInsets(result, list
0852: .getInsets(cachedInsets));
0853: }
0854:
0855: public boolean isSizeDependent() {
0856: return list.getVisibleRowCount() <= 0;
0857: }
0858: }
0859:
0860: public class VerticalLayoutStategy extends
0861: AbstractLayoutStrategy {
0862: public VerticalLayoutStategy() {
0863: if (list.getFixedCellHeight() <= 0) {
0864: cellHeight = -1;
0865:
0866: cellHeights = new int[getNumberOfElements()];
0867: for (int i = 0; i < getNumberOfElements(); i++) {
0868: cellHeights[i] = getPreferredSize(i).height;
0869: }
0870: }
0871: }
0872:
0873: @Override
0874: public Rectangle getBounds(final int index) {
0875: int y = 0;
0876: int height = cellHeight;
0877: if (cellHeight == -1) {
0878: for (int i = 0; i < index; i++) {
0879: y += cellHeights[i];
0880: }
0881: height = cellHeights[index];
0882: } else if (index > 0) {
0883: y = cellHeight * index;
0884: }
0885:
0886: return new Rectangle(insets.left, insets.top + y, list
0887: .getWidth()
0888: - insets.left - insets.right, height);
0889: }
0890:
0891: public int getRow(final int index) {
0892: return index;
0893: }
0894:
0895: public int getColumn(final int index) {
0896: return 0;
0897: }
0898:
0899: public int getIndex(final int row, final int column) {
0900: return row;
0901: }
0902:
0903: @Override
0904: public int getRowCount() {
0905: return list.getModel().getSize();
0906: }
0907:
0908: @Override
0909: public int getColumnCount() {
0910: return getNumberOfElements() > 0 ? 1 : 0;
0911: }
0912:
0913: @Override
0914: public Dimension getSize() {
0915: int height = 0;
0916: if (cellHeight == -1) {
0917: for (int i = 0; i < getRowCount(); i++) {
0918: height += cellHeights[i];
0919: }
0920: } else {
0921: height = cellHeight * getRowCount();
0922: }
0923:
0924: Dimension result = new Dimension(getColumnCount()
0925: * cellWidth, height);
0926: return Utilities.addInsets(result, list
0927: .getInsets(cachedInsets));
0928: }
0929:
0930: @Override
0931: public boolean isSizeDependent() {
0932: return false;
0933: }
0934: }
0935:
0936: public class VerticalWrapVisibleSpecifiedLayoutStrategy extends
0937: AbstractLayoutStrategy {
0938: public int getRow(final int index) {
0939: return index % getRowCount();
0940: }
0941:
0942: public int getIndex(final int row, final int column) {
0943: return column * getRowCount() + row;
0944: }
0945:
0946: public int getColumn(final int index) {
0947: return index / getRowCount();
0948: }
0949:
0950: @Override
0951: public int getRowCount() {
0952: return list.getVisibleRowCount();
0953: }
0954:
0955: @Override
0956: public int getColumnCount() {
0957: int modelSize = list.getModel().getSize();
0958: return (modelSize == 0 ? 0 : modelSize - 1)
0959: / getRowCount() + 1;
0960: }
0961: }
0962:
0963: public class VerticalWrapVisibleUnspecifiedLayoutStrategy
0964: extends AbstractLayoutStrategy {
0965: private int maximumNumOfRows;
0966:
0967: public VerticalWrapVisibleUnspecifiedLayoutStrategy() {
0968: maximumNumOfRows = (list.getHeight() - insets.top - insets.bottom)
0969: / cellHeight;
0970: if (maximumNumOfRows == 0) {
0971: maximumNumOfRows = 1;
0972: }
0973: }
0974:
0975: public int getIndex(final int row, final int column) {
0976: return column * getRowCount() + row;
0977: }
0978:
0979: public int getRow(final int index) {
0980: return index % getRowCount();
0981: }
0982:
0983: public int getColumn(final int index) {
0984: return index / getRowCount();
0985: }
0986:
0987: @Override
0988: public int getRowCount() {
0989: return maximumNumOfRows;
0990: }
0991:
0992: @Override
0993: public int getColumnCount() {
0994: int modelSize = list.getModel().getSize();
0995: return (modelSize == 0 ? 0 : modelSize - 1)
0996: / getRowCount() + 1;
0997: }
0998: }
0999:
1000: public class HorizontalWrapVisibleSpecifiedLayoutStrategy
1001: extends AbstractLayoutStrategy {
1002: public int getRow(final int index) {
1003: return index / getColumnCount();
1004: }
1005:
1006: public int getIndex(final int row, final int column) {
1007: return row * getColumnCount() + column;
1008: }
1009:
1010: public int getColumn(final int index) {
1011: return index % getColumnCount();
1012: }
1013:
1014: @Override
1015: public int getRowCount() {
1016: return list.getVisibleRowCount();
1017: }
1018:
1019: @Override
1020: public int getColumnCount() {
1021: int modelSize = list.getModel().getSize();
1022: return (modelSize == 0 ? 0 : modelSize - 1)
1023: / getRowCount() + 1;
1024: }
1025: }
1026:
1027: public class HorizontalWrapVisibleUnspecifiedLayoutStrategy
1028: extends AbstractLayoutStrategy {
1029: private int maximumNumOfColumns;
1030:
1031: public HorizontalWrapVisibleUnspecifiedLayoutStrategy() {
1032: maximumNumOfColumns = (list.getWidth() - insets.left - insets.right)
1033: / cellWidth;
1034: if (maximumNumOfColumns == 0) {
1035: maximumNumOfColumns = 1;
1036: }
1037: }
1038:
1039: public int getIndex(final int row, final int column) {
1040: return row * getColumnCount() + column;
1041: }
1042:
1043: public int getRow(final int index) {
1044: return index / getColumnCount();
1045: }
1046:
1047: public int getColumn(final int index) {
1048: return index % getColumnCount();
1049: }
1050:
1051: @Override
1052: public int getRowCount() {
1053: int modelSize = list.getModel().getSize();
1054: return (modelSize == 0 ? 0 : modelSize - 1)
1055: / getColumnCount() + 1;
1056: }
1057:
1058: @Override
1059: public int getColumnCount() {
1060: return maximumNumOfColumns;
1061: }
1062: }
1063:
1064: public class OrientationStrategy implements LayoutStrategy {
1065: protected final LayoutStrategy strategy;
1066:
1067: public OrientationStrategy(final LayoutStrategy strategy) {
1068: this .strategy = strategy;
1069: }
1070:
1071: public int getIndex(final int row, final int column) {
1072: return strategy.getIndex(row, column);
1073: }
1074:
1075: public int getRowCount() {
1076: return strategy.getRowCount();
1077: }
1078:
1079: public int getColumnCount() {
1080: return strategy.getColumnCount();
1081: }
1082:
1083: public int getRow(final int index) {
1084: return strategy.getRow(index);
1085: }
1086:
1087: public int getColumn(final int index) {
1088: return strategy.getColumn(index);
1089: }
1090:
1091: public Dimension getSize() {
1092: return strategy.getSize();
1093: }
1094:
1095: public Rectangle getBounds(final int index) {
1096: return strategy.getBounds(index);
1097: }
1098:
1099: public boolean isSizeDependent() {
1100: return strategy.isSizeDependent();
1101: }
1102: }
1103:
1104: public class RightToLeftStrategy extends OrientationStrategy {
1105: public RightToLeftStrategy(final LayoutStrategy strategy) {
1106: super (strategy);
1107: }
1108:
1109: @Override
1110: public Rectangle getBounds(final int index) {
1111: Rectangle bounds = strategy.getBounds(index);
1112: int x = list.getWidth() - bounds.x - bounds.width;
1113:
1114: return new Rectangle(x, bounds.y, bounds.width,
1115: bounds.height);
1116: }
1117: }
1118:
1119: public ListLayouter() {
1120: reset();
1121: }
1122:
1123: public void setList(final JList list) {
1124: if (this .list != list) {
1125: this .list = list;
1126: reset();
1127: }
1128: }
1129:
1130: public void reinit() {
1131: reset();
1132: getLayoutStrategy();
1133: }
1134:
1135: public void reset() {
1136: strategy = null;
1137: }
1138:
1139: public LayoutStrategy getLayoutStrategy() {
1140: reinitFields();
1141: if (strategy == null) {
1142: if (list.getComponentOrientation().isLeftToRight()) {
1143: strategy = getStrategy();
1144: } else {
1145: strategy = new RightToLeftStrategy(getStrategy());
1146: }
1147: }
1148:
1149: return strategy;
1150: }
1151:
1152: private LayoutStrategy getStrategy() {
1153: LayoutStrategy result;
1154:
1155: if (list.getLayoutOrientation() == JList.VERTICAL) {
1156: result = new VerticalLayoutStategy();
1157: } else if (list.getLayoutOrientation() == JList.VERTICAL_WRAP) {
1158: if (list.getVisibleRowCount() > 0) {
1159: result = new VerticalWrapVisibleSpecifiedLayoutStrategy();
1160: } else {
1161: result = new VerticalWrapVisibleUnspecifiedLayoutStrategy();
1162: }
1163: } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
1164: if (list.getVisibleRowCount() > 0) {
1165: result = new HorizontalWrapVisibleSpecifiedLayoutStrategy();
1166: } else {
1167: result = new HorizontalWrapVisibleUnspecifiedLayoutStrategy();
1168: }
1169: } else {
1170: throw new IllegalArgumentException(Messages
1171: .getString("swing.70")); //$NON-NLS-1$
1172: }
1173:
1174: return result;
1175: }
1176:
1177: public int getNumberOfElements() {
1178: return list.getModel().getSize();
1179: }
1180:
1181: public JList getList() {
1182: return list;
1183: }
1184:
1185: public int getNearestIndex(final Point location) {
1186: int result = -1;
1187: int distance = Integer.MAX_VALUE;
1188: LayoutStrategy s = getLayoutStrategy();
1189: for (int i = 0; i < getNumberOfElements(); i++) {
1190: Rectangle bounds = s.getBounds(i);
1191: int d = getDistance(bounds, location);
1192: if (distance > d) {
1193: distance = d;
1194: result = i;
1195: }
1196: }
1197:
1198: return result;
1199: }
1200:
1201: private void reinitFields() {
1202: insets = list.getInsets(cachedInsets);
1203: }
1204:
1205: private int getMaximumWidth() {
1206: if (list.getFixedCellWidth() >= 0) {
1207: return list.getFixedCellWidth();
1208: }
1209:
1210: int result = -1;
1211: for (int i = 0; i < list.getModel().getSize(); i++) {
1212: Dimension size = getPreferredSize(i);
1213: if (result < size.width) {
1214: result = size.width;
1215: }
1216: }
1217: return result;
1218: }
1219:
1220: private int getMaximumHeight() {
1221: if (list.getFixedCellHeight() >= 0) {
1222: return list.getFixedCellHeight();
1223: }
1224:
1225: int result = -1;
1226: for (int i = 0; i < list.getModel().getSize(); i++) {
1227: Dimension size = getPreferredSize(i);
1228: if (result < size.height) {
1229: result = size.height;
1230: }
1231: }
1232: return result;
1233: }
1234:
1235: private Dimension getPreferredSize(final int i) {
1236: if (list.getCellRenderer() == null) {
1237: return new Dimension(0, 0);
1238: }
1239: return list.getCellRenderer().getListCellRendererComponent(
1240: list, list.getModel().getElementAt(i), i, false,
1241: false).getPreferredSize();
1242: }
1243:
1244: private int getDistance(final Rectangle r, final Point p) {
1245: int dx = 0;
1246: if (p.x < r.x) {
1247: dx = r.x - p.x;
1248: } else if (p.x > r.x + r.width) {
1249: dx = p.x - r.x - r.width;
1250: } else {
1251: dx = 0;
1252: }
1253:
1254: int dy = 0;
1255: if (p.y < r.y) {
1256: dy = r.y - p.y;
1257: } else if (p.y > r.y + r.height) {
1258: dy = p.y - r.y - r.height;
1259: } else {
1260: dy = 0;
1261: }
1262:
1263: return dx + dy;
1264: }
1265: }
1266: }
|