src/share/classes/javax/swing/plaf/basic/BasicListUI.java

Print this page




  42 import java.beans.PropertyChangeEvent;
  43 
  44 import sun.swing.SwingUtilities2;
  45 import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
  46 
  47 /**
  48  * An extensible implementation of {@code ListUI}.
  49  * <p>
  50  * {@code BasicListUI} instances cannot be shared between multiple
  51  * lists.
  52  *
  53  * @author Hans Muller
  54  * @author Philip Milne
  55  * @author Shannon Hickey (drag and drop)
  56  */
  57 public class BasicListUI extends ListUI
  58 {
  59     private static final StringBuilder BASELINE_COMPONENT_KEY =
  60         new StringBuilder("List.baselineComponent");
  61 
  62     protected JList list = null;
  63     protected CellRendererPane rendererPane;
  64 
  65     // Listeners that this UI attaches to the JList
  66     protected FocusListener focusListener;
  67     protected MouseInputListener mouseInputListener;
  68     protected ListSelectionListener listSelectionListener;
  69     protected ListDataListener listDataListener;
  70     protected PropertyChangeListener propertyChangeListener;
  71     private Handler handler;
  72 
  73     protected int[] cellHeights = null;
  74     protected int cellHeight = -1;
  75     protected int cellWidth = -1;
  76     protected int updateLayoutStateNeeded = modelChanged;
  77     /**
  78      * Height of the list. When asked to paint, if the current size of
  79      * the list differs, this will update the layout state.
  80      */
  81     private int listHeight;
  82 


 179 
 180         map.put(TransferHandler.getCutAction().getValue(Action.NAME),
 181                 TransferHandler.getCutAction());
 182         map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
 183                 TransferHandler.getCopyAction());
 184         map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
 185                 TransferHandler.getPasteAction());
 186     }
 187 
 188     /**
 189      * Paint one List cell: compute the relevant state, get the "rubber stamp"
 190      * cell renderer component, and then use the CellRendererPane to paint it.
 191      * Subclasses may want to override this method rather than paint().
 192      *
 193      * @see #paint
 194      */
 195     protected void paintCell(
 196         Graphics g,
 197         int row,
 198         Rectangle rowBounds,
 199         ListCellRenderer cellRenderer,
 200         ListModel dataModel,
 201         ListSelectionModel selModel,
 202         int leadIndex)
 203     {
 204         Object value = dataModel.getElementAt(row);
 205         boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
 206         boolean isSelected = selModel.isSelectedIndex(row);
 207 
 208         Component rendererComponent =
 209             cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
 210 
 211         int cx = rowBounds.x;
 212         int cy = rowBounds.y;
 213         int cw = rowBounds.width;
 214         int ch = rowBounds.height;
 215 
 216         if (isFileList) {
 217             // Shrink renderer to preferred size. This is mostly used on Windows
 218             // where selection is only shown around the file name, instead of
 219             // across the whole list cell.
 220             int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4);


 246     private void paintImpl(Graphics g, JComponent c)
 247     {
 248         switch (layoutOrientation) {
 249         case JList.VERTICAL_WRAP:
 250             if (list.getHeight() != listHeight) {
 251                 updateLayoutStateNeeded |= heightChanged;
 252                 redrawList();
 253             }
 254             break;
 255         case JList.HORIZONTAL_WRAP:
 256             if (list.getWidth() != listWidth) {
 257                 updateLayoutStateNeeded |= widthChanged;
 258                 redrawList();
 259             }
 260             break;
 261         default:
 262             break;
 263         }
 264         maybeUpdateLayoutState();
 265 
 266         ListCellRenderer renderer = list.getCellRenderer();
 267         ListModel dataModel = list.getModel();
 268         ListSelectionModel selModel = list.getSelectionModel();
 269         int size;
 270 
 271         if ((renderer == null) || (size = dataModel.getSize()) == 0) {
 272             return;
 273         }
 274 
 275         // Determine how many columns we need to paint
 276         Rectangle paintBounds = g.getClipBounds();
 277 
 278         int startColumn, endColumn;
 279         if (c.getComponentOrientation().isLeftToRight()) {
 280             startColumn = convertLocationToColumn(paintBounds.x,
 281                                                   paintBounds.y);
 282             endColumn = convertLocationToColumn(paintBounds.x +
 283                                                 paintBounds.width,
 284                                                 paintBounds.y);
 285         } else {
 286             startColumn = convertLocationToColumn(paintBounds.x +
 287                                                 paintBounds.width,


 461         }
 462 
 463         return rect;
 464     }
 465 
 466     /**
 467      * Returns the baseline.
 468      *
 469      * @throws NullPointerException {@inheritDoc}
 470      * @throws IllegalArgumentException {@inheritDoc}
 471      * @see javax.swing.JComponent#getBaseline(int, int)
 472      * @since 1.6
 473      */
 474     public int getBaseline(JComponent c, int width, int height) {
 475         super.getBaseline(c, width, height);
 476         int rowHeight = list.getFixedCellHeight();
 477         UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
 478         Component renderer = (Component)lafDefaults.get(
 479                 BASELINE_COMPONENT_KEY);
 480         if (renderer == null) {
 481             ListCellRenderer lcr = (ListCellRenderer)UIManager.get(

 482                     "List.cellRenderer");
 483 
 484             // fix for 6711072 some LAFs like Nimbus do not provide this
 485             // UIManager key and we should not through a NPE here because of it
 486             if (lcr == null) {
 487                 lcr = new DefaultListCellRenderer();
 488             }
 489             renderer = lcr.getListCellRendererComponent(
 490                     list, "a", -1, false, false);
 491             lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
 492         }
 493         renderer.setFont(list.getFont());
 494         // JList actually has much more complex behavior here.
 495         // If rowHeight != -1 the rowHeight is either the max of all cell
 496         // heights (layout orientation != VERTICAL), or is variable depending
 497         // upon the cell.  We assume a default size.
 498         // We could theoretically query the real renderer, but that would
 499         // not work for an empty model and the results may vary with
 500         // the content.
 501         if (rowHeight == -1) {


 698             list.setTransferHandler(defaultTransferHandler);
 699             // default TransferHandler doesn't support drop
 700             // so we don't want drop handling
 701             if (list.getDropTarget() instanceof UIResource) {
 702                 list.setDropTarget(null);
 703             }
 704         }
 705 
 706         focusListener = createFocusListener();
 707         mouseInputListener = createMouseInputListener();
 708         propertyChangeListener = createPropertyChangeListener();
 709         listSelectionListener = createListSelectionListener();
 710         listDataListener = createListDataListener();
 711 
 712         list.addFocusListener(focusListener);
 713         list.addMouseListener(mouseInputListener);
 714         list.addMouseMotionListener(mouseInputListener);
 715         list.addPropertyChangeListener(propertyChangeListener);
 716         list.addKeyListener(getHandler());
 717 
 718         ListModel model = list.getModel();
 719         if (model != null) {
 720             model.addListDataListener(listDataListener);
 721         }
 722 
 723         ListSelectionModel selectionModel = list.getSelectionModel();
 724         if (selectionModel != null) {
 725             selectionModel.addListSelectionListener(listSelectionListener);
 726         }
 727     }
 728 
 729 
 730     /**
 731      * Removes the listeners from the JList, its model, and its
 732      * selectionModel.  All of the listener fields, are reset to
 733      * null here.  This method is called at uninstallUI() time,
 734      * it should be kept in sync with installListeners.
 735      *
 736      * @see #uninstallUI
 737      * @see #installListeners
 738      */
 739     protected void uninstallListeners()
 740     {
 741         list.removeFocusListener(focusListener);
 742         list.removeMouseListener(mouseInputListener);
 743         list.removeMouseMotionListener(mouseInputListener);
 744         list.removePropertyChangeListener(propertyChangeListener);
 745         list.removeKeyListener(getHandler());
 746 
 747         ListModel model = list.getModel();
 748         if (model != null) {
 749             model.removeListDataListener(listDataListener);
 750         }
 751 
 752         ListSelectionModel selectionModel = list.getSelectionModel();
 753         if (selectionModel != null) {
 754             selectionModel.removeListSelectionListener(listSelectionListener);
 755         }
 756 
 757         focusListener = null;
 758         mouseInputListener  = null;
 759         listSelectionListener = null;
 760         listDataListener = null;
 761         propertyChangeListener = null;
 762         handler = null;
 763     }
 764 
 765 
 766     /**
 767      * Initializes list properties such as font, foreground, and background,
 768      * and adds the CellRendererPane. The font, foreground, and background
 769      * properties are only set if their current value is either null
 770      * or a UIResource, other properties are set if the current
 771      * value is null.
 772      *
 773      * @see #uninstallDefaults
 774      * @see #installUI
 775      * @see CellRendererPane
 776      */
 777     protected void installDefaults()
 778     {
 779         list.setLayout(null);
 780 
 781         LookAndFeel.installBorder(list, "List.border");
 782 
 783         LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");
 784 
 785         LookAndFeel.installProperty(list, "opaque", Boolean.TRUE);
 786 
 787         if (list.getCellRenderer() == null) {
 788             list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer")));


 789         }
 790 
 791         Color sbg = list.getSelectionBackground();
 792         if (sbg == null || sbg instanceof UIResource) {
 793             list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
 794         }
 795 
 796         Color sfg = list.getSelectionForeground();
 797         if (sfg == null || sfg instanceof UIResource) {
 798             list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
 799         }
 800 
 801         Long l = (Long)UIManager.get("List.timeFactor");
 802         timeFactor = (l!=null) ? l.longValue() : 1000L;
 803 
 804         updateIsFileList();
 805     }
 806 
 807     private void updateIsFileList() {
 808         boolean b = Boolean.TRUE.equals(list.getClientProperty("List.isFileList"));


 849         if (list.getCellRenderer() instanceof UIResource) {
 850             list.setCellRenderer(null);
 851         }
 852         if (list.getTransferHandler() instanceof UIResource) {
 853             list.setTransferHandler(null);
 854         }
 855     }
 856 
 857 
 858     /**
 859      * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
 860      * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
 861      * in order.
 862      *
 863      * @see #installDefaults
 864      * @see #installListeners
 865      * @see #installKeyboardActions
 866      */
 867     public void installUI(JComponent c)
 868     {
 869         list = (JList)c;


 870 
 871         layoutOrientation = list.getLayoutOrientation();
 872 
 873         rendererPane = new CellRendererPane();
 874         list.add(rendererPane);
 875 
 876         columnCount = 1;
 877 
 878         updateLayoutStateNeeded = modelChanged;
 879         isLeftToRight = list.getComponentOrientation().isLeftToRight();
 880 
 881         installDefaults();
 882         installListeners();
 883         installKeyboardActions();
 884     }
 885 
 886 
 887     /**
 888      * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
 889      * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>


 908         rendererPane = null;
 909         list = null;
 910     }
 911 
 912 
 913     /**
 914      * Returns a new instance of BasicListUI.  BasicListUI delegates are
 915      * allocated one per JList.
 916      *
 917      * @return A new ListUI implementation for the Windows look and feel.
 918      */
 919     public static ComponentUI createUI(JComponent list) {
 920         return new BasicListUI();
 921     }
 922 
 923 
 924     /**
 925      * {@inheritDoc}
 926      * @throws NullPointerException {@inheritDoc}
 927      */
 928     public int locationToIndex(JList list, Point location) {
 929         maybeUpdateLayoutState();
 930         return convertLocationToModel(location.x, location.y);
 931     }
 932 
 933 
 934     /**
 935      * {@inheritDoc}
 936      */
 937     public Point indexToLocation(JList list, int index) {
 938         maybeUpdateLayoutState();
 939         Rectangle rect = getCellBounds(list, index, index);
 940 
 941         if (rect != null) {
 942             return new Point(rect.x, rect.y);
 943         }
 944         return null;
 945     }
 946 
 947 
 948     /**
 949      * {@inheritDoc}
 950      */
 951     public Rectangle getCellBounds(JList list, int index1, int index2) {
 952         maybeUpdateLayoutState();
 953 
 954         int minIndex = Math.min(index1, index2);
 955         int maxIndex = Math.max(index1, index2);
 956 
 957         if (minIndex >= list.getModel().getSize()) {
 958             return null;
 959         }
 960 
 961         Rectangle minBounds = getCellBounds(list, minIndex);
 962 
 963         if (minBounds == null) {
 964             return null;
 965         }
 966         if (minIndex == maxIndex) {
 967             return minBounds;
 968         }
 969         Rectangle maxBounds = getCellBounds(list, maxIndex);
 970 
 971         if (maxBounds != null) {


 975 
 976                 if (minRow != maxRow) {
 977                     minBounds.x = 0;
 978                     minBounds.width = list.getWidth();
 979                 }
 980             }
 981             else if (minBounds.x != maxBounds.x) {
 982                 // Different columns
 983                 minBounds.y = 0;
 984                 minBounds.height = list.getHeight();
 985             }
 986             minBounds.add(maxBounds);
 987         }
 988         return minBounds;
 989     }
 990 
 991     /**
 992      * Gets the bounds of the specified model index, returning the resulting
 993      * bounds, or null if <code>index</code> is not valid.
 994      */
 995     private Rectangle getCellBounds(JList list, int index) {
 996         maybeUpdateLayoutState();
 997 
 998         int row = convertModelToRow(index);
 999         int column = convertModelToColumn(index);
1000 
1001         if (row == -1 || column == -1) {
1002             return null;
1003         }
1004 
1005         Insets insets = list.getInsets();
1006         int x;
1007         int w = cellWidth;
1008         int y = insets.top;
1009         int h;
1010         switch (layoutOrientation) {
1011         case JList.VERTICAL_WRAP:
1012         case JList.HORIZONTAL_WRAP:
1013             if (isLeftToRight) {
1014                 x = insets.left + column * cellWidth;
1015             } else {


1334         cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
1335 
1336         if (fixedCellHeight != -1) {
1337             cellHeight = fixedCellHeight;
1338             cellHeights = null;
1339         }
1340         else {
1341             cellHeight = -1;
1342             cellHeights = new int[list.getModel().getSize()];
1343         }
1344 
1345         /* If either of  JList fixedCellWidth and fixedCellHeight haven't
1346          * been set, then initialize cellWidth and cellHeights by
1347          * scanning through the entire model.  Note: if the renderer is
1348          * null, we just set cellWidth and cellHeights[*] to zero,
1349          * if they're not set already.
1350          */
1351 
1352         if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
1353 
1354             ListModel dataModel = list.getModel();
1355             int dataModelSize = dataModel.getSize();
1356             ListCellRenderer renderer = list.getCellRenderer();
1357 
1358             if (renderer != null) {
1359                 for(int index = 0; index < dataModelSize; index++) {
1360                     Object value = dataModel.getElementAt(index);
1361                     Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
1362                     rendererPane.add(c);
1363                     Dimension cellSize = c.getPreferredSize();
1364                     if (fixedCellWidth == -1) {
1365                         cellWidth = Math.max(cellSize.width, cellWidth);
1366                     }
1367                     if (fixedCellHeight == -1) {
1368                         cellHeights[index] = cellSize.height;
1369                     }
1370                 }
1371             }
1372             else {
1373                 if (cellWidth == -1) {
1374                     cellWidth = 0;
1375                 }
1376                 if (cellHeights == null) {


1821         private static final String SELECT_ALL = "selectAll";
1822         private static final String CLEAR_SELECTION = "clearSelection";
1823 
1824         // add the lead item to the selection without changing lead or anchor
1825         private static final String ADD_TO_SELECTION = "addToSelection";
1826 
1827         // toggle the selected state of the lead item and move the anchor to it
1828         private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
1829 
1830         // extend the selection to the lead item
1831         private static final String EXTEND_TO = "extendTo";
1832 
1833         // move the anchor to the lead and ensure only that item is selected
1834         private static final String MOVE_SELECTION_TO = "moveSelectionTo";
1835 
1836         Actions(String name) {
1837             super(name);
1838         }
1839         public void actionPerformed(ActionEvent e) {
1840             String name = getName();
1841             JList list = (JList)e.getSource();

1842             BasicListUI ui = (BasicListUI)BasicLookAndFeel.getUIOfType(
1843                      list.getUI(), BasicListUI.class);
1844 
1845             if (name == SELECT_PREVIOUS_COLUMN) {
1846                 changeSelection(list, CHANGE_SELECTION,
1847                                 getNextColumnIndex(list, ui, -1), -1);
1848             }
1849             else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) {
1850                 changeSelection(list, EXTEND_SELECTION,
1851                                 getNextColumnIndex(list, ui, -1), -1);
1852             }
1853             else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) {
1854                 changeSelection(list, CHANGE_LEAD,
1855                                 getNextColumnIndex(list, ui, -1), -1);
1856             }
1857             else if (name == SELECT_NEXT_COLUMN) {
1858                 changeSelection(list, CHANGE_SELECTION,
1859                                 getNextColumnIndex(list, ui, 1), 1);
1860             }
1861             else if (name == SELECT_NEXT_COLUMN_EXTEND) {


1980         public boolean isEnabled(Object c) {
1981             Object name = getName();
1982             if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD ||
1983                     name == SELECT_NEXT_COLUMN_CHANGE_LEAD ||
1984                     name == SELECT_PREVIOUS_ROW_CHANGE_LEAD ||
1985                     name == SELECT_NEXT_ROW_CHANGE_LEAD ||
1986                     name == SELECT_FIRST_ROW_CHANGE_LEAD ||
1987                     name == SELECT_LAST_ROW_CHANGE_LEAD ||
1988                     name == SCROLL_UP_CHANGE_LEAD ||
1989                     name == SCROLL_DOWN_CHANGE_LEAD) {
1990 
1991                 // discontinuous selection actions are only enabled for
1992                 // DefaultListSelectionModel
1993                 return c != null && ((JList)c).getSelectionModel()
1994                                         instanceof DefaultListSelectionModel;
1995             }
1996 
1997             return true;
1998         }
1999 
2000         private void clearSelection(JList list) {
2001             list.clearSelection();
2002         }
2003 
2004         private void selectAll(JList list) {
2005             int size = list.getModel().getSize();
2006             if (size > 0) {
2007                 ListSelectionModel lsm = list.getSelectionModel();
2008                 int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
2009 
2010                 if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
2011                     if (lead == -1) {
2012                         int min = adjustIndex(list.getMinSelectionIndex(), list);
2013                         lead = (min == -1 ? 0 : min);
2014                     }
2015 
2016                     list.setSelectionInterval(lead, lead);
2017                     list.ensureIndexIsVisible(lead);
2018                 } else {
2019                     list.setValueIsAdjusting(true);
2020 
2021                     int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);
2022 
2023                     list.setSelectionInterval(0, size - 1);
2024 
2025                     // this is done to restore the anchor and lead
2026                     SwingUtilities2.setLeadAnchorWithoutSelection(lsm, anchor, lead);
2027 
2028                     list.setValueIsAdjusting(false);
2029                 }
2030             }
2031         }
2032 
2033         private int getNextPageIndex(JList list, int direction) {
2034             if (list.getModel().getSize() == 0) {
2035                 return -1;
2036             }
2037 
2038             int index = -1;
2039             Rectangle visRect = list.getVisibleRect();
2040             ListSelectionModel lsm = list.getSelectionModel();
2041             int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
2042             Rectangle leadRect =
2043                 (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead);
2044 
2045             if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
2046                 list.getVisibleRowCount() <= 0) {
2047                 if (!list.getComponentOrientation().isLeftToRight()) {
2048                     direction = -direction;
2049                 }
2050                 // apply for horizontal scrolling: the step for next
2051                 // page index is number of visible columns
2052                 if (direction < 0) {
2053                     // left


2138                         // go one cell up if last visible cell doesn't fit
2139                         // into adjasted visible rectangle
2140                         if (cellBounds.y + cellBounds.height >
2141                             visRect.y + visRect.height) {
2142                             p.y = cellBounds.y - 1;
2143                             index = list.locationToIndex(p);
2144                             cellBounds = list.getCellBounds(index, index);
2145                         }
2146                         // if index isn't greater then lead
2147                         // try to go to cell next after lead
2148                         if (cellBounds.y <= leadRect.y) {
2149                             p.y = leadRect.y + leadRect.height;
2150                             index = list.locationToIndex(p);
2151                         }
2152                     }
2153                 }
2154             }
2155             return index;
2156         }
2157 
2158         private void changeSelection(JList list, int type,
2159                                      int index, int direction) {
2160             if (index >= 0 && index < list.getModel().getSize()) {
2161                 ListSelectionModel lsm = list.getSelectionModel();
2162 
2163                 // CHANGE_LEAD is only valid with multiple interval selection
2164                 if (type == CHANGE_LEAD &&
2165                         list.getSelectionMode()
2166                             != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) {
2167 
2168                     type = CHANGE_SELECTION;
2169                 }
2170 
2171                 // IMPORTANT - This needs to happen before the index is changed.
2172                 // This is because JFileChooser, which uses JList, also scrolls
2173                 // the selected item into view. If that happens first, then
2174                 // this method becomes a no-op.
2175                 adjustScrollPositionIfNecessary(list, index, direction);
2176 
2177                 if (type == EXTEND_SELECTION) {
2178                     int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);


2181                     }
2182 
2183                     list.setSelectionInterval(anchor, index);
2184                 }
2185                 else if (type == CHANGE_SELECTION) {
2186                     list.setSelectedIndex(index);
2187                 }
2188                 else {
2189                     // casting should be safe since the action is only enabled
2190                     // for DefaultListSelectionModel
2191                     ((DefaultListSelectionModel)lsm).moveLeadSelectionIndex(index);
2192                 }
2193             }
2194         }
2195 
2196         /**
2197          * When scroll down makes selected index the last completely visible
2198          * index. When scroll up makes selected index the first visible index.
2199          * Adjust visible rectangle respect to list's component orientation.
2200          */
2201         private void adjustScrollPositionIfNecessary(JList list, int index,
2202                                                      int direction) {
2203             if (direction == 0) {
2204                 return;
2205             }
2206             Rectangle cellBounds = list.getCellBounds(index, index);
2207             Rectangle visRect = list.getVisibleRect();
2208             if (cellBounds != null && !visRect.contains(cellBounds)) {
2209                 if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
2210                     list.getVisibleRowCount() <= 0) {
2211                     // horizontal
2212                     if (list.getComponentOrientation().isLeftToRight()) {
2213                         if (direction > 0) {
2214                             // right for left-to-right
2215                             int x =Math.max(0,
2216                                 cellBounds.x + cellBounds.width - visRect.width);
2217                             int startIndex =
2218                                 list.locationToIndex(new Point(x, cellBounds.y));
2219                             Rectangle startRect = list.getCellBounds(startIndex,
2220                                                                      startIndex);
2221                             if (startRect.x < x && startRect.x < cellBounds.x) {


2269                                                                  startIndex);
2270                         if (startRect.y < y && startRect.y < cellBounds.y) {
2271                             startRect.y += startRect.height;
2272                             startIndex =
2273                                 list.locationToIndex(startRect.getLocation());
2274                             startRect =
2275                                 list.getCellBounds(startIndex, startIndex);
2276                         }
2277                         cellBounds = startRect;
2278                         cellBounds.height = visRect.height;
2279                     }
2280                     else {
2281                         // adjust height to fit into visible rectangle
2282                         cellBounds.height = Math.min(cellBounds.height, visRect.height);
2283                     }
2284                 }
2285                 list.scrollRectToVisible(cellBounds);
2286             }
2287         }
2288 
2289         private int getNextColumnIndex(JList list, BasicListUI ui,
2290                                        int amount) {
2291             if (list.getLayoutOrientation() != JList.VERTICAL) {
2292                 int index = adjustIndex(list.getLeadSelectionIndex(), list);
2293                 int size = list.getModel().getSize();
2294 
2295                 if (index == -1) {
2296                     return 0;
2297                 } else if (size == 1) {
2298                     // there's only one item so we should select it
2299                     return 0;
2300                 } else if (ui == null || ui.columnCount <= 1) {
2301                     return -1;
2302                 }
2303 
2304                 int column = ui.convertModelToColumn(index);
2305                 int row = ui.convertModelToRow(index);
2306 
2307                 column += amount;
2308                 if (column >= ui.columnCount || column < 0) {
2309                     // No wrapping.
2310                     return -1;
2311                 }
2312                 int maxRowCount = ui.getRowCount(column);
2313                 if (row >= maxRowCount) {
2314                     return -1;
2315                 }
2316                 return ui.getModelIndex(column, row);
2317             }
2318             // Won't change the selection.
2319             return -1;
2320         }
2321 
2322         private int getNextIndex(JList list, BasicListUI ui, int amount) {
2323             int index = adjustIndex(list.getLeadSelectionIndex(), list);
2324             int size = list.getModel().getSize();
2325 
2326             if (index == -1) {
2327                 if (size > 0) {
2328                     if (amount > 0) {
2329                         index = 0;
2330                     }
2331                     else {
2332                         index = size - 1;
2333                     }
2334                 }
2335             } else if (size == 1) {
2336                 // there's only one item so we should select it
2337                 index = 0;
2338             } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
2339                 if (ui != null) {
2340                     index += ui.columnCount * amount;
2341                 }
2342             } else {


2354                           BeforeDrag {
2355         //
2356         // KeyListener
2357         //
2358         private String prefix = "";
2359         private String typedString = "";
2360         private long lastTime = 0L;
2361 
2362         /**
2363          * Invoked when a key has been typed.
2364          *
2365          * Moves the keyboard focus to the first element whose prefix matches the
2366          * sequence of alphanumeric keys pressed by the user with delay less
2367          * than value of <code>timeFactor</code> property (or 1000 milliseconds
2368          * if it is not defined). Subsequent same key presses move the keyboard
2369          * focus to the next object that starts with the same letter until another
2370          * key is pressed, then it is treated as the prefix with appropriate number
2371          * of the same letters followed by first typed another letter.
2372          */
2373         public void keyTyped(KeyEvent e) {
2374             JList src = (JList)e.getSource();
2375             ListModel model = src.getModel();
2376 
2377             if (model.getSize() == 0 || e.isAltDown() ||
2378                     BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
2379                     isNavigationKey(e)) {
2380                 // Nothing to select
2381                 return;
2382             }
2383             boolean startingFromSelection = true;
2384 
2385             char c = e.getKeyChar();
2386 
2387             long time = e.getWhen();
2388             int startIndex = adjustIndex(src.getLeadSelectionIndex(), list);
2389             if (time - lastTime < timeFactor) {
2390                 typedString += c;
2391                 if((prefix.length() == 1) && (c == prefix.charAt(0))) {
2392                     // Subsequent same key presses move the keyboard focus to the next
2393                     // object that starts with the same letter.
2394                     startIndex++;
2395                 } else {


2451         private boolean isNavigationKey(KeyEvent event) {
2452             InputMap inputMap = list.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
2453             KeyStroke key = KeyStroke.getKeyStrokeForEvent(event);
2454 
2455             if (inputMap != null && inputMap.get(key) != null) {
2456                 return true;
2457             }
2458             return false;
2459         }
2460 
2461         //
2462         // PropertyChangeListener
2463         //
2464         public void propertyChange(PropertyChangeEvent e) {
2465             String propertyName = e.getPropertyName();
2466 
2467             /* If the JList.model property changes, remove our listener,
2468              * listDataListener from the old model and add it to the new one.
2469              */
2470             if (propertyName == "model") {
2471                 ListModel oldModel = (ListModel)e.getOldValue();
2472                 ListModel newModel = (ListModel)e.getNewValue();


2473                 if (oldModel != null) {
2474                     oldModel.removeListDataListener(listDataListener);
2475                 }
2476                 if (newModel != null) {
2477                     newModel.addListDataListener(listDataListener);
2478                 }
2479                 updateLayoutStateNeeded |= modelChanged;
2480                 redrawList();
2481             }
2482 
2483             /* If the JList.selectionModel property changes, remove our listener,
2484              * listSelectionListener from the old selectionModel and add it to the new one.
2485              */
2486             else if (propertyName == "selectionModel") {
2487                 ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
2488                 ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
2489                 if (oldModel != null) {
2490                     oldModel.removeListSelectionListener(listSelectionListener);
2491                 }
2492                 if (newModel != null) {


2811                 Rectangle r = getCellBounds(list, leadIndex, leadIndex);
2812                 if (r != null) {
2813                     list.repaint(r.x, r.y, r.width, r.height);
2814                 }
2815             }
2816         }
2817 
2818         /* The focusGained() focusLost() methods run when the JList
2819          * focus changes.
2820          */
2821 
2822         public void focusGained(FocusEvent e) {
2823             repaintCellFocus();
2824         }
2825 
2826         public void focusLost(FocusEvent e) {
2827             repaintCellFocus();
2828         }
2829     }
2830 
2831     private static int adjustIndex(int index, JList list) {
2832         return index < list.getModel().getSize() ? index : -1;
2833     }
2834 
2835     private static final TransferHandler defaultTransferHandler = new ListTransferHandler();
2836 
2837     @SuppressWarnings("serial") // Superclass is a JDK-implementation class
2838     static class ListTransferHandler extends TransferHandler implements UIResource {
2839 
2840         /**
2841          * Create a Transferable to use as the source for a data transfer.
2842          *
2843          * @param c  The component holding the data to be transfered.  This
2844          *  argument is provided to enable sharing of TransferHandlers by
2845          *  multiple components.
2846          * @return  The representation of the data to be transfered.
2847          *
2848          */
2849         protected Transferable createTransferable(JComponent c) {
2850             if (c instanceof JList) {
2851                 JList list = (JList) c;
2852                 Object[] values = list.getSelectedValues();
2853 
2854                 if (values == null || values.length == 0) {
2855                     return null;
2856                 }
2857 
2858                 StringBuilder plainStr = new StringBuilder();
2859                 StringBuilder htmlStr = new StringBuilder();
2860 
2861                 htmlStr.append("<html>\n<body>\n<ul>\n");
2862 
2863                 for (int i = 0; i < values.length; i++) {
2864                     Object obj = values[i];
2865                     String val = ((obj == null) ? "" : obj.toString());
2866                     plainStr.append(val + "\n");
2867                     htmlStr.append("  <li>" + val + "\n");
2868                 }
2869 
2870                 // remove the last newline
2871                 plainStr.deleteCharAt(plainStr.length() - 1);


  42 import java.beans.PropertyChangeEvent;
  43 
  44 import sun.swing.SwingUtilities2;
  45 import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
  46 
  47 /**
  48  * An extensible implementation of {@code ListUI}.
  49  * <p>
  50  * {@code BasicListUI} instances cannot be shared between multiple
  51  * lists.
  52  *
  53  * @author Hans Muller
  54  * @author Philip Milne
  55  * @author Shannon Hickey (drag and drop)
  56  */
  57 public class BasicListUI extends ListUI
  58 {
  59     private static final StringBuilder BASELINE_COMPONENT_KEY =
  60         new StringBuilder("List.baselineComponent");
  61 
  62     protected JList<Object> list = null;
  63     protected CellRendererPane rendererPane;
  64 
  65     // Listeners that this UI attaches to the JList
  66     protected FocusListener focusListener;
  67     protected MouseInputListener mouseInputListener;
  68     protected ListSelectionListener listSelectionListener;
  69     protected ListDataListener listDataListener;
  70     protected PropertyChangeListener propertyChangeListener;
  71     private Handler handler;
  72 
  73     protected int[] cellHeights = null;
  74     protected int cellHeight = -1;
  75     protected int cellWidth = -1;
  76     protected int updateLayoutStateNeeded = modelChanged;
  77     /**
  78      * Height of the list. When asked to paint, if the current size of
  79      * the list differs, this will update the layout state.
  80      */
  81     private int listHeight;
  82 


 179 
 180         map.put(TransferHandler.getCutAction().getValue(Action.NAME),
 181                 TransferHandler.getCutAction());
 182         map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
 183                 TransferHandler.getCopyAction());
 184         map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
 185                 TransferHandler.getPasteAction());
 186     }
 187 
 188     /**
 189      * Paint one List cell: compute the relevant state, get the "rubber stamp"
 190      * cell renderer component, and then use the CellRendererPane to paint it.
 191      * Subclasses may want to override this method rather than paint().
 192      *
 193      * @see #paint
 194      */
 195     protected void paintCell(
 196         Graphics g,
 197         int row,
 198         Rectangle rowBounds,
 199         ListCellRenderer<Object> cellRenderer,
 200         ListModel<Object> dataModel,
 201         ListSelectionModel selModel,
 202         int leadIndex)
 203     {
 204         Object value = dataModel.getElementAt(row);
 205         boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
 206         boolean isSelected = selModel.isSelectedIndex(row);
 207 
 208         Component rendererComponent =
 209             cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
 210 
 211         int cx = rowBounds.x;
 212         int cy = rowBounds.y;
 213         int cw = rowBounds.width;
 214         int ch = rowBounds.height;
 215 
 216         if (isFileList) {
 217             // Shrink renderer to preferred size. This is mostly used on Windows
 218             // where selection is only shown around the file name, instead of
 219             // across the whole list cell.
 220             int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4);


 246     private void paintImpl(Graphics g, JComponent c)
 247     {
 248         switch (layoutOrientation) {
 249         case JList.VERTICAL_WRAP:
 250             if (list.getHeight() != listHeight) {
 251                 updateLayoutStateNeeded |= heightChanged;
 252                 redrawList();
 253             }
 254             break;
 255         case JList.HORIZONTAL_WRAP:
 256             if (list.getWidth() != listWidth) {
 257                 updateLayoutStateNeeded |= widthChanged;
 258                 redrawList();
 259             }
 260             break;
 261         default:
 262             break;
 263         }
 264         maybeUpdateLayoutState();
 265 
 266         ListCellRenderer<Object> renderer = list.getCellRenderer();
 267         ListModel<Object> dataModel = list.getModel();
 268         ListSelectionModel selModel = list.getSelectionModel();
 269         int size;
 270 
 271         if ((renderer == null) || (size = dataModel.getSize()) == 0) {
 272             return;
 273         }
 274 
 275         // Determine how many columns we need to paint
 276         Rectangle paintBounds = g.getClipBounds();
 277 
 278         int startColumn, endColumn;
 279         if (c.getComponentOrientation().isLeftToRight()) {
 280             startColumn = convertLocationToColumn(paintBounds.x,
 281                                                   paintBounds.y);
 282             endColumn = convertLocationToColumn(paintBounds.x +
 283                                                 paintBounds.width,
 284                                                 paintBounds.y);
 285         } else {
 286             startColumn = convertLocationToColumn(paintBounds.x +
 287                                                 paintBounds.width,


 461         }
 462 
 463         return rect;
 464     }
 465 
 466     /**
 467      * Returns the baseline.
 468      *
 469      * @throws NullPointerException {@inheritDoc}
 470      * @throws IllegalArgumentException {@inheritDoc}
 471      * @see javax.swing.JComponent#getBaseline(int, int)
 472      * @since 1.6
 473      */
 474     public int getBaseline(JComponent c, int width, int height) {
 475         super.getBaseline(c, width, height);
 476         int rowHeight = list.getFixedCellHeight();
 477         UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
 478         Component renderer = (Component)lafDefaults.get(
 479                 BASELINE_COMPONENT_KEY);
 480         if (renderer == null) {
 481             @SuppressWarnings("unchecked")
 482             ListCellRenderer<Object> lcr = (ListCellRenderer)UIManager.get(
 483                     "List.cellRenderer");
 484 
 485             // fix for 6711072 some LAFs like Nimbus do not provide this
 486             // UIManager key and we should not through a NPE here because of it
 487             if (lcr == null) {
 488                 lcr = new DefaultListCellRenderer();
 489             }
 490             renderer = lcr.getListCellRendererComponent(
 491                     list, "a", -1, false, false);
 492             lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
 493         }
 494         renderer.setFont(list.getFont());
 495         // JList actually has much more complex behavior here.
 496         // If rowHeight != -1 the rowHeight is either the max of all cell
 497         // heights (layout orientation != VERTICAL), or is variable depending
 498         // upon the cell.  We assume a default size.
 499         // We could theoretically query the real renderer, but that would
 500         // not work for an empty model and the results may vary with
 501         // the content.
 502         if (rowHeight == -1) {


 699             list.setTransferHandler(defaultTransferHandler);
 700             // default TransferHandler doesn't support drop
 701             // so we don't want drop handling
 702             if (list.getDropTarget() instanceof UIResource) {
 703                 list.setDropTarget(null);
 704             }
 705         }
 706 
 707         focusListener = createFocusListener();
 708         mouseInputListener = createMouseInputListener();
 709         propertyChangeListener = createPropertyChangeListener();
 710         listSelectionListener = createListSelectionListener();
 711         listDataListener = createListDataListener();
 712 
 713         list.addFocusListener(focusListener);
 714         list.addMouseListener(mouseInputListener);
 715         list.addMouseMotionListener(mouseInputListener);
 716         list.addPropertyChangeListener(propertyChangeListener);
 717         list.addKeyListener(getHandler());
 718 
 719         ListModel<Object> model = list.getModel();
 720         if (model != null) {
 721             model.addListDataListener(listDataListener);
 722         }
 723 
 724         ListSelectionModel selectionModel = list.getSelectionModel();
 725         if (selectionModel != null) {
 726             selectionModel.addListSelectionListener(listSelectionListener);
 727         }
 728     }
 729 
 730 
 731     /**
 732      * Removes the listeners from the JList, its model, and its
 733      * selectionModel.  All of the listener fields, are reset to
 734      * null here.  This method is called at uninstallUI() time,
 735      * it should be kept in sync with installListeners.
 736      *
 737      * @see #uninstallUI
 738      * @see #installListeners
 739      */
 740     protected void uninstallListeners()
 741     {
 742         list.removeFocusListener(focusListener);
 743         list.removeMouseListener(mouseInputListener);
 744         list.removeMouseMotionListener(mouseInputListener);
 745         list.removePropertyChangeListener(propertyChangeListener);
 746         list.removeKeyListener(getHandler());
 747 
 748         ListModel<Object> model = list.getModel();
 749         if (model != null) {
 750             model.removeListDataListener(listDataListener);
 751         }
 752 
 753         ListSelectionModel selectionModel = list.getSelectionModel();
 754         if (selectionModel != null) {
 755             selectionModel.removeListSelectionListener(listSelectionListener);
 756         }
 757 
 758         focusListener = null;
 759         mouseInputListener  = null;
 760         listSelectionListener = null;
 761         listDataListener = null;
 762         propertyChangeListener = null;
 763         handler = null;
 764     }
 765 
 766 
 767     /**
 768      * Initializes list properties such as font, foreground, and background,
 769      * and adds the CellRendererPane. The font, foreground, and background
 770      * properties are only set if their current value is either null
 771      * or a UIResource, other properties are set if the current
 772      * value is null.
 773      *
 774      * @see #uninstallDefaults
 775      * @see #installUI
 776      * @see CellRendererPane
 777      */
 778     protected void installDefaults()
 779     {
 780         list.setLayout(null);
 781 
 782         LookAndFeel.installBorder(list, "List.border");
 783 
 784         LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");
 785 
 786         LookAndFeel.installProperty(list, "opaque", Boolean.TRUE);
 787 
 788         if (list.getCellRenderer() == null) {
 789             @SuppressWarnings("unchecked")
 790             ListCellRenderer<Object> tmp = (ListCellRenderer)(UIManager.get("List.cellRenderer"));
 791             list.setCellRenderer(tmp);
 792         }
 793 
 794         Color sbg = list.getSelectionBackground();
 795         if (sbg == null || sbg instanceof UIResource) {
 796             list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
 797         }
 798 
 799         Color sfg = list.getSelectionForeground();
 800         if (sfg == null || sfg instanceof UIResource) {
 801             list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
 802         }
 803 
 804         Long l = (Long)UIManager.get("List.timeFactor");
 805         timeFactor = (l!=null) ? l.longValue() : 1000L;
 806 
 807         updateIsFileList();
 808     }
 809 
 810     private void updateIsFileList() {
 811         boolean b = Boolean.TRUE.equals(list.getClientProperty("List.isFileList"));


 852         if (list.getCellRenderer() instanceof UIResource) {
 853             list.setCellRenderer(null);
 854         }
 855         if (list.getTransferHandler() instanceof UIResource) {
 856             list.setTransferHandler(null);
 857         }
 858     }
 859 
 860 
 861     /**
 862      * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
 863      * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
 864      * in order.
 865      *
 866      * @see #installDefaults
 867      * @see #installListeners
 868      * @see #installKeyboardActions
 869      */
 870     public void installUI(JComponent c)
 871     {
 872         @SuppressWarnings("unchecked")
 873         JList<Object> tmp = (JList)c;
 874         list = tmp;
 875 
 876         layoutOrientation = list.getLayoutOrientation();
 877 
 878         rendererPane = new CellRendererPane();
 879         list.add(rendererPane);
 880 
 881         columnCount = 1;
 882 
 883         updateLayoutStateNeeded = modelChanged;
 884         isLeftToRight = list.getComponentOrientation().isLeftToRight();
 885 
 886         installDefaults();
 887         installListeners();
 888         installKeyboardActions();
 889     }
 890 
 891 
 892     /**
 893      * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
 894      * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>


 913         rendererPane = null;
 914         list = null;
 915     }
 916 
 917 
 918     /**
 919      * Returns a new instance of BasicListUI.  BasicListUI delegates are
 920      * allocated one per JList.
 921      *
 922      * @return A new ListUI implementation for the Windows look and feel.
 923      */
 924     public static ComponentUI createUI(JComponent list) {
 925         return new BasicListUI();
 926     }
 927 
 928 
 929     /**
 930      * {@inheritDoc}
 931      * @throws NullPointerException {@inheritDoc}
 932      */
 933     public int locationToIndex(JList<?> list, Point location) {
 934         maybeUpdateLayoutState();
 935         return convertLocationToModel(location.x, location.y);
 936     }
 937 
 938 
 939     /**
 940      * {@inheritDoc}
 941      */
 942     public Point indexToLocation(JList<?> list, int index) {
 943         maybeUpdateLayoutState();
 944         Rectangle rect = getCellBounds(list, index, index);
 945 
 946         if (rect != null) {
 947             return new Point(rect.x, rect.y);
 948         }
 949         return null;
 950     }
 951 
 952 
 953     /**
 954      * {@inheritDoc}
 955      */
 956     public Rectangle getCellBounds(JList<?> list, int index1, int index2) {
 957         maybeUpdateLayoutState();
 958 
 959         int minIndex = Math.min(index1, index2);
 960         int maxIndex = Math.max(index1, index2);
 961 
 962         if (minIndex >= list.getModel().getSize()) {
 963             return null;
 964         }
 965 
 966         Rectangle minBounds = getCellBounds(list, minIndex);
 967 
 968         if (minBounds == null) {
 969             return null;
 970         }
 971         if (minIndex == maxIndex) {
 972             return minBounds;
 973         }
 974         Rectangle maxBounds = getCellBounds(list, maxIndex);
 975 
 976         if (maxBounds != null) {


 980 
 981                 if (minRow != maxRow) {
 982                     minBounds.x = 0;
 983                     minBounds.width = list.getWidth();
 984                 }
 985             }
 986             else if (minBounds.x != maxBounds.x) {
 987                 // Different columns
 988                 minBounds.y = 0;
 989                 minBounds.height = list.getHeight();
 990             }
 991             minBounds.add(maxBounds);
 992         }
 993         return minBounds;
 994     }
 995 
 996     /**
 997      * Gets the bounds of the specified model index, returning the resulting
 998      * bounds, or null if <code>index</code> is not valid.
 999      */
1000     private Rectangle getCellBounds(JList<?> list, int index) {
1001         maybeUpdateLayoutState();
1002 
1003         int row = convertModelToRow(index);
1004         int column = convertModelToColumn(index);
1005 
1006         if (row == -1 || column == -1) {
1007             return null;
1008         }
1009 
1010         Insets insets = list.getInsets();
1011         int x;
1012         int w = cellWidth;
1013         int y = insets.top;
1014         int h;
1015         switch (layoutOrientation) {
1016         case JList.VERTICAL_WRAP:
1017         case JList.HORIZONTAL_WRAP:
1018             if (isLeftToRight) {
1019                 x = insets.left + column * cellWidth;
1020             } else {


1339         cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
1340 
1341         if (fixedCellHeight != -1) {
1342             cellHeight = fixedCellHeight;
1343             cellHeights = null;
1344         }
1345         else {
1346             cellHeight = -1;
1347             cellHeights = new int[list.getModel().getSize()];
1348         }
1349 
1350         /* If either of  JList fixedCellWidth and fixedCellHeight haven't
1351          * been set, then initialize cellWidth and cellHeights by
1352          * scanning through the entire model.  Note: if the renderer is
1353          * null, we just set cellWidth and cellHeights[*] to zero,
1354          * if they're not set already.
1355          */
1356 
1357         if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
1358 
1359             ListModel<Object> dataModel = list.getModel();
1360             int dataModelSize = dataModel.getSize();
1361             ListCellRenderer<Object> renderer = list.getCellRenderer();
1362 
1363             if (renderer != null) {
1364                 for(int index = 0; index < dataModelSize; index++) {
1365                     Object value = dataModel.getElementAt(index);
1366                     Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
1367                     rendererPane.add(c);
1368                     Dimension cellSize = c.getPreferredSize();
1369                     if (fixedCellWidth == -1) {
1370                         cellWidth = Math.max(cellSize.width, cellWidth);
1371                     }
1372                     if (fixedCellHeight == -1) {
1373                         cellHeights[index] = cellSize.height;
1374                     }
1375                 }
1376             }
1377             else {
1378                 if (cellWidth == -1) {
1379                     cellWidth = 0;
1380                 }
1381                 if (cellHeights == null) {


1826         private static final String SELECT_ALL = "selectAll";
1827         private static final String CLEAR_SELECTION = "clearSelection";
1828 
1829         // add the lead item to the selection without changing lead or anchor
1830         private static final String ADD_TO_SELECTION = "addToSelection";
1831 
1832         // toggle the selected state of the lead item and move the anchor to it
1833         private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
1834 
1835         // extend the selection to the lead item
1836         private static final String EXTEND_TO = "extendTo";
1837 
1838         // move the anchor to the lead and ensure only that item is selected
1839         private static final String MOVE_SELECTION_TO = "moveSelectionTo";
1840 
1841         Actions(String name) {
1842             super(name);
1843         }
1844         public void actionPerformed(ActionEvent e) {
1845             String name = getName();
1846             @SuppressWarnings("unchecked")
1847             JList<Object> list = (JList)e.getSource();
1848             BasicListUI ui = (BasicListUI)BasicLookAndFeel.getUIOfType(
1849                      list.getUI(), BasicListUI.class);
1850 
1851             if (name == SELECT_PREVIOUS_COLUMN) {
1852                 changeSelection(list, CHANGE_SELECTION,
1853                                 getNextColumnIndex(list, ui, -1), -1);
1854             }
1855             else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) {
1856                 changeSelection(list, EXTEND_SELECTION,
1857                                 getNextColumnIndex(list, ui, -1), -1);
1858             }
1859             else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) {
1860                 changeSelection(list, CHANGE_LEAD,
1861                                 getNextColumnIndex(list, ui, -1), -1);
1862             }
1863             else if (name == SELECT_NEXT_COLUMN) {
1864                 changeSelection(list, CHANGE_SELECTION,
1865                                 getNextColumnIndex(list, ui, 1), 1);
1866             }
1867             else if (name == SELECT_NEXT_COLUMN_EXTEND) {


1986         public boolean isEnabled(Object c) {
1987             Object name = getName();
1988             if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD ||
1989                     name == SELECT_NEXT_COLUMN_CHANGE_LEAD ||
1990                     name == SELECT_PREVIOUS_ROW_CHANGE_LEAD ||
1991                     name == SELECT_NEXT_ROW_CHANGE_LEAD ||
1992                     name == SELECT_FIRST_ROW_CHANGE_LEAD ||
1993                     name == SELECT_LAST_ROW_CHANGE_LEAD ||
1994                     name == SCROLL_UP_CHANGE_LEAD ||
1995                     name == SCROLL_DOWN_CHANGE_LEAD) {
1996 
1997                 // discontinuous selection actions are only enabled for
1998                 // DefaultListSelectionModel
1999                 return c != null && ((JList)c).getSelectionModel()
2000                                         instanceof DefaultListSelectionModel;
2001             }
2002 
2003             return true;
2004         }
2005 
2006         private void clearSelection(JList<?> list) {
2007             list.clearSelection();
2008         }
2009 
2010         private void selectAll(JList<?> list) {
2011             int size = list.getModel().getSize();
2012             if (size > 0) {
2013                 ListSelectionModel lsm = list.getSelectionModel();
2014                 int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
2015 
2016                 if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
2017                     if (lead == -1) {
2018                         int min = adjustIndex(list.getMinSelectionIndex(), list);
2019                         lead = (min == -1 ? 0 : min);
2020                     }
2021 
2022                     list.setSelectionInterval(lead, lead);
2023                     list.ensureIndexIsVisible(lead);
2024                 } else {
2025                     list.setValueIsAdjusting(true);
2026 
2027                     int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);
2028 
2029                     list.setSelectionInterval(0, size - 1);
2030 
2031                     // this is done to restore the anchor and lead
2032                     SwingUtilities2.setLeadAnchorWithoutSelection(lsm, anchor, lead);
2033 
2034                     list.setValueIsAdjusting(false);
2035                 }
2036             }
2037         }
2038 
2039         private int getNextPageIndex(JList<?> list, int direction) {
2040             if (list.getModel().getSize() == 0) {
2041                 return -1;
2042             }
2043 
2044             int index = -1;
2045             Rectangle visRect = list.getVisibleRect();
2046             ListSelectionModel lsm = list.getSelectionModel();
2047             int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
2048             Rectangle leadRect =
2049                 (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead);
2050 
2051             if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
2052                 list.getVisibleRowCount() <= 0) {
2053                 if (!list.getComponentOrientation().isLeftToRight()) {
2054                     direction = -direction;
2055                 }
2056                 // apply for horizontal scrolling: the step for next
2057                 // page index is number of visible columns
2058                 if (direction < 0) {
2059                     // left


2144                         // go one cell up if last visible cell doesn't fit
2145                         // into adjasted visible rectangle
2146                         if (cellBounds.y + cellBounds.height >
2147                             visRect.y + visRect.height) {
2148                             p.y = cellBounds.y - 1;
2149                             index = list.locationToIndex(p);
2150                             cellBounds = list.getCellBounds(index, index);
2151                         }
2152                         // if index isn't greater then lead
2153                         // try to go to cell next after lead
2154                         if (cellBounds.y <= leadRect.y) {
2155                             p.y = leadRect.y + leadRect.height;
2156                             index = list.locationToIndex(p);
2157                         }
2158                     }
2159                 }
2160             }
2161             return index;
2162         }
2163 
2164         private void changeSelection(JList<?> list, int type,
2165                                      int index, int direction) {
2166             if (index >= 0 && index < list.getModel().getSize()) {
2167                 ListSelectionModel lsm = list.getSelectionModel();
2168 
2169                 // CHANGE_LEAD is only valid with multiple interval selection
2170                 if (type == CHANGE_LEAD &&
2171                         list.getSelectionMode()
2172                             != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) {
2173 
2174                     type = CHANGE_SELECTION;
2175                 }
2176 
2177                 // IMPORTANT - This needs to happen before the index is changed.
2178                 // This is because JFileChooser, which uses JList, also scrolls
2179                 // the selected item into view. If that happens first, then
2180                 // this method becomes a no-op.
2181                 adjustScrollPositionIfNecessary(list, index, direction);
2182 
2183                 if (type == EXTEND_SELECTION) {
2184                     int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);


2187                     }
2188 
2189                     list.setSelectionInterval(anchor, index);
2190                 }
2191                 else if (type == CHANGE_SELECTION) {
2192                     list.setSelectedIndex(index);
2193                 }
2194                 else {
2195                     // casting should be safe since the action is only enabled
2196                     // for DefaultListSelectionModel
2197                     ((DefaultListSelectionModel)lsm).moveLeadSelectionIndex(index);
2198                 }
2199             }
2200         }
2201 
2202         /**
2203          * When scroll down makes selected index the last completely visible
2204          * index. When scroll up makes selected index the first visible index.
2205          * Adjust visible rectangle respect to list's component orientation.
2206          */
2207         private void adjustScrollPositionIfNecessary(JList<?> list, int index,
2208                                                      int direction) {
2209             if (direction == 0) {
2210                 return;
2211             }
2212             Rectangle cellBounds = list.getCellBounds(index, index);
2213             Rectangle visRect = list.getVisibleRect();
2214             if (cellBounds != null && !visRect.contains(cellBounds)) {
2215                 if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
2216                     list.getVisibleRowCount() <= 0) {
2217                     // horizontal
2218                     if (list.getComponentOrientation().isLeftToRight()) {
2219                         if (direction > 0) {
2220                             // right for left-to-right
2221                             int x =Math.max(0,
2222                                 cellBounds.x + cellBounds.width - visRect.width);
2223                             int startIndex =
2224                                 list.locationToIndex(new Point(x, cellBounds.y));
2225                             Rectangle startRect = list.getCellBounds(startIndex,
2226                                                                      startIndex);
2227                             if (startRect.x < x && startRect.x < cellBounds.x) {


2275                                                                  startIndex);
2276                         if (startRect.y < y && startRect.y < cellBounds.y) {
2277                             startRect.y += startRect.height;
2278                             startIndex =
2279                                 list.locationToIndex(startRect.getLocation());
2280                             startRect =
2281                                 list.getCellBounds(startIndex, startIndex);
2282                         }
2283                         cellBounds = startRect;
2284                         cellBounds.height = visRect.height;
2285                     }
2286                     else {
2287                         // adjust height to fit into visible rectangle
2288                         cellBounds.height = Math.min(cellBounds.height, visRect.height);
2289                     }
2290                 }
2291                 list.scrollRectToVisible(cellBounds);
2292             }
2293         }
2294 
2295         private int getNextColumnIndex(JList<?> list, BasicListUI ui,
2296                                        int amount) {
2297             if (list.getLayoutOrientation() != JList.VERTICAL) {
2298                 int index = adjustIndex(list.getLeadSelectionIndex(), list);
2299                 int size = list.getModel().getSize();
2300 
2301                 if (index == -1) {
2302                     return 0;
2303                 } else if (size == 1) {
2304                     // there's only one item so we should select it
2305                     return 0;
2306                 } else if (ui == null || ui.columnCount <= 1) {
2307                     return -1;
2308                 }
2309 
2310                 int column = ui.convertModelToColumn(index);
2311                 int row = ui.convertModelToRow(index);
2312 
2313                 column += amount;
2314                 if (column >= ui.columnCount || column < 0) {
2315                     // No wrapping.
2316                     return -1;
2317                 }
2318                 int maxRowCount = ui.getRowCount(column);
2319                 if (row >= maxRowCount) {
2320                     return -1;
2321                 }
2322                 return ui.getModelIndex(column, row);
2323             }
2324             // Won't change the selection.
2325             return -1;
2326         }
2327 
2328         private int getNextIndex(JList<?> list, BasicListUI ui, int amount) {
2329             int index = adjustIndex(list.getLeadSelectionIndex(), list);
2330             int size = list.getModel().getSize();
2331 
2332             if (index == -1) {
2333                 if (size > 0) {
2334                     if (amount > 0) {
2335                         index = 0;
2336                     }
2337                     else {
2338                         index = size - 1;
2339                     }
2340                 }
2341             } else if (size == 1) {
2342                 // there's only one item so we should select it
2343                 index = 0;
2344             } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
2345                 if (ui != null) {
2346                     index += ui.columnCount * amount;
2347                 }
2348             } else {


2360                           BeforeDrag {
2361         //
2362         // KeyListener
2363         //
2364         private String prefix = "";
2365         private String typedString = "";
2366         private long lastTime = 0L;
2367 
2368         /**
2369          * Invoked when a key has been typed.
2370          *
2371          * Moves the keyboard focus to the first element whose prefix matches the
2372          * sequence of alphanumeric keys pressed by the user with delay less
2373          * than value of <code>timeFactor</code> property (or 1000 milliseconds
2374          * if it is not defined). Subsequent same key presses move the keyboard
2375          * focus to the next object that starts with the same letter until another
2376          * key is pressed, then it is treated as the prefix with appropriate number
2377          * of the same letters followed by first typed another letter.
2378          */
2379         public void keyTyped(KeyEvent e) {
2380             JList<?> src = (JList)e.getSource();
2381             ListModel<?> model = src.getModel();
2382 
2383             if (model.getSize() == 0 || e.isAltDown() ||
2384                     BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
2385                     isNavigationKey(e)) {
2386                 // Nothing to select
2387                 return;
2388             }
2389             boolean startingFromSelection = true;
2390 
2391             char c = e.getKeyChar();
2392 
2393             long time = e.getWhen();
2394             int startIndex = adjustIndex(src.getLeadSelectionIndex(), list);
2395             if (time - lastTime < timeFactor) {
2396                 typedString += c;
2397                 if((prefix.length() == 1) && (c == prefix.charAt(0))) {
2398                     // Subsequent same key presses move the keyboard focus to the next
2399                     // object that starts with the same letter.
2400                     startIndex++;
2401                 } else {


2457         private boolean isNavigationKey(KeyEvent event) {
2458             InputMap inputMap = list.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
2459             KeyStroke key = KeyStroke.getKeyStrokeForEvent(event);
2460 
2461             if (inputMap != null && inputMap.get(key) != null) {
2462                 return true;
2463             }
2464             return false;
2465         }
2466 
2467         //
2468         // PropertyChangeListener
2469         //
2470         public void propertyChange(PropertyChangeEvent e) {
2471             String propertyName = e.getPropertyName();
2472 
2473             /* If the JList.model property changes, remove our listener,
2474              * listDataListener from the old model and add it to the new one.
2475              */
2476             if (propertyName == "model") {
2477                 @SuppressWarnings("unchecked")
2478                 ListModel<?> oldModel = (ListModel)e.getOldValue();
2479                 @SuppressWarnings("unchecked")
2480                 ListModel<?> newModel = (ListModel)e.getNewValue();
2481                 if (oldModel != null) {
2482                     oldModel.removeListDataListener(listDataListener);
2483                 }
2484                 if (newModel != null) {
2485                     newModel.addListDataListener(listDataListener);
2486                 }
2487                 updateLayoutStateNeeded |= modelChanged;
2488                 redrawList();
2489             }
2490 
2491             /* If the JList.selectionModel property changes, remove our listener,
2492              * listSelectionListener from the old selectionModel and add it to the new one.
2493              */
2494             else if (propertyName == "selectionModel") {
2495                 ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
2496                 ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
2497                 if (oldModel != null) {
2498                     oldModel.removeListSelectionListener(listSelectionListener);
2499                 }
2500                 if (newModel != null) {


2819                 Rectangle r = getCellBounds(list, leadIndex, leadIndex);
2820                 if (r != null) {
2821                     list.repaint(r.x, r.y, r.width, r.height);
2822                 }
2823             }
2824         }
2825 
2826         /* The focusGained() focusLost() methods run when the JList
2827          * focus changes.
2828          */
2829 
2830         public void focusGained(FocusEvent e) {
2831             repaintCellFocus();
2832         }
2833 
2834         public void focusLost(FocusEvent e) {
2835             repaintCellFocus();
2836         }
2837     }
2838 
2839     private static int adjustIndex(int index, JList<?> list) {
2840         return index < list.getModel().getSize() ? index : -1;
2841     }
2842 
2843     private static final TransferHandler defaultTransferHandler = new ListTransferHandler();
2844 
2845     @SuppressWarnings("serial") // Superclass is a JDK-implementation class
2846     static class ListTransferHandler extends TransferHandler implements UIResource {
2847 
2848         /**
2849          * Create a Transferable to use as the source for a data transfer.
2850          *
2851          * @param c  The component holding the data to be transfered.  This
2852          *  argument is provided to enable sharing of TransferHandlers by
2853          *  multiple components.
2854          * @return  The representation of the data to be transfered.
2855          *
2856          */
2857         protected Transferable createTransferable(JComponent c) {
2858             if (c instanceof JList) {
2859                 JList<?> list = (JList) c;
2860                 Object[] values = list.getSelectedValues();
2861 
2862                 if (values == null || values.length == 0) {
2863                     return null;
2864                 }
2865 
2866                 StringBuilder plainStr = new StringBuilder();
2867                 StringBuilder htmlStr = new StringBuilder();
2868 
2869                 htmlStr.append("<html>\n<body>\n<ul>\n");
2870 
2871                 for (int i = 0; i < values.length; i++) {
2872                     Object obj = values[i];
2873                     String val = ((obj == null) ? "" : obj.toString());
2874                     plainStr.append(val + "\n");
2875                     htmlStr.append("  <li>" + val + "\n");
2876                 }
2877 
2878                 // remove the last newline
2879                 plainStr.deleteCharAt(plainStr.length() - 1);