History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicListUI.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicListUI.java+2880−0
@@ -0,0 +1,2880 @@
1+/*
2+ * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package javax.swing.plaf.basic;
27+
28+import sun.swing.DefaultLookup;
29+import sun.swing.UIAction;
30+
31+import javax.swing.*;
32+import javax.swing.event.*;
33+import javax.swing.plaf.*;
34+import javax.swing.text.Position;
35+
36+import java.awt.*;
37+import java.awt.event.*;
38+import java.awt.datatransfer.Transferable;
39+import java.awt.geom.Point2D;
40+
41+import java.beans.PropertyChangeListener;
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+
83+ /**
84+ * Width of the list. When asked to paint, if the current size of
85+ * the list differs, this will update the layout state.
86+ */
87+ private int listWidth;
88+
89+ /**
90+ * The layout orientation of the list.
91+ */
92+ private int layoutOrientation;
93+
94+ // Following ivars are used if the list is laying out horizontally
95+
96+ /**
97+ * Number of columns to create.
98+ */
99+ private int columnCount;
100+ /**
101+ * Preferred height to make the list, this is only used if the
102+ * the list is layed out horizontally.
103+ */
104+ private int preferredHeight;
105+ /**
106+ * Number of rows per column. This is only used if the row height is
107+ * fixed.
108+ */
109+ private int rowsPerColumn;
110+
111+ /**
112+ * The time factor to treate the series of typed alphanumeric key
113+ * as prefix for first letter navigation.
114+ */
115+ private long timeFactor = 1000L;
116+
117+ /**
118+ * Local cache of JList's client property "List.isFileList"
119+ */
120+ private boolean isFileList = false;
121+
122+ /**
123+ * Local cache of JList's component orientation property
124+ */
125+ private boolean isLeftToRight = true;
126+
127+ /* The bits below define JList property changes that affect layout.
128+ * When one of these properties changes we set a bit in
129+ * updateLayoutStateNeeded. The change is dealt with lazily, see
130+ * maybeUpdateLayoutState. Changes to the JLists model, e.g. the
131+ * models length changed, are handled similarly, see DataListener.
132+ */
133+
134+ protected final static int modelChanged = 1 << 0;
135+ protected final static int selectionModelChanged = 1 << 1;
136+ protected final static int fontChanged = 1 << 2;
137+ protected final static int fixedCellWidthChanged = 1 << 3;
138+ protected final static int fixedCellHeightChanged = 1 << 4;
139+ protected final static int prototypeCellValueChanged = 1 << 5;
140+ protected final static int cellRendererChanged = 1 << 6;
141+ private final static int layoutOrientationChanged = 1 << 7;
142+ private final static int heightChanged = 1 << 8;
143+ private final static int widthChanged = 1 << 9;
144+ private final static int componentOrientationChanged = 1 << 10;
145+
146+ private static final int DROP_LINE_THICKNESS = 2;
147+
148+ static void loadActionMap(LazyActionMap map) {
149+ map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN));
150+ map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND));
151+ map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD));
152+ map.put(new Actions(Actions.SELECT_NEXT_COLUMN));
153+ map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND));
154+ map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD));
155+ map.put(new Actions(Actions.SELECT_PREVIOUS_ROW));
156+ map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND));
157+ map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD));
158+ map.put(new Actions(Actions.SELECT_NEXT_ROW));
159+ map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND));
160+ map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD));
161+ map.put(new Actions(Actions.SELECT_FIRST_ROW));
162+ map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND));
163+ map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD));
164+ map.put(new Actions(Actions.SELECT_LAST_ROW));
165+ map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND));
166+ map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD));
167+ map.put(new Actions(Actions.SCROLL_UP));
168+ map.put(new Actions(Actions.SCROLL_UP_EXTEND));
169+ map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD));
170+ map.put(new Actions(Actions.SCROLL_DOWN));
171+ map.put(new Actions(Actions.SCROLL_DOWN_EXTEND));
172+ map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD));
173+ map.put(new Actions(Actions.SELECT_ALL));
174+ map.put(new Actions(Actions.CLEAR_SELECTION));
175+ map.put(new Actions(Actions.ADD_TO_SELECTION));
176+ map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
177+ map.put(new Actions(Actions.EXTEND_TO));
178+ map.put(new Actions(Actions.MOVE_SELECTION_TO));
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);
221+ if (!isLeftToRight) {
222+ cx += (cw - w);
223+ }
224+ cw = w;
225+ }
226+
227+ rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
228+ }
229+
230+
231+ /**
232+ * Paint the rows that intersect the Graphics objects clipRect. This
233+ * method calls paintCell as necessary. Subclasses
234+ * may want to override these methods.
235+ *
236+ * @see #paintCell
237+ */
238+ public void paint(Graphics g, JComponent c) {
239+ Shape clip = g.getClip();
240+ paintImpl(g, c);
241+ g.setClip(clip);
242+
243+ paintDropLine(g);
244+ }
245+
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,
288+ paintBounds.y);
289+ endColumn = convertLocationToColumn(paintBounds.x,
290+ paintBounds.y);
291+ }
292+ int maxY = paintBounds.y + paintBounds.height;
293+ int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list);
294+ int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ?
295+ columnCount : 1;
296+
297+
298+ for (int colCounter = startColumn; colCounter <= endColumn;
299+ colCounter++) {
300+ // And then how many rows in this columnn
301+ int row = convertLocationToRowInColumn(paintBounds.y, colCounter);
302+ int rowCount = getRowCount(colCounter);
303+ int index = getModelIndex(colCounter, row);
304+ Rectangle rowBounds = getCellBounds(list, index, index);
305+
306+ if (rowBounds == null) {
307+ // Not valid, bail!
308+ return;
309+ }
310+ while (row < rowCount && rowBounds.y < maxY &&
311+ index < size) {
312+ rowBounds.height = getHeight(colCounter, row);
313+ g.setClip(rowBounds.x, rowBounds.y, rowBounds.width,
314+ rowBounds.height);
315+ g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width,
316+ paintBounds.height);
317+ paintCell(g, index, rowBounds, renderer, dataModel, selModel,
318+ leadIndex);
319+ rowBounds.y += rowBounds.height;
320+ index += rowIncrement;
321+ row++;
322+ }
323+ }
324+ // Empty out the renderer pane, allowing renderers to be gc'ed.
325+ rendererPane.removeAll();
326+ }
327+
328+ private void paintDropLine(Graphics g) {
329+ JList.DropLocation loc = list.getDropLocation();
330+ if (loc == null || !loc.isInsert()) {
331+ return;
332+ }
333+
334+ Color c = DefaultLookup.getColor(list, this, "List.dropLineColor", null);
335+ if (c != null) {
336+ g.setColor(c);
337+ Rectangle rect = getDropLineRect(loc);
338+ g.fillRect(rect.x, rect.y, rect.width, rect.height);
339+ }
340+ }
341+
342+ private Rectangle getDropLineRect(JList.DropLocation loc) {
343+ int size = list.getModel().getSize();
344+
345+ if (size == 0) {
346+ Insets insets = list.getInsets();
347+ if (layoutOrientation == JList.HORIZONTAL_WRAP) {
348+ if (isLeftToRight) {
349+ return new Rectangle(insets.left, insets.top, DROP_LINE_THICKNESS, 20);
350+ } else {
351+ return new Rectangle(list.getWidth() - DROP_LINE_THICKNESS - insets.right,
352+ insets.top, DROP_LINE_THICKNESS, 20);
353+ }
354+ } else {
355+ return new Rectangle(insets.left, insets.top,
356+ list.getWidth() - insets.left - insets.right,
357+ DROP_LINE_THICKNESS);
358+ }
359+ }
360+
361+ Rectangle rect = null;
362+ int index = loc.getIndex();
363+ boolean decr = false;
364+
365+ if (layoutOrientation == JList.HORIZONTAL_WRAP) {
366+ if (index == size) {
367+ decr = true;
368+ } else if (index != 0 && convertModelToRow(index)
369+ != convertModelToRow(index - 1)) {
370+
371+ Rectangle prev = getCellBounds(list, index - 1);
372+ Rectangle me = getCellBounds(list, index);
373+ Point p = loc.getDropPoint();
374+
375+ if (isLeftToRight) {
376+ decr = Point2D.distance(prev.x + prev.width,
377+ prev.y + (int)(prev.height / 2.0),
378+ p.x, p.y)
379+ < Point2D.distance(me.x,
380+ me.y + (int)(me.height / 2.0),
381+ p.x, p.y);
382+ } else {
383+ decr = Point2D.distance(prev.x,
384+ prev.y + (int)(prev.height / 2.0),
385+ p.x, p.y)
386+ < Point2D.distance(me.x + me.width,
387+ me.y + (int)(prev.height / 2.0),
388+ p.x, p.y);
389+ }
390+ }
391+
392+ if (decr) {
393+ index--;
394+ rect = getCellBounds(list, index);
395+ if (isLeftToRight) {
396+ rect.x += rect.width;
397+ } else {
398+ rect.x -= DROP_LINE_THICKNESS;
399+ }
400+ } else {
401+ rect = getCellBounds(list, index);
402+ if (!isLeftToRight) {
403+ rect.x += rect.width - DROP_LINE_THICKNESS;
404+ }
405+ }
406+
407+ if (rect.x >= list.getWidth()) {
408+ rect.x = list.getWidth() - DROP_LINE_THICKNESS;
409+ } else if (rect.x < 0) {
410+ rect.x = 0;
411+ }
412+
413+ rect.width = DROP_LINE_THICKNESS;
414+ } else if (layoutOrientation == JList.VERTICAL_WRAP) {
415+ if (index == size) {
416+ index--;
417+ rect = getCellBounds(list, index);
418+ rect.y += rect.height;
419+ } else if (index != 0 && convertModelToColumn(index)
420+ != convertModelToColumn(index - 1)) {
421+
422+ Rectangle prev = getCellBounds(list, index - 1);
423+ Rectangle me = getCellBounds(list, index);
424+ Point p = loc.getDropPoint();
425+ if (Point2D.distance(prev.x + (int)(prev.width / 2.0),
426+ prev.y + prev.height,
427+ p.x, p.y)
428+ < Point2D.distance(me.x + (int)(me.width / 2.0),
429+ me.y,
430+ p.x, p.y)) {
431+
432+ index--;
433+ rect = getCellBounds(list, index);
434+ rect.y += rect.height;
435+ } else {
436+ rect = getCellBounds(list, index);
437+ }
438+ } else {
439+ rect = getCellBounds(list, index);
440+ }
441+
442+ if (rect.y >= list.getHeight()) {
443+ rect.y = list.getHeight() - DROP_LINE_THICKNESS;
444+ }
445+
446+ rect.height = DROP_LINE_THICKNESS;
447+ } else {
448+ if (index == size) {
449+ index--;
450+ rect = getCellBounds(list, index);
451+ rect.y += rect.height;
452+ } else {
453+ rect = getCellBounds(list, index);
454+ }
455+
456+ if (rect.y >= list.getHeight()) {
457+ rect.y = list.getHeight() - DROP_LINE_THICKNESS;
458+ }
459+
460+ rect.height = DROP_LINE_THICKNESS;
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) {
502+ rowHeight = renderer.getPreferredSize().height;
503+ }
504+ return renderer.getBaseline(Integer.MAX_VALUE, rowHeight) +
505+ list.getInsets().top;
506+ }
507+
508+ /**
509+ * Returns an enum indicating how the baseline of the component
510+ * changes as the size changes.
511+ *
512+ * @throws NullPointerException {@inheritDoc}
513+ * @see javax.swing.JComponent#getBaseline(int, int)
514+ * @since 1.6
515+ */
516+ public Component.BaselineResizeBehavior getBaselineResizeBehavior(
517+ JComponent c) {
518+ super.getBaselineResizeBehavior(c);
519+ return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
520+ }
521+
522+ /**
523+ * The preferredSize of the list depends upon the layout orientation.
524+ * <table summary="Describes the preferred size for each layout orientation">
525+ * <tr><th>Layout Orientation</th><th>Preferred Size</th></tr>
526+ * <tr>
527+ * <td>JList.VERTICAL
528+ * <td>The preferredSize of the list is total height of the rows
529+ * and the maximum width of the cells. If JList.fixedCellHeight
530+ * is specified then the total height of the rows is just
531+ * (cellVerticalMargins + fixedCellHeight) * model.getSize() where
532+ * rowVerticalMargins is the space we allocate for drawing
533+ * the yellow focus outline. Similarly if fixedCellWidth is
534+ * specified then we just use that.
535+ * </td>
536+ * <tr>
537+ * <td>JList.VERTICAL_WRAP
538+ * <td>If the visible row count is greater than zero, the preferredHeight
539+ * is the maximum cell height * visibleRowCount. If the visible row
540+ * count is <= 0, the preferred height is either the current height
541+ * of the list, or the maximum cell height, whichever is
542+ * bigger. The preferred width is than the maximum cell width *
543+ * number of columns needed. Where the number of columns needs is
544+ * list.height / max cell height. Max cell height is either the fixed
545+ * cell height, or is determined by iterating through all the cells
546+ * to find the maximum height from the ListCellRenderer.
547+ * <tr>
548+ * <td>JList.HORIZONTAL_WRAP
549+ * <td>If the visible row count is greater than zero, the preferredHeight
550+ * is the maximum cell height * adjustedRowCount. Where
551+ * visibleRowCount is used to determine the number of columns.
552+ * Because this lays out horizontally the number of rows is
553+ * then determined from the column count. For example, lets say
554+ * you have a model with 10 items and the visible row count is 8.
555+ * The number of columns needed to display this is 2, but you no
556+ * longer need 8 rows to display this, you only need 5, thus
557+ * the adjustedRowCount is 5.
558+ * <p>If the visible row
559+ * count is <= 0, the preferred height is dictated by the
560+ * number of columns, which will be as many as can fit in the width
561+ * of the <code>JList</code> (width / max cell width), with at
562+ * least one column. The preferred height then becomes the
563+ * model size / number of columns * maximum cell height.
564+ * Max cell height is either the fixed
565+ * cell height, or is determined by iterating through all the cells
566+ * to find the maximum height from the ListCellRenderer.
567+ * </table>
568+ * The above specifies the raw preferred width and height. The resulting
569+ * preferred width is the above width + insets.left + insets.right and
570+ * the resulting preferred height is the above height + insets.top +
571+ * insets.bottom. Where the <code>Insets</code> are determined from
572+ * <code>list.getInsets()</code>.
573+ *
574+ * @param c The JList component.
575+ * @return The total size of the list.
576+ */
577+ public Dimension getPreferredSize(JComponent c) {
578+ maybeUpdateLayoutState();
579+
580+ int lastRow = list.getModel().getSize() - 1;
581+ if (lastRow < 0) {
582+ return new Dimension(0, 0);
583+ }
584+
585+ Insets insets = list.getInsets();
586+ int width = cellWidth * columnCount + insets.left + insets.right;
587+ int height;
588+
589+ if (layoutOrientation != JList.VERTICAL) {
590+ height = preferredHeight;
591+ }
592+ else {
593+ Rectangle bounds = getCellBounds(list, lastRow);
594+
595+ if (bounds != null) {
596+ height = bounds.y + bounds.height + insets.bottom;
597+ }
598+ else {
599+ height = 0;
600+ }
601+ }
602+ return new Dimension(width, height);
603+ }
604+
605+
606+ /**
607+ * Selected the previous row and force it to be visible.
608+ *
609+ * @see JList#ensureIndexIsVisible
610+ */
611+ protected void selectPreviousIndex() {
612+ int s = list.getSelectedIndex();
613+ if(s > 0) {
614+ s -= 1;
615+ list.setSelectedIndex(s);
616+ list.ensureIndexIsVisible(s);
617+ }
618+ }
619+
620+
621+ /**
622+ * Selected the previous row and force it to be visible.
623+ *
624+ * @see JList#ensureIndexIsVisible
625+ */
626+ protected void selectNextIndex()
627+ {
628+ int s = list.getSelectedIndex();
629+ if((s + 1) < list.getModel().getSize()) {
630+ s += 1;
631+ list.setSelectedIndex(s);
632+ list.ensureIndexIsVisible(s);
633+ }
634+ }
635+
636+
637+ /**
638+ * Registers the keyboard bindings on the <code>JList</code> that the
639+ * <code>BasicListUI</code> is associated with. This method is called at
640+ * installUI() time.
641+ *
642+ * @see #installUI
643+ */
644+ protected void installKeyboardActions() {
645+ InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
646+
647+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
648+ inputMap);
649+
650+ LazyActionMap.installLazyActionMap(list, BasicListUI.class,
651+ "List.actionMap");
652+ }
653+
654+ InputMap getInputMap(int condition) {
655+ if (condition == JComponent.WHEN_FOCUSED) {
656+ InputMap keyMap = (InputMap)DefaultLookup.get(
657+ list, this, "List.focusInputMap");
658+ InputMap rtlKeyMap;
659+
660+ if (isLeftToRight ||
661+ ((rtlKeyMap = (InputMap)DefaultLookup.get(list, this,
662+ "List.focusInputMap.RightToLeft")) == null)) {
663+ return keyMap;
664+ } else {
665+ rtlKeyMap.setParent(keyMap);
666+ return rtlKeyMap;
667+ }
668+ }
669+ return null;
670+ }
671+
672+ /**
673+ * Unregisters keyboard actions installed from
674+ * <code>installKeyboardActions</code>.
675+ * This method is called at uninstallUI() time - subclassess should
676+ * ensure that all of the keyboard actions registered at installUI
677+ * time are removed here.
678+ *
679+ * @see #installUI
680+ */
681+ protected void uninstallKeyboardActions() {
682+ SwingUtilities.replaceUIActionMap(list, null);
683+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
684+ }
685+
686+
687+ /**
688+ * Creates and installs the listeners for the JList, its model, and its
689+ * selectionModel. This method is called at installUI() time.
690+ *
691+ * @see #installUI
692+ * @see #uninstallListeners
693+ */
694+ protected void installListeners()
695+ {
696+ TransferHandler th = list.getTransferHandler();
697+ if (th == null || th instanceof UIResource) {
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"));
809+ if (b != isFileList) {
810+ isFileList = b;
811+ Font oldFont = list.getFont();
812+ if (oldFont == null || oldFont instanceof UIResource) {
813+ Font newFont = UIManager.getFont(b ? "FileChooser.listFont" : "List.font");
814+ if (newFont != null && newFont != oldFont) {
815+ list.setFont(newFont);
816+ }
817+ }
818+ }
819+ }
820+
821+
822+ /**
823+ * Sets the list properties that have not been explicitly overridden to
824+ * {@code null}. A property is considered overridden if its current value
825+ * is not a {@code UIResource}.
826+ *
827+ * @see #installDefaults
828+ * @see #uninstallUI
829+ * @see CellRendererPane
830+ */
831+ protected void uninstallDefaults()
832+ {
833+ LookAndFeel.uninstallBorder(list);
834+ if (list.getFont() instanceof UIResource) {
835+ list.setFont(null);
836+ }
837+ if (list.getForeground() instanceof UIResource) {
838+ list.setForeground(null);
839+ }
840+ if (list.getBackground() instanceof UIResource) {
841+ list.setBackground(null);
842+ }
843+ if (list.getSelectionBackground() instanceof UIResource) {
844+ list.setSelectionBackground(null);
845+ }
846+ if (list.getSelectionForeground() instanceof UIResource) {
847+ list.setSelectionForeground(null);
848+ }
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>
890+ * in order. Sets this.list to null.
891+ *
892+ * @see #uninstallListeners
893+ * @see #uninstallKeyboardActions
894+ * @see #uninstallDefaults
895+ */
896+ public void uninstallUI(JComponent c)
897+ {
898+ uninstallListeners();
899+ uninstallDefaults();
900+ uninstallKeyboardActions();
901+
902+ cellWidth = cellHeight = -1;
903+ cellHeights = null;
904+
905+ listWidth = listHeight = -1;
906+
907+ list.remove(rendererPane);
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) {
972+ if (layoutOrientation == JList.HORIZONTAL_WRAP) {
973+ int minRow = convertModelToRow(minIndex);
974+ int maxRow = convertModelToRow(maxIndex);
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 {
1016+ x = list.getWidth() - insets.right - (column+1) * cellWidth;
1017+ }
1018+ y += cellHeight * row;
1019+ h = cellHeight;
1020+ break;
1021+ default:
1022+ x = insets.left;
1023+ if (cellHeights == null) {
1024+ y += (cellHeight * row);
1025+ }
1026+ else if (row >= cellHeights.length) {
1027+ y = 0;
1028+ }
1029+ else {
1030+ for(int i = 0; i < row; i++) {
1031+ y += cellHeights[i];
1032+ }
1033+ }
1034+ w = list.getWidth() - (insets.left + insets.right);
1035+ h = getRowHeight(index);
1036+ break;
1037+ }
1038+ return new Rectangle(x, y, w, h);
1039+ }
1040+
1041+ /**
1042+ * Returns the height of the specified row based on the current layout.
1043+ *
1044+ * @return The specified row height or -1 if row isn't valid.
1045+ * @see #convertYToRow
1046+ * @see #convertRowToY
1047+ * @see #updateLayoutState
1048+ */
1049+ protected int getRowHeight(int row)
1050+ {
1051+ return getHeight(0, row);
1052+ }
1053+
1054+
1055+ /**
1056+ * Convert the JList relative coordinate to the row that contains it,
1057+ * based on the current layout. If y0 doesn't fall within any row,
1058+ * return -1.
1059+ *
1060+ * @return The row that contains y0, or -1.
1061+ * @see #getRowHeight
1062+ * @see #updateLayoutState
1063+ */
1064+ protected int convertYToRow(int y0)
1065+ {
1066+ return convertLocationToRow(0, y0, false);
1067+ }
1068+
1069+
1070+ /**
1071+ * Return the JList relative Y coordinate of the origin of the specified
1072+ * row or -1 if row isn't valid.
1073+ *
1074+ * @return The Y coordinate of the origin of row, or -1.
1075+ * @see #getRowHeight
1076+ * @see #updateLayoutState
1077+ */
1078+ protected int convertRowToY(int row)
1079+ {
1080+ if (row >= getRowCount(0) || row < 0) {
1081+ return -1;
1082+ }
1083+ Rectangle bounds = getCellBounds(list, row, row);
1084+ return bounds.y;
1085+ }
1086+
1087+ /**
1088+ * Returns the height of the cell at the passed in location.
1089+ */
1090+ private int getHeight(int column, int row) {
1091+ if (column < 0 || column > columnCount || row < 0) {
1092+ return -1;
1093+ }
1094+ if (layoutOrientation != JList.VERTICAL) {
1095+ return cellHeight;
1096+ }
1097+ if (row >= list.getModel().getSize()) {
1098+ return -1;
1099+ }
1100+ return (cellHeights == null) ? cellHeight :
1101+ ((row < cellHeights.length) ? cellHeights[row] : -1);
1102+ }
1103+
1104+ /**
1105+ * Returns the row at location x/y.
1106+ *
1107+ * @param closest If true and the location doesn't exactly match a
1108+ * particular location, this will return the closest row.
1109+ */
1110+ private int convertLocationToRow(int x, int y0, boolean closest) {
1111+ int size = list.getModel().getSize();
1112+
1113+ if (size <= 0) {
1114+ return -1;
1115+ }
1116+ Insets insets = list.getInsets();
1117+ if (cellHeights == null) {
1118+ int row = (cellHeight == 0) ? 0 :
1119+ ((y0 - insets.top) / cellHeight);
1120+ if (closest) {
1121+ if (row < 0) {
1122+ row = 0;
1123+ }
1124+ else if (row >= size) {
1125+ row = size - 1;
1126+ }
1127+ }
1128+ return row;
1129+ }
1130+ else if (size > cellHeights.length) {
1131+ return -1;
1132+ }
1133+ else {
1134+ int y = insets.top;
1135+ int row = 0;
1136+
1137+ if (closest && y0 < y) {
1138+ return 0;
1139+ }
1140+ int i;
1141+ for (i = 0; i < size; i++) {
1142+ if ((y0 >= y) && (y0 < y + cellHeights[i])) {
1143+ return row;
1144+ }
1145+ y += cellHeights[i];
1146+ row += 1;
1147+ }
1148+ return i - 1;
1149+ }
1150+ }
1151+
1152+ /**
1153+ * Returns the closest row that starts at the specified y-location
1154+ * in the passed in column.
1155+ */
1156+ private int convertLocationToRowInColumn(int y, int column) {
1157+ int x = 0;
1158+
1159+ if (layoutOrientation != JList.VERTICAL) {
1160+ if (isLeftToRight) {
1161+ x = column * cellWidth;
1162+ } else {
1163+ x = list.getWidth() - (column+1)*cellWidth - list.getInsets().right;
1164+ }
1165+ }
1166+ return convertLocationToRow(x, y, true);
1167+ }
1168+
1169+ /**
1170+ * Returns the closest location to the model index of the passed in
1171+ * location.
1172+ */
1173+ private int convertLocationToModel(int x, int y) {
1174+ int row = convertLocationToRow(x, y, true);
1175+ int column = convertLocationToColumn(x, y);
1176+
1177+ if (row >= 0 && column >= 0) {
1178+ return getModelIndex(column, row);
1179+ }
1180+ return -1;
1181+ }
1182+
1183+ /**
1184+ * Returns the number of rows in the given column.
1185+ */
1186+ private int getRowCount(int column) {
1187+ if (column < 0 || column >= columnCount) {
1188+ return -1;
1189+ }
1190+ if (layoutOrientation == JList.VERTICAL ||
1191+ (column == 0 && columnCount == 1)) {
1192+ return list.getModel().getSize();
1193+ }
1194+ if (column >= columnCount) {
1195+ return -1;
1196+ }
1197+ if (layoutOrientation == JList.VERTICAL_WRAP) {
1198+ if (column < (columnCount - 1)) {
1199+ return rowsPerColumn;
1200+ }
1201+ return list.getModel().getSize() - (columnCount - 1) *
1202+ rowsPerColumn;
1203+ }
1204+ // JList.HORIZONTAL_WRAP
1205+ int diff = columnCount - (columnCount * rowsPerColumn -
1206+ list.getModel().getSize());
1207+
1208+ if (column >= diff) {
1209+ return Math.max(0, rowsPerColumn - 1);
1210+ }
1211+ return rowsPerColumn;
1212+ }
1213+
1214+ /**
1215+ * Returns the model index for the specified display location.
1216+ * If <code>column</code>x<code>row</code> is beyond the length of the
1217+ * model, this will return the model size - 1.
1218+ */
1219+ private int getModelIndex(int column, int row) {
1220+ switch (layoutOrientation) {
1221+ case JList.VERTICAL_WRAP:
1222+ return Math.min(list.getModel().getSize() - 1, rowsPerColumn *
1223+ column + Math.min(row, rowsPerColumn-1));
1224+ case JList.HORIZONTAL_WRAP:
1225+ return Math.min(list.getModel().getSize() - 1, row * columnCount +
1226+ column);
1227+ default:
1228+ return row;
1229+ }
1230+ }
1231+
1232+ /**
1233+ * Returns the closest column to the passed in location.
1234+ */
1235+ private int convertLocationToColumn(int x, int y) {
1236+ if (cellWidth > 0) {
1237+ if (layoutOrientation == JList.VERTICAL) {
1238+ return 0;
1239+ }
1240+ Insets insets = list.getInsets();
1241+ int col;
1242+ if (isLeftToRight) {
1243+ col = (x - insets.left) / cellWidth;
1244+ } else {
1245+ col = (list.getWidth() - x - insets.right - 1) / cellWidth;
1246+ }
1247+ if (col < 0) {
1248+ return 0;
1249+ }
1250+ else if (col >= columnCount) {
1251+ return columnCount - 1;
1252+ }
1253+ return col;
1254+ }
1255+ return 0;
1256+ }
1257+
1258+ /**
1259+ * Returns the row that the model index <code>index</code> will be
1260+ * displayed in..
1261+ */
1262+ private int convertModelToRow(int index) {
1263+ int size = list.getModel().getSize();
1264+
1265+ if ((index < 0) || (index >= size)) {
1266+ return -1;
1267+ }
1268+
1269+ if (layoutOrientation != JList.VERTICAL && columnCount > 1 &&
1270+ rowsPerColumn > 0) {
1271+ if (layoutOrientation == JList.VERTICAL_WRAP) {
1272+ return index % rowsPerColumn;
1273+ }
1274+ return index / columnCount;
1275+ }
1276+ return index;
1277+ }
1278+
1279+ /**
1280+ * Returns the column that the model index <code>index</code> will be
1281+ * displayed in.
1282+ */
1283+ private int convertModelToColumn(int index) {
1284+ int size = list.getModel().getSize();
1285+
1286+ if ((index < 0) || (index >= size)) {
1287+ return -1;
1288+ }
1289+
1290+ if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 &&
1291+ columnCount > 1) {
1292+ if (layoutOrientation == JList.VERTICAL_WRAP) {
1293+ return index / rowsPerColumn;
1294+ }
1295+ return index % columnCount;
1296+ }
1297+ return 0;
1298+ }
1299+
1300+ /**
1301+ * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
1302+ * updateLayoutStateNeeded. This method should be called by methods
1303+ * before doing any computation based on the geometry of the list.
1304+ * For example it's the first call in paint() and getPreferredSize().
1305+ *
1306+ * @see #updateLayoutState
1307+ */
1308+ protected void maybeUpdateLayoutState()
1309+ {
1310+ if (updateLayoutStateNeeded != 0) {
1311+ updateLayoutState();
1312+ updateLayoutStateNeeded = 0;
1313+ }
1314+ }
1315+
1316+
1317+ /**
1318+ * Recompute the value of cellHeight or cellHeights based
1319+ * and cellWidth, based on the current font and the current
1320+ * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
1321+ *
1322+ * @see #maybeUpdateLayoutState
1323+ */
1324+ protected void updateLayoutState()
1325+ {
1326+ /* If both JList fixedCellWidth and fixedCellHeight have been
1327+ * set, then initialize cellWidth and cellHeight, and set
1328+ * cellHeights to null.
1329+ */
1330+
1331+ int fixedCellHeight = list.getFixedCellHeight();
1332+ int fixedCellWidth = list.getFixedCellWidth();
1333+
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) {
1377+ cellHeights = new int[dataModelSize];
1378+ }
1379+ for(int index = 0; index < dataModelSize; index++) {
1380+ cellHeights[index] = 0;
1381+ }
1382+ }
1383+ }
1384+
1385+ columnCount = 1;
1386+ if (layoutOrientation != JList.VERTICAL) {
1387+ updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight);
1388+ }
1389+ }
1390+
1391+ /**
1392+ * Invoked when the list is layed out horizontally to determine how
1393+ * many columns to create.
1394+ * <p>
1395+ * This updates the <code>rowsPerColumn, </code><code>columnCount</code>,
1396+ * <code>preferredHeight</code> and potentially <code>cellHeight</code>
1397+ * instance variables.
1398+ */
1399+ private void updateHorizontalLayoutState(int fixedCellWidth,
1400+ int fixedCellHeight) {
1401+ int visRows = list.getVisibleRowCount();
1402+ int dataModelSize = list.getModel().getSize();
1403+ Insets insets = list.getInsets();
1404+
1405+ listHeight = list.getHeight();
1406+ listWidth = list.getWidth();
1407+
1408+ if (dataModelSize == 0) {
1409+ rowsPerColumn = columnCount = 0;
1410+ preferredHeight = insets.top + insets.bottom;
1411+ return;
1412+ }
1413+
1414+ int height;
1415+
1416+ if (fixedCellHeight != -1) {
1417+ height = fixedCellHeight;
1418+ }
1419+ else {
1420+ // Determine the max of the renderer heights.
1421+ int maxHeight = 0;
1422+ if (cellHeights.length > 0) {
1423+ maxHeight = cellHeights[cellHeights.length - 1];
1424+ for (int counter = cellHeights.length - 2;
1425+ counter >= 0; counter--) {
1426+ maxHeight = Math.max(maxHeight, cellHeights[counter]);
1427+ }
1428+ }
1429+ height = cellHeight = maxHeight;
1430+ cellHeights = null;
1431+ }
1432+ // The number of rows is either determined by the visible row
1433+ // count, or by the height of the list.
1434+ rowsPerColumn = dataModelSize;
1435+ if (visRows > 0) {
1436+ rowsPerColumn = visRows;
1437+ columnCount = Math.max(1, dataModelSize / rowsPerColumn);
1438+ if (dataModelSize > 0 && dataModelSize > rowsPerColumn &&
1439+ dataModelSize % rowsPerColumn != 0) {
1440+ columnCount++;
1441+ }
1442+ if (layoutOrientation == JList.HORIZONTAL_WRAP) {
1443+ // Because HORIZONTAL_WRAP flows differently, the
1444+ // rowsPerColumn needs to be adjusted.
1445+ rowsPerColumn = (dataModelSize / columnCount);
1446+ if (dataModelSize % columnCount > 0) {
1447+ rowsPerColumn++;
1448+ }
1449+ }
1450+ }
1451+ else if (layoutOrientation == JList.VERTICAL_WRAP && height != 0) {
1452+ rowsPerColumn = Math.max(1, (listHeight - insets.top -
1453+ insets.bottom) / height);
1454+ columnCount = Math.max(1, dataModelSize / rowsPerColumn);
1455+ if (dataModelSize > 0 && dataModelSize > rowsPerColumn &&
1456+ dataModelSize % rowsPerColumn != 0) {
1457+ columnCount++;
1458+ }
1459+ }
1460+ else if (layoutOrientation == JList.HORIZONTAL_WRAP && cellWidth > 0 &&
1461+ listWidth > 0) {
1462+ columnCount = Math.max(1, (listWidth - insets.left -
1463+ insets.right) / cellWidth);
1464+ rowsPerColumn = dataModelSize / columnCount;
1465+ if (dataModelSize % columnCount > 0) {
1466+ rowsPerColumn++;
1467+ }
1468+ }
1469+ preferredHeight = rowsPerColumn * cellHeight + insets.top +
1470+ insets.bottom;
1471+ }
1472+
1473+ private Handler getHandler() {
1474+ if (handler == null) {
1475+ handler = new Handler();
1476+ }
1477+ return handler;
1478+ }
1479+
1480+ /**
1481+ * Mouse input, and focus handling for JList. An instance of this
1482+ * class is added to the appropriate java.awt.Component lists
1483+ * at installUI() time. Note keyboard input is handled with JComponent
1484+ * KeyboardActions, see installKeyboardActions().
1485+ * <p>
1486+ * <strong>Warning:</strong>
1487+ * Serialized objects of this class will not be compatible with
1488+ * future Swing releases. The current serialization support is
1489+ * appropriate for short term storage or RMI between applications running
1490+ * the same version of Swing. As of 1.4, support for long term storage
1491+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
1492+ * has been added to the <code>java.beans</code> package.
1493+ * Please see {@link java.beans.XMLEncoder}.
1494+ *
1495+ * @see #createMouseInputListener
1496+ * @see #installKeyboardActions
1497+ * @see #installUI
1498+ */
1499+ public class MouseInputHandler implements MouseInputListener
1500+ {
1501+ public void mouseClicked(MouseEvent e) {
1502+ getHandler().mouseClicked(e);
1503+ }
1504+
1505+ public void mouseEntered(MouseEvent e) {
1506+ getHandler().mouseEntered(e);
1507+ }
1508+
1509+ public void mouseExited(MouseEvent e) {
1510+ getHandler().mouseExited(e);
1511+ }
1512+
1513+ public void mousePressed(MouseEvent e) {
1514+ getHandler().mousePressed(e);
1515+ }
1516+
1517+ public void mouseDragged(MouseEvent e) {
1518+ getHandler().mouseDragged(e);
1519+ }
1520+
1521+ public void mouseMoved(MouseEvent e) {
1522+ getHandler().mouseMoved(e);
1523+ }
1524+
1525+ public void mouseReleased(MouseEvent e) {
1526+ getHandler().mouseReleased(e);
1527+ }
1528+ }
1529+
1530+
1531+ /**
1532+ * Creates a delegate that implements MouseInputListener.
1533+ * The delegate is added to the corresponding java.awt.Component listener
1534+ * lists at installUI() time. Subclasses can override this method to return
1535+ * a custom MouseInputListener, e.g.
1536+ * <pre>
1537+ * class MyListUI extends BasicListUI {
1538+ * protected MouseInputListener <b>createMouseInputListener</b>() {
1539+ * return new MyMouseInputHandler();
1540+ * }
1541+ * public class MyMouseInputHandler extends MouseInputHandler {
1542+ * public void mouseMoved(MouseEvent e) {
1543+ * // do some extra work when the mouse moves
1544+ * super.mouseMoved(e);
1545+ * }
1546+ * }
1547+ * }
1548+ * </pre>
1549+ *
1550+ * @see MouseInputHandler
1551+ * @see #installUI
1552+ */
1553+ protected MouseInputListener createMouseInputListener() {
1554+ return getHandler();
1555+ }
1556+
1557+ /**
1558+ * This class should be treated as a &quot;protected&quot; inner class.
1559+ * Instantiate it only within subclasses of {@code BasicListUI}.
1560+ */
1561+ public class FocusHandler implements FocusListener
1562+ {
1563+ protected void repaintCellFocus()
1564+ {
1565+ getHandler().repaintCellFocus();
1566+ }
1567+
1568+ /* The focusGained() focusLost() methods run when the JList
1569+ * focus changes.
1570+ */
1571+
1572+ public void focusGained(FocusEvent e) {
1573+ getHandler().focusGained(e);
1574+ }
1575+
1576+ public void focusLost(FocusEvent e) {
1577+ getHandler().focusLost(e);
1578+ }
1579+ }
1580+
1581+ protected FocusListener createFocusListener() {
1582+ return getHandler();
1583+ }
1584+
1585+ /**
1586+ * The ListSelectionListener that's added to the JLists selection
1587+ * model at installUI time, and whenever the JList.selectionModel property
1588+ * changes. When the selection changes we repaint the affected rows.
1589+ * <p>
1590+ * <strong>Warning:</strong>
1591+ * Serialized objects of this class will not be compatible with
1592+ * future Swing releases. The current serialization support is
1593+ * appropriate for short term storage or RMI between applications running
1594+ * the same version of Swing. As of 1.4, support for long term storage
1595+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
1596+ * has been added to the <code>java.beans</code> package.
1597+ * Please see {@link java.beans.XMLEncoder}.
1598+ *
1599+ * @see #createListSelectionListener
1600+ * @see #getCellBounds
1601+ * @see #installUI
1602+ */
1603+ public class ListSelectionHandler implements ListSelectionListener
1604+ {
1605+ public void valueChanged(ListSelectionEvent e)
1606+ {
1607+ getHandler().valueChanged(e);
1608+ }
1609+ }
1610+
1611+
1612+ /**
1613+ * Creates an instance of ListSelectionHandler that's added to
1614+ * the JLists by selectionModel as needed. Subclasses can override
1615+ * this method to return a custom ListSelectionListener, e.g.
1616+ * <pre>
1617+ * class MyListUI extends BasicListUI {
1618+ * protected ListSelectionListener <b>createListSelectionListener</b>() {
1619+ * return new MySelectionListener();
1620+ * }
1621+ * public class MySelectionListener extends ListSelectionHandler {
1622+ * public void valueChanged(ListSelectionEvent e) {
1623+ * // do some extra work when the selection changes
1624+ * super.valueChange(e);
1625+ * }
1626+ * }
1627+ * }
1628+ * </pre>
1629+ *
1630+ * @see ListSelectionHandler
1631+ * @see #installUI
1632+ */
1633+ protected ListSelectionListener createListSelectionListener() {
1634+ return getHandler();
1635+ }
1636+
1637+
1638+ private void redrawList() {
1639+ list.revalidate();
1640+ list.repaint();
1641+ }
1642+
1643+
1644+ /**
1645+ * The ListDataListener that's added to the JLists model at
1646+ * installUI time, and whenever the JList.model property changes.
1647+ * <p>
1648+ * <strong>Warning:</strong>
1649+ * Serialized objects of this class will not be compatible with
1650+ * future Swing releases. The current serialization support is
1651+ * appropriate for short term storage or RMI between applications running
1652+ * the same version of Swing. As of 1.4, support for long term storage
1653+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
1654+ * has been added to the <code>java.beans</code> package.
1655+ * Please see {@link java.beans.XMLEncoder}.
1656+ *
1657+ * @see JList#getModel
1658+ * @see #maybeUpdateLayoutState
1659+ * @see #createListDataListener
1660+ * @see #installUI
1661+ */
1662+ public class ListDataHandler implements ListDataListener
1663+ {
1664+ public void intervalAdded(ListDataEvent e) {
1665+ getHandler().intervalAdded(e);
1666+ }
1667+
1668+
1669+ public void intervalRemoved(ListDataEvent e)
1670+ {
1671+ getHandler().intervalRemoved(e);
1672+ }
1673+
1674+
1675+ public void contentsChanged(ListDataEvent e) {
1676+ getHandler().contentsChanged(e);
1677+ }
1678+ }
1679+
1680+
1681+ /**
1682+ * Creates an instance of ListDataListener that's added to
1683+ * the JLists by model as needed. Subclasses can override
1684+ * this method to return a custom ListDataListener, e.g.
1685+ * <pre>
1686+ * class MyListUI extends BasicListUI {
1687+ * protected ListDataListener <b>createListDataListener</b>() {
1688+ * return new MyListDataListener();
1689+ * }
1690+ * public class MyListDataListener extends ListDataHandler {
1691+ * public void contentsChanged(ListDataEvent e) {
1692+ * // do some extra work when the models contents change
1693+ * super.contentsChange(e);
1694+ * }
1695+ * }
1696+ * }
1697+ * </pre>
1698+ *
1699+ * @see ListDataListener
1700+ * @see JList#getModel
1701+ * @see #installUI
1702+ */
1703+ protected ListDataListener createListDataListener() {
1704+ return getHandler();
1705+ }
1706+
1707+
1708+ /**
1709+ * The PropertyChangeListener that's added to the JList at
1710+ * installUI time. When the value of a JList property that
1711+ * affects layout changes, we set a bit in updateLayoutStateNeeded.
1712+ * If the JLists model changes we additionally remove our listeners
1713+ * from the old model. Likewise for the JList selectionModel.
1714+ * <p>
1715+ * <strong>Warning:</strong>
1716+ * Serialized objects of this class will not be compatible with
1717+ * future Swing releases. The current serialization support is
1718+ * appropriate for short term storage or RMI between applications running
1719+ * the same version of Swing. As of 1.4, support for long term storage
1720+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
1721+ * has been added to the <code>java.beans</code> package.
1722+ * Please see {@link java.beans.XMLEncoder}.
1723+ *
1724+ * @see #maybeUpdateLayoutState
1725+ * @see #createPropertyChangeListener
1726+ * @see #installUI
1727+ */
1728+ public class PropertyChangeHandler implements PropertyChangeListener
1729+ {
1730+ public void propertyChange(PropertyChangeEvent e)
1731+ {
1732+ getHandler().propertyChange(e);
1733+ }
1734+ }
1735+
1736+
1737+ /**
1738+ * Creates an instance of PropertyChangeHandler that's added to
1739+ * the JList by installUI(). Subclasses can override this method
1740+ * to return a custom PropertyChangeListener, e.g.
1741+ * <pre>
1742+ * class MyListUI extends BasicListUI {
1743+ * protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
1744+ * return new MyPropertyChangeListener();
1745+ * }
1746+ * public class MyPropertyChangeListener extends PropertyChangeHandler {
1747+ * public void propertyChange(PropertyChangeEvent e) {
1748+ * if (e.getPropertyName().equals("model")) {
1749+ * // do some extra work when the model changes
1750+ * }
1751+ * super.propertyChange(e);
1752+ * }
1753+ * }
1754+ * }
1755+ * </pre>
1756+ *
1757+ * @see PropertyChangeListener
1758+ * @see #installUI
1759+ */
1760+ protected PropertyChangeListener createPropertyChangeListener() {
1761+ return getHandler();
1762+ }
1763+
1764+ /** Used by IncrementLeadSelectionAction. Indicates the action should
1765+ * change the lead, and not select it. */
1766+ private static final int CHANGE_LEAD = 0;
1767+ /** Used by IncrementLeadSelectionAction. Indicates the action should
1768+ * change the selection and lead. */
1769+ private static final int CHANGE_SELECTION = 1;
1770+ /** Used by IncrementLeadSelectionAction. Indicates the action should
1771+ * extend the selection from the anchor to the next index. */
1772+ private static final int EXTEND_SELECTION = 2;
1773+
1774+
1775+ private static class Actions extends UIAction {
1776+ private static final String SELECT_PREVIOUS_COLUMN =
1777+ "selectPreviousColumn";
1778+ private static final String SELECT_PREVIOUS_COLUMN_EXTEND =
1779+ "selectPreviousColumnExtendSelection";
1780+ private static final String SELECT_PREVIOUS_COLUMN_CHANGE_LEAD =
1781+ "selectPreviousColumnChangeLead";
1782+ private static final String SELECT_NEXT_COLUMN = "selectNextColumn";
1783+ private static final String SELECT_NEXT_COLUMN_EXTEND =
1784+ "selectNextColumnExtendSelection";
1785+ private static final String SELECT_NEXT_COLUMN_CHANGE_LEAD =
1786+ "selectNextColumnChangeLead";
1787+ private static final String SELECT_PREVIOUS_ROW = "selectPreviousRow";
1788+ private static final String SELECT_PREVIOUS_ROW_EXTEND =
1789+ "selectPreviousRowExtendSelection";
1790+ private static final String SELECT_PREVIOUS_ROW_CHANGE_LEAD =
1791+ "selectPreviousRowChangeLead";
1792+ private static final String SELECT_NEXT_ROW = "selectNextRow";
1793+ private static final String SELECT_NEXT_ROW_EXTEND =
1794+ "selectNextRowExtendSelection";
1795+ private static final String SELECT_NEXT_ROW_CHANGE_LEAD =
1796+ "selectNextRowChangeLead";
1797+ private static final String SELECT_FIRST_ROW = "selectFirstRow";
1798+ private static final String SELECT_FIRST_ROW_EXTEND =
1799+ "selectFirstRowExtendSelection";
1800+ private static final String SELECT_FIRST_ROW_CHANGE_LEAD =
1801+ "selectFirstRowChangeLead";
1802+ private static final String SELECT_LAST_ROW = "selectLastRow";
1803+ private static final String SELECT_LAST_ROW_EXTEND =
1804+ "selectLastRowExtendSelection";
1805+ private static final String SELECT_LAST_ROW_CHANGE_LEAD =
1806+ "selectLastRowChangeLead";
1807+ private static final String SCROLL_UP = "scrollUp";
1808+ private static final String SCROLL_UP_EXTEND =
1809+ "scrollUpExtendSelection";
1810+ private static final String SCROLL_UP_CHANGE_LEAD =
1811+ "scrollUpChangeLead";
1812+ private static final String SCROLL_DOWN = "scrollDown";
1813+ private static final String SCROLL_DOWN_EXTEND =
1814+ "scrollDownExtendSelection";
1815+ private static final String SCROLL_DOWN_CHANGE_LEAD =
1816+ "scrollDownChangeLead";
1817+ private static final String SELECT_ALL = "selectAll";
1818+ private static final String CLEAR_SELECTION = "clearSelection";
1819+
1820+ // add the lead item to the selection without changing lead or anchor
1821+ private static final String ADD_TO_SELECTION = "addToSelection";
1822+
1823+ // toggle the selected state of the lead item and move the anchor to it
1824+ private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
1825+
1826+ // extend the selection to the lead item
1827+ private static final String EXTEND_TO = "extendTo";
1828+
1829+ // move the anchor to the lead and ensure only that item is selected
1830+ private static final String MOVE_SELECTION_TO = "moveSelectionTo";
1831+
1832+ Actions(String name) {
1833+ super(name);
1834+ }
1835+ public void actionPerformed(ActionEvent e) {
1836+ String name = getName();
1837+ JList list = (JList)e.getSource();
1838+ BasicListUI ui = (BasicListUI)BasicLookAndFeel.getUIOfType(
1839+ list.getUI(), BasicListUI.class);
1840+
1841+ if (name == SELECT_PREVIOUS_COLUMN) {
1842+ changeSelection(list, CHANGE_SELECTION,
1843+ getNextColumnIndex(list, ui, -1), -1);
1844+ }
1845+ else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) {
1846+ changeSelection(list, EXTEND_SELECTION,
1847+ getNextColumnIndex(list, ui, -1), -1);
1848+ }
1849+ else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) {
1850+ changeSelection(list, CHANGE_LEAD,
1851+ getNextColumnIndex(list, ui, -1), -1);
1852+ }
1853+ else if (name == SELECT_NEXT_COLUMN) {
1854+ changeSelection(list, CHANGE_SELECTION,
1855+ getNextColumnIndex(list, ui, 1), 1);
1856+ }
1857+ else if (name == SELECT_NEXT_COLUMN_EXTEND) {
1858+ changeSelection(list, EXTEND_SELECTION,
1859+ getNextColumnIndex(list, ui, 1), 1);
1860+ }
1861+ else if (name == SELECT_NEXT_COLUMN_CHANGE_LEAD) {
1862+ changeSelection(list, CHANGE_LEAD,
1863+ getNextColumnIndex(list, ui, 1), 1);
1864+ }
1865+ else if (name == SELECT_PREVIOUS_ROW) {
1866+ changeSelection(list, CHANGE_SELECTION,
1867+ getNextIndex(list, ui, -1), -1);
1868+ }
1869+ else if (name == SELECT_PREVIOUS_ROW_EXTEND) {
1870+ changeSelection(list, EXTEND_SELECTION,
1871+ getNextIndex(list, ui, -1), -1);
1872+ }
1873+ else if (name == SELECT_PREVIOUS_ROW_CHANGE_LEAD) {
1874+ changeSelection(list, CHANGE_LEAD,
1875+ getNextIndex(list, ui, -1), -1);
1876+ }
1877+ else if (name == SELECT_NEXT_ROW) {
1878+ changeSelection(list, CHANGE_SELECTION,
1879+ getNextIndex(list, ui, 1), 1);
1880+ }
1881+ else if (name == SELECT_NEXT_ROW_EXTEND) {
1882+ changeSelection(list, EXTEND_SELECTION,
1883+ getNextIndex(list, ui, 1), 1);
1884+ }
1885+ else if (name == SELECT_NEXT_ROW_CHANGE_LEAD) {
1886+ changeSelection(list, CHANGE_LEAD,
1887+ getNextIndex(list, ui, 1), 1);
1888+ }
1889+ else if (name == SELECT_FIRST_ROW) {
1890+ changeSelection(list, CHANGE_SELECTION, 0, -1);
1891+ }
1892+ else if (name == SELECT_FIRST_ROW_EXTEND) {
1893+ changeSelection(list, EXTEND_SELECTION, 0, -1);
1894+ }
1895+ else if (name == SELECT_FIRST_ROW_CHANGE_LEAD) {
1896+ changeSelection(list, CHANGE_LEAD, 0, -1);
1897+ }
1898+ else if (name == SELECT_LAST_ROW) {
1899+ changeSelection(list, CHANGE_SELECTION,
1900+ list.getModel().getSize() - 1, 1);
1901+ }
1902+ else if (name == SELECT_LAST_ROW_EXTEND) {
1903+ changeSelection(list, EXTEND_SELECTION,
1904+ list.getModel().getSize() - 1, 1);
1905+ }
1906+ else if (name == SELECT_LAST_ROW_CHANGE_LEAD) {
1907+ changeSelection(list, CHANGE_LEAD,
1908+ list.getModel().getSize() - 1, 1);
1909+ }
1910+ else if (name == SCROLL_UP) {
1911+ changeSelection(list, CHANGE_SELECTION,
1912+ getNextPageIndex(list, -1), -1);
1913+ }
1914+ else if (name == SCROLL_UP_EXTEND) {
1915+ changeSelection(list, EXTEND_SELECTION,
1916+ getNextPageIndex(list, -1), -1);
1917+ }
1918+ else if (name == SCROLL_UP_CHANGE_LEAD) {
1919+ changeSelection(list, CHANGE_LEAD,
1920+ getNextPageIndex(list, -1), -1);
1921+ }
1922+ else if (name == SCROLL_DOWN) {
1923+ changeSelection(list, CHANGE_SELECTION,
1924+ getNextPageIndex(list, 1), 1);
1925+ }
1926+ else if (name == SCROLL_DOWN_EXTEND) {
1927+ changeSelection(list, EXTEND_SELECTION,
1928+ getNextPageIndex(list, 1), 1);
1929+ }
1930+ else if (name == SCROLL_DOWN_CHANGE_LEAD) {
1931+ changeSelection(list, CHANGE_LEAD,
1932+ getNextPageIndex(list, 1), 1);
1933+ }
1934+ else if (name == SELECT_ALL) {
1935+ selectAll(list);
1936+ }
1937+ else if (name == CLEAR_SELECTION) {
1938+ clearSelection(list);
1939+ }
1940+ else if (name == ADD_TO_SELECTION) {
1941+ int index = adjustIndex(
1942+ list.getSelectionModel().getLeadSelectionIndex(), list);
1943+
1944+ if (!list.isSelectedIndex(index)) {
1945+ int oldAnchor = list.getSelectionModel().getAnchorSelectionIndex();
1946+ list.setValueIsAdjusting(true);
1947+ list.addSelectionInterval(index, index);
1948+ list.getSelectionModel().setAnchorSelectionIndex(oldAnchor);
1949+ list.setValueIsAdjusting(false);
1950+ }
1951+ }
1952+ else if (name == TOGGLE_AND_ANCHOR) {
1953+ int index = adjustIndex(
1954+ list.getSelectionModel().getLeadSelectionIndex(), list);
1955+
1956+ if (list.isSelectedIndex(index)) {
1957+ list.removeSelectionInterval(index, index);
1958+ } else {
1959+ list.addSelectionInterval(index, index);
1960+ }
1961+ }
1962+ else if (name == EXTEND_TO) {
1963+ changeSelection(
1964+ list, EXTEND_SELECTION,
1965+ adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list),
1966+ 0);
1967+ }
1968+ else if (name == MOVE_SELECTION_TO) {
1969+ changeSelection(
1970+ list, CHANGE_SELECTION,
1971+ adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list),
1972+ 0);
1973+ }
1974+ }
1975+
1976+ public boolean isEnabled(Object c) {
1977+ Object name = getName();
1978+ if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD ||
1979+ name == SELECT_NEXT_COLUMN_CHANGE_LEAD ||
1980+ name == SELECT_PREVIOUS_ROW_CHANGE_LEAD ||
1981+ name == SELECT_NEXT_ROW_CHANGE_LEAD ||
1982+ name == SELECT_FIRST_ROW_CHANGE_LEAD ||
1983+ name == SELECT_LAST_ROW_CHANGE_LEAD ||
1984+ name == SCROLL_UP_CHANGE_LEAD ||
1985+ name == SCROLL_DOWN_CHANGE_LEAD) {
1986+
1987+ // discontinuous selection actions are only enabled for
1988+ // DefaultListSelectionModel
1989+ return c != null && ((JList)c).getSelectionModel()
1990+ instanceof DefaultListSelectionModel;
1991+ }
1992+
1993+ return true;
1994+ }
1995+
1996+ private void clearSelection(JList list) {
1997+ list.clearSelection();
1998+ }
1999+
2000+ private void selectAll(JList list) {
2001+ int size = list.getModel().getSize();
2002+ if (size > 0) {
2003+ ListSelectionModel lsm = list.getSelectionModel();
2004+ int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
2005+
2006+ if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
2007+ if (lead == -1) {
2008+ int min = adjustIndex(list.getMinSelectionIndex(), list);
2009+ lead = (min == -1 ? 0 : min);
2010+ }
2011+
2012+ list.setSelectionInterval(lead, lead);
2013+ list.ensureIndexIsVisible(lead);
2014+ } else {
2015+ list.setValueIsAdjusting(true);
2016+
2017+ int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);
2018+
2019+ list.setSelectionInterval(0, size - 1);
2020+
2021+ // this is done to restore the anchor and lead
2022+ SwingUtilities2.setLeadAnchorWithoutSelection(lsm, anchor, lead);
2023+
2024+ list.setValueIsAdjusting(false);
2025+ }
2026+ }
2027+ }
2028+
2029+ private int getNextPageIndex(JList list, int direction) {
2030+ if (list.getModel().getSize() == 0) {
2031+ return -1;
2032+ }
2033+
2034+ int index = -1;
2035+ Rectangle visRect = list.getVisibleRect();
2036+ ListSelectionModel lsm = list.getSelectionModel();
2037+ int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
2038+ Rectangle leadRect =
2039+ (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead);
2040+
2041+ if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
2042+ list.getVisibleRowCount() <= 0) {
2043+ if (!list.getComponentOrientation().isLeftToRight()) {
2044+ direction = -direction;
2045+ }
2046+ // apply for horizontal scrolling: the step for next
2047+ // page index is number of visible columns
2048+ if (direction < 0) {
2049+ // left
2050+ visRect.x = leadRect.x + leadRect.width - visRect.width;
2051+ Point p = new Point(visRect.x - 1, leadRect.y);
2052+ index = list.locationToIndex(p);
2053+ Rectangle cellBounds = list.getCellBounds(index, index);
2054+ if (visRect.intersects(cellBounds)) {
2055+ p.x = cellBounds.x - 1;
2056+ index = list.locationToIndex(p);
2057+ cellBounds = list.getCellBounds(index, index);
2058+ }
2059+ // this is necessary for right-to-left orientation only
2060+ if (cellBounds.y != leadRect.y) {
2061+ p.x = cellBounds.x + cellBounds.width;
2062+ index = list.locationToIndex(p);
2063+ }
2064+ }
2065+ else {
2066+ // right
2067+ visRect.x = leadRect.x;
2068+ Point p = new Point(visRect.x + visRect.width, leadRect.y);
2069+ index = list.locationToIndex(p);
2070+ Rectangle cellBounds = list.getCellBounds(index, index);
2071+ if (visRect.intersects(cellBounds)) {
2072+ p.x = cellBounds.x + cellBounds.width;
2073+ index = list.locationToIndex(p);
2074+ cellBounds = list.getCellBounds(index, index);
2075+ }
2076+ if (cellBounds.y != leadRect.y) {
2077+ p.x = cellBounds.x - 1;
2078+ index = list.locationToIndex(p);
2079+ }
2080+ }
2081+ }
2082+ else {
2083+ if (direction < 0) {
2084+ // up
2085+ // go to the first visible cell
2086+ Point p = new Point(leadRect.x, visRect.y);
2087+ index = list.locationToIndex(p);
2088+ if (lead <= index) {
2089+ // if lead is the first visible cell (or above it)
2090+ // adjust the visible rect up
2091+ visRect.y = leadRect.y + leadRect.height - visRect.height;
2092+ p.y = visRect.y;
2093+ index = list.locationToIndex(p);
2094+ Rectangle cellBounds = list.getCellBounds(index, index);
2095+ // go one cell down if first visible cell doesn't fit
2096+ // into adjasted visible rectangle
2097+ if (cellBounds.y < visRect.y) {
2098+ p.y = cellBounds.y + cellBounds.height;
2099+ index = list.locationToIndex(p);
2100+ cellBounds = list.getCellBounds(index, index);
2101+ }
2102+ // if index isn't less then lead
2103+ // try to go to cell previous to lead
2104+ if (cellBounds.y >= leadRect.y) {
2105+ p.y = leadRect.y - 1;
2106+ index = list.locationToIndex(p);
2107+ }
2108+ }
2109+ }
2110+ else {
2111+ // down
2112+ // go to the last completely visible cell
2113+ Point p = new Point(leadRect.x,
2114+ visRect.y + visRect.height - 1);
2115+ index = list.locationToIndex(p);
2116+ Rectangle cellBounds = list.getCellBounds(index, index);
2117+ // go up one cell if last visible cell doesn't fit
2118+ // into visible rectangle
2119+ if (cellBounds.y + cellBounds.height >
2120+ visRect.y + visRect.height) {
2121+ p.y = cellBounds.y - 1;
2122+ index = list.locationToIndex(p);
2123+ cellBounds = list.getCellBounds(index, index);
2124+ index = Math.max(index, lead);
2125+ }
2126+
2127+ if (lead >= index) {
2128+ // if lead is the last completely visible index
2129+ // (or below it) adjust the visible rect down
2130+ visRect.y = leadRect.y;
2131+ p.y = visRect.y + visRect.height - 1;
2132+ index = list.locationToIndex(p);
2133+ cellBounds = list.getCellBounds(index, index);
2134+ // go one cell up if last visible cell doesn't fit
2135+ // into adjasted visible rectangle
2136+ if (cellBounds.y + cellBounds.height >
2137+ visRect.y + visRect.height) {
2138+ p.y = cellBounds.y - 1;
2139+ index = list.locationToIndex(p);
2140+ cellBounds = list.getCellBounds(index, index);
2141+ }
2142+ // if index isn't greater then lead
2143+ // try to go to cell next after lead
2144+ if (cellBounds.y <= leadRect.y) {
2145+ p.y = leadRect.y + leadRect.height;
2146+ index = list.locationToIndex(p);
2147+ }
2148+ }
2149+ }
2150+ }
2151+ return index;
2152+ }
2153+
2154+ private void changeSelection(JList list, int type,
2155+ int index, int direction) {
2156+ if (index >= 0 && index < list.getModel().getSize()) {
2157+ ListSelectionModel lsm = list.getSelectionModel();
2158+
2159+ // CHANGE_LEAD is only valid with multiple interval selection
2160+ if (type == CHANGE_LEAD &&
2161+ list.getSelectionMode()
2162+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) {
2163+
2164+ type = CHANGE_SELECTION;
2165+ }
2166+
2167+ // IMPORTANT - This needs to happen before the index is changed.
2168+ // This is because JFileChooser, which uses JList, also scrolls
2169+ // the selected item into view. If that happens first, then
2170+ // this method becomes a no-op.
2171+ adjustScrollPositionIfNecessary(list, index, direction);
2172+
2173+ if (type == EXTEND_SELECTION) {
2174+ int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);
2175+ if (anchor == -1) {
2176+ anchor = 0;
2177+ }
2178+
2179+ list.setSelectionInterval(anchor, index);
2180+ }
2181+ else if (type == CHANGE_SELECTION) {
2182+ list.setSelectedIndex(index);
2183+ }
2184+ else {
2185+ // casting should be safe since the action is only enabled
2186+ // for DefaultListSelectionModel
2187+ ((DefaultListSelectionModel)lsm).moveLeadSelectionIndex(index);
2188+ }
2189+ }
2190+ }
2191+
2192+ /**
2193+ * When scroll down makes selected index the last completely visible
2194+ * index. When scroll up makes selected index the first visible index.
2195+ * Adjust visible rectangle respect to list's component orientation.
2196+ */
2197+ private void adjustScrollPositionIfNecessary(JList list, int index,
2198+ int direction) {
2199+ if (direction == 0) {
2200+ return;
2201+ }
2202+ Rectangle cellBounds = list.getCellBounds(index, index);
2203+ Rectangle visRect = list.getVisibleRect();
2204+ if (cellBounds != null && !visRect.contains(cellBounds)) {
2205+ if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
2206+ list.getVisibleRowCount() <= 0) {
2207+ // horizontal
2208+ if (list.getComponentOrientation().isLeftToRight()) {
2209+ if (direction > 0) {
2210+ // right for left-to-right
2211+ int x =Math.max(0,
2212+ cellBounds.x + cellBounds.width - visRect.width);
2213+ int startIndex =
2214+ list.locationToIndex(new Point(x, cellBounds.y));
2215+ Rectangle startRect = list.getCellBounds(startIndex,
2216+ startIndex);
2217+ if (startRect.x < x && startRect.x < cellBounds.x) {
2218+ startRect.x += startRect.width;
2219+ startIndex =
2220+ list.locationToIndex(startRect.getLocation());
2221+ startRect = list.getCellBounds(startIndex,
2222+ startIndex);
2223+ }
2224+ cellBounds = startRect;
2225+ }
2226+ cellBounds.width = visRect.width;
2227+ }
2228+ else {
2229+ if (direction > 0) {
2230+ // left for right-to-left
2231+ int x = cellBounds.x + visRect.width;
2232+ int rightIndex =
2233+ list.locationToIndex(new Point(x, cellBounds.y));
2234+ Rectangle rightRect = list.getCellBounds(rightIndex,
2235+ rightIndex);
2236+ if (rightRect.x + rightRect.width > x &&
2237+ rightRect.x > cellBounds.x) {
2238+ rightRect.width = 0;
2239+ }
2240+ cellBounds.x = Math.max(0,
2241+ rightRect.x + rightRect.width - visRect.width);
2242+ cellBounds.width = visRect.width;
2243+ }
2244+ else {
2245+ cellBounds.x += Math.max(0,
2246+ cellBounds.width - visRect.width);
2247+ // adjust width to fit into visible rectangle
2248+ cellBounds.width = Math.min(cellBounds.width,
2249+ visRect.width);
2250+ }
2251+ }
2252+ }
2253+ else {
2254+ // vertical
2255+ if (direction > 0 &&
2256+ (cellBounds.y < visRect.y ||
2257+ cellBounds.y + cellBounds.height
2258+ > visRect.y + visRect.height)) {
2259+ //down
2260+ int y = Math.max(0,
2261+ cellBounds.y + cellBounds.height - visRect.height);
2262+ int startIndex =
2263+ list.locationToIndex(new Point(cellBounds.x, y));
2264+ Rectangle startRect = list.getCellBounds(startIndex,
2265+ startIndex);
2266+ if (startRect.y < y && startRect.y < cellBounds.y) {
2267+ startRect.y += startRect.height;
2268+ startIndex =
2269+ list.locationToIndex(startRect.getLocation());
2270+ startRect =
2271+ list.getCellBounds(startIndex, startIndex);
2272+ }
2273+ cellBounds = startRect;
2274+ cellBounds.height = visRect.height;
2275+ }
2276+ else {
2277+ // adjust height to fit into visible rectangle
2278+ cellBounds.height = Math.min(cellBounds.height, visRect.height);
2279+ }
2280+ }
2281+ list.scrollRectToVisible(cellBounds);
2282+ }
2283+ }
2284+
2285+ private int getNextColumnIndex(JList list, BasicListUI ui,
2286+ int amount) {
2287+ if (list.getLayoutOrientation() != JList.VERTICAL) {
2288+ int index = adjustIndex(list.getLeadSelectionIndex(), list);
2289+ int size = list.getModel().getSize();
2290+
2291+ if (index == -1) {
2292+ return 0;
2293+ } else if (size == 1) {
2294+ // there's only one item so we should select it
2295+ return 0;
2296+ } else if (ui == null || ui.columnCount <= 1) {
2297+ return -1;
2298+ }
2299+
2300+ int column = ui.convertModelToColumn(index);
2301+ int row = ui.convertModelToRow(index);
2302+
2303+ column += amount;
2304+ if (column >= ui.columnCount || column < 0) {
2305+ // No wrapping.
2306+ return -1;
2307+ }
2308+ int maxRowCount = ui.getRowCount(column);
2309+ if (row >= maxRowCount) {
2310+ return -1;
2311+ }
2312+ return ui.getModelIndex(column, row);
2313+ }
2314+ // Won't change the selection.
2315+ return -1;
2316+ }
2317+
2318+ private int getNextIndex(JList list, BasicListUI ui, int amount) {
2319+ int index = adjustIndex(list.getLeadSelectionIndex(), list);
2320+ int size = list.getModel().getSize();
2321+
2322+ if (index == -1) {
2323+ if (size > 0) {
2324+ if (amount > 0) {
2325+ index = 0;
2326+ }
2327+ else {
2328+ index = size - 1;
2329+ }
2330+ }
2331+ } else if (size == 1) {
2332+ // there's only one item so we should select it
2333+ index = 0;
2334+ } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
2335+ if (ui != null) {
2336+ index += ui.columnCount * amount;
2337+ }
2338+ } else {
2339+ index += amount;
2340+ }
2341+
2342+ return index;
2343+ }
2344+ }
2345+
2346+
2347+ private class Handler implements FocusListener, KeyListener,
2348+ ListDataListener, ListSelectionListener,
2349+ MouseInputListener, PropertyChangeListener,
2350+ BeforeDrag {
2351+ //
2352+ // KeyListener
2353+ //
2354+ private String prefix = "";
2355+ private String typedString = "";
2356+ private long lastTime = 0L;
2357+
2358+ /**
2359+ * Invoked when a key has been typed.
2360+ *
2361+ * Moves the keyboard focus to the first element whose prefix matches the
2362+ * sequence of alphanumeric keys pressed by the user with delay less
2363+ * than value of <code>timeFactor</code> property (or 1000 milliseconds
2364+ * if it is not defined). Subsequent same key presses move the keyboard
2365+ * focus to the next object that starts with the same letter until another
2366+ * key is pressed, then it is treated as the prefix with appropriate number
2367+ * of the same letters followed by first typed another letter.
2368+ */
2369+ public void keyTyped(KeyEvent e) {
2370+ JList src = (JList)e.getSource();
2371+ ListModel model = src.getModel();
2372+
2373+ if (model.getSize() == 0 || e.isAltDown() ||
2374+ BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
2375+ isNavigationKey(e)) {
2376+ // Nothing to select
2377+ return;
2378+ }
2379+ boolean startingFromSelection = true;
2380+
2381+ char c = e.getKeyChar();
2382+
2383+ long time = e.getWhen();
2384+ int startIndex = adjustIndex(src.getLeadSelectionIndex(), list);
2385+ if (time - lastTime < timeFactor) {
2386+ typedString += c;
2387+ if((prefix.length() == 1) && (c == prefix.charAt(0))) {
2388+ // Subsequent same key presses move the keyboard focus to the next
2389+ // object that starts with the same letter.
2390+ startIndex++;
2391+ } else {
2392+ prefix = typedString;
2393+ }
2394+ } else {
2395+ startIndex++;
2396+ typedString = "" + c;
2397+ prefix = typedString;
2398+ }
2399+ lastTime = time;
2400+
2401+ if (startIndex < 0 || startIndex >= model.getSize()) {
2402+ startingFromSelection = false;
2403+ startIndex = 0;
2404+ }
2405+ int index = src.getNextMatch(prefix, startIndex,
2406+ Position.Bias.Forward);
2407+ if (index >= 0) {
2408+ src.setSelectedIndex(index);
2409+ src.ensureIndexIsVisible(index);
2410+ } else if (startingFromSelection) { // wrap
2411+ index = src.getNextMatch(prefix, 0,
2412+ Position.Bias.Forward);
2413+ if (index >= 0) {
2414+ src.setSelectedIndex(index);
2415+ src.ensureIndexIsVisible(index);
2416+ }
2417+ }
2418+ }
2419+
2420+ /**
2421+ * Invoked when a key has been pressed.
2422+ *
2423+ * Checks to see if the key event is a navigation key to prevent
2424+ * dispatching these keys for the first letter navigation.
2425+ */
2426+ public void keyPressed(KeyEvent e) {
2427+ if ( isNavigationKey(e) ) {
2428+ prefix = "";
2429+ typedString = "";
2430+ lastTime = 0L;
2431+ }
2432+ }
2433+
2434+ /**
2435+ * Invoked when a key has been released.
2436+ * See the class description for {@link KeyEvent} for a definition of
2437+ * a key released event.
2438+ */
2439+ public void keyReleased(KeyEvent e) {
2440+ }
2441+
2442+ /**
2443+ * Returns whether or not the supplied key event maps to a key that is used for
2444+ * navigation. This is used for optimizing key input by only passing non-
2445+ * navigation keys to the first letter navigation mechanism.
2446+ */
2447+ private boolean isNavigationKey(KeyEvent event) {
2448+ InputMap inputMap = list.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
2449+ KeyStroke key = KeyStroke.getKeyStrokeForEvent(event);
2450+
2451+ if (inputMap != null && inputMap.get(key) != null) {
2452+ return true;
2453+ }
2454+ return false;
2455+ }
2456+
2457+ //
2458+ // PropertyChangeListener
2459+ //
2460+ public void propertyChange(PropertyChangeEvent e) {
2461+ String propertyName = e.getPropertyName();
2462+
2463+ /* If the JList.model property changes, remove our listener,
2464+ * listDataListener from the old model and add it to the new one.
2465+ */
2466+ if (propertyName == "model") {
2467+ ListModel oldModel = (ListModel)e.getOldValue();
2468+ ListModel newModel = (ListModel)e.getNewValue();
2469+ if (oldModel != null) {
2470+ oldModel.removeListDataListener(listDataListener);
2471+ }
2472+ if (newModel != null) {
2473+ newModel.addListDataListener(listDataListener);
2474+ }
2475+ updateLayoutStateNeeded |= modelChanged;
2476+ redrawList();
2477+ }
2478+
2479+ /* If the JList.selectionModel property changes, remove our listener,
2480+ * listSelectionListener from the old selectionModel and add it to the new one.
2481+ */
2482+ else if (propertyName == "selectionModel") {
2483+ ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
2484+ ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
2485+ if (oldModel != null) {
2486+ oldModel.removeListSelectionListener(listSelectionListener);
2487+ }
2488+ if (newModel != null) {
2489+ newModel.addListSelectionListener(listSelectionListener);
2490+ }
2491+ updateLayoutStateNeeded |= modelChanged;
2492+ redrawList();
2493+ }
2494+ else if (propertyName == "cellRenderer") {
2495+ updateLayoutStateNeeded |= cellRendererChanged;
2496+ redrawList();
2497+ }
2498+ else if (propertyName == "font") {
2499+ updateLayoutStateNeeded |= fontChanged;
2500+ redrawList();
2501+ }
2502+ else if (propertyName == "prototypeCellValue") {
2503+ updateLayoutStateNeeded |= prototypeCellValueChanged;
2504+ redrawList();
2505+ }
2506+ else if (propertyName == "fixedCellHeight") {
2507+ updateLayoutStateNeeded |= fixedCellHeightChanged;
2508+ redrawList();
2509+ }
2510+ else if (propertyName == "fixedCellWidth") {
2511+ updateLayoutStateNeeded |= fixedCellWidthChanged;
2512+ redrawList();
2513+ }
2514+ else if (propertyName == "selectionForeground") {
2515+ list.repaint();
2516+ }
2517+ else if (propertyName == "selectionBackground") {
2518+ list.repaint();
2519+ }
2520+ else if ("layoutOrientation" == propertyName) {
2521+ updateLayoutStateNeeded |= layoutOrientationChanged;
2522+ layoutOrientation = list.getLayoutOrientation();
2523+ redrawList();
2524+ }
2525+ else if ("visibleRowCount" == propertyName) {
2526+ if (layoutOrientation != JList.VERTICAL) {
2527+ updateLayoutStateNeeded |= layoutOrientationChanged;
2528+ redrawList();
2529+ }
2530+ }
2531+ else if ("componentOrientation" == propertyName) {
2532+ isLeftToRight = list.getComponentOrientation().isLeftToRight();
2533+ updateLayoutStateNeeded |= componentOrientationChanged;
2534+ redrawList();
2535+
2536+ InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
2537+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
2538+ inputMap);
2539+ } else if ("List.isFileList" == propertyName) {
2540+ updateIsFileList();
2541+ redrawList();
2542+ } else if ("dropLocation" == propertyName) {
2543+ JList.DropLocation oldValue = (JList.DropLocation)e.getOldValue();
2544+ repaintDropLocation(oldValue);
2545+ repaintDropLocation(list.getDropLocation());
2546+ }
2547+ }
2548+
2549+ private void repaintDropLocation(JList.DropLocation loc) {
2550+ if (loc == null) {
2551+ return;
2552+ }
2553+
2554+ Rectangle r;
2555+
2556+ if (loc.isInsert()) {
2557+ r = getDropLineRect(loc);
2558+ } else {
2559+ r = getCellBounds(list, loc.getIndex());
2560+ }
2561+
2562+ if (r != null) {
2563+ list.repaint(r);
2564+ }
2565+ }
2566+
2567+ //
2568+ // ListDataListener
2569+ //
2570+ public void intervalAdded(ListDataEvent e) {
2571+ updateLayoutStateNeeded = modelChanged;
2572+
2573+ int minIndex = Math.min(e.getIndex0(), e.getIndex1());
2574+ int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
2575+
2576+ /* Sync the SelectionModel with the DataModel.
2577+ */
2578+
2579+ ListSelectionModel sm = list.getSelectionModel();
2580+ if (sm != null) {
2581+ sm.insertIndexInterval(minIndex, maxIndex - minIndex+1, true);
2582+ }
2583+
2584+ /* Repaint the entire list, from the origin of
2585+ * the first added cell, to the bottom of the
2586+ * component.
2587+ */
2588+ redrawList();
2589+ }
2590+
2591+
2592+ public void intervalRemoved(ListDataEvent e)
2593+ {
2594+ updateLayoutStateNeeded = modelChanged;
2595+
2596+ /* Sync the SelectionModel with the DataModel.
2597+ */
2598+
2599+ ListSelectionModel sm = list.getSelectionModel();
2600+ if (sm != null) {
2601+ sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
2602+ }
2603+
2604+ /* Repaint the entire list, from the origin of
2605+ * the first removed cell, to the bottom of the
2606+ * component.
2607+ */
2608+
2609+ redrawList();
2610+ }
2611+
2612+
2613+ public void contentsChanged(ListDataEvent e) {
2614+ updateLayoutStateNeeded = modelChanged;
2615+ redrawList();
2616+ }
2617+
2618+
2619+ //
2620+ // ListSelectionListener
2621+ //
2622+ public void valueChanged(ListSelectionEvent e) {
2623+ maybeUpdateLayoutState();
2624+
2625+ int size = list.getModel().getSize();
2626+ int firstIndex = Math.min(size - 1, Math.max(e.getFirstIndex(), 0));
2627+ int lastIndex = Math.min(size - 1, Math.max(e.getLastIndex(), 0));
2628+
2629+ Rectangle bounds = getCellBounds(list, firstIndex, lastIndex);
2630+
2631+ if (bounds != null) {
2632+ list.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
2633+ }
2634+ }
2635+
2636+ //
2637+ // MouseListener
2638+ //
2639+ public void mouseClicked(MouseEvent e) {
2640+ }
2641+
2642+ public void mouseEntered(MouseEvent e) {
2643+ }
2644+
2645+ public void mouseExited(MouseEvent e) {
2646+ }
2647+
2648+ // Whether or not the mouse press (which is being considered as part
2649+ // of a drag sequence) also caused the selection change to be fully
2650+ // processed.
2651+ private boolean dragPressDidSelection;
2652+
2653+ public void mousePressed(MouseEvent e) {
2654+ if (SwingUtilities2.shouldIgnore(e, list)) {
2655+ return;
2656+ }
2657+
2658+ boolean dragEnabled = list.getDragEnabled();
2659+ boolean grabFocus = true;
2660+
2661+ // different behavior if drag is enabled
2662+ if (dragEnabled) {
2663+ int row = SwingUtilities2.loc2IndexFileList(list, e.getPoint());
2664+ // if we have a valid row and this is a drag initiating event
2665+ if (row != -1 && DragRecognitionSupport.mousePressed(e)) {
2666+ dragPressDidSelection = false;
2667+
2668+ if (BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
2669+ // do nothing for control - will be handled on release
2670+ // or when drag starts
2671+ return;
2672+ } else if (!e.isShiftDown() && list.isSelectedIndex(row)) {
2673+ // clicking on something that's already selected
2674+ // and need to make it the lead now
2675+ list.addSelectionInterval(row, row);
2676+ return;
2677+ }
2678+
2679+ // could be a drag initiating event - don't grab focus
2680+ grabFocus = false;
2681+
2682+ dragPressDidSelection = true;
2683+ }
2684+ } else {
2685+ // When drag is enabled mouse drags won't change the selection
2686+ // in the list, so we only set the isAdjusting flag when it's
2687+ // not enabled
2688+ list.setValueIsAdjusting(true);
2689+ }
2690+
2691+ if (grabFocus) {
2692+ SwingUtilities2.adjustFocus(list);
2693+ }
2694+
2695+ adjustSelection(e);
2696+ }
2697+
2698+ private void adjustSelection(MouseEvent e) {
2699+ int row = SwingUtilities2.loc2IndexFileList(list, e.getPoint());
2700+ if (row < 0) {
2701+ // If shift is down in multi-select, we should do nothing.
2702+ // For single select or non-shift-click, clear the selection
2703+ if (isFileList &&
2704+ e.getID() == MouseEvent.MOUSE_PRESSED &&
2705+ (!e.isShiftDown() ||
2706+ list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
2707+ list.clearSelection();
2708+ }
2709+ }
2710+ else {
2711+ int anchorIndex = adjustIndex(list.getAnchorSelectionIndex(), list);
2712+ boolean anchorSelected;
2713+ if (anchorIndex == -1) {
2714+ anchorIndex = 0;
2715+ anchorSelected = false;
2716+ } else {
2717+ anchorSelected = list.isSelectedIndex(anchorIndex);
2718+ }
2719+
2720+ if (BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
2721+ if (e.isShiftDown()) {
2722+ if (anchorSelected) {
2723+ list.addSelectionInterval(anchorIndex, row);
2724+ } else {
2725+ list.removeSelectionInterval(anchorIndex, row);
2726+ if (isFileList) {
2727+ list.addSelectionInterval(row, row);
2728+ list.getSelectionModel().setAnchorSelectionIndex(anchorIndex);
2729+ }
2730+ }
2731+ } else if (list.isSelectedIndex(row)) {
2732+ list.removeSelectionInterval(row, row);
2733+ } else {
2734+ list.addSelectionInterval(row, row);
2735+ }
2736+ } else if (e.isShiftDown()) {
2737+ list.setSelectionInterval(anchorIndex, row);
2738+ } else {
2739+ list.setSelectionInterval(row, row);
2740+ }
2741+ }
2742+ }
2743+
2744+ public void dragStarting(MouseEvent me) {
2745+ if (BasicGraphicsUtils.isMenuShortcutKeyDown(me)) {
2746+ int row = SwingUtilities2.loc2IndexFileList(list, me.getPoint());
2747+ list.addSelectionInterval(row, row);
2748+ }
2749+ }
2750+
2751+ public void mouseDragged(MouseEvent e) {
2752+ if (SwingUtilities2.shouldIgnore(e, list)) {
2753+ return;
2754+ }
2755+
2756+ if (list.getDragEnabled()) {
2757+ DragRecognitionSupport.mouseDragged(e, this);
2758+ return;
2759+ }
2760+
2761+ if (e.isShiftDown() || BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
2762+ return;
2763+ }
2764+
2765+ int row = locationToIndex(list, e.getPoint());
2766+ if (row != -1) {
2767+ // 4835633. Dragging onto a File should not select it.
2768+ if (isFileList) {
2769+ return;
2770+ }
2771+ Rectangle cellBounds = getCellBounds(list, row, row);
2772+ if (cellBounds != null) {
2773+ list.scrollRectToVisible(cellBounds);
2774+ list.setSelectionInterval(row, row);
2775+ }
2776+ }
2777+ }
2778+
2779+ public void mouseMoved(MouseEvent e) {
2780+ }
2781+
2782+ public void mouseReleased(MouseEvent e) {
2783+ if (SwingUtilities2.shouldIgnore(e, list)) {
2784+ return;
2785+ }
2786+
2787+ if (list.getDragEnabled()) {
2788+ MouseEvent me = DragRecognitionSupport.mouseReleased(e);
2789+ if (me != null) {
2790+ SwingUtilities2.adjustFocus(list);
2791+ if (!dragPressDidSelection) {
2792+ adjustSelection(me);
2793+ }
2794+ }
2795+ } else {
2796+ list.setValueIsAdjusting(false);
2797+ }
2798+ }
2799+
2800+ //
2801+ // FocusListener
2802+ //
2803+ protected void repaintCellFocus()
2804+ {
2805+ int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list);
2806+ if (leadIndex != -1) {
2807+ Rectangle r = getCellBounds(list, leadIndex, leadIndex);
2808+ if (r != null) {
2809+ list.repaint(r.x, r.y, r.width, r.height);
2810+ }
2811+ }
2812+ }
2813+
2814+ /* The focusGained() focusLost() methods run when the JList
2815+ * focus changes.
2816+ */
2817+
2818+ public void focusGained(FocusEvent e) {
2819+ repaintCellFocus();
2820+ }
2821+
2822+ public void focusLost(FocusEvent e) {
2823+ repaintCellFocus();
2824+ }
2825+ }
2826+
2827+ private static int adjustIndex(int index, JList list) {
2828+ return index < list.getModel().getSize() ? index : -1;
2829+ }
2830+
2831+ private static final TransferHandler defaultTransferHandler = new ListTransferHandler();
2832+
2833+ static class ListTransferHandler extends TransferHandler implements UIResource {
2834+
2835+ /**
2836+ * Create a Transferable to use as the source for a data transfer.
2837+ *
2838+ * @param c The component holding the data to be transfered. This
2839+ * argument is provided to enable sharing of TransferHandlers by
2840+ * multiple components.
2841+ * @return The representation of the data to be transfered.
2842+ *
2843+ */
2844+ protected Transferable createTransferable(JComponent c) {
2845+ if (c instanceof JList) {
2846+ JList list = (JList) c;
2847+ Object[] values = list.getSelectedValues();
2848+
2849+ if (values == null || values.length == 0) {
2850+ return null;
2851+ }
2852+
2853+ StringBuffer plainBuf = new StringBuffer();
2854+ StringBuffer htmlBuf = new StringBuffer();
2855+
2856+ htmlBuf.append("<html>\n<body>\n<ul>\n");
2857+
2858+ for (int i = 0; i < values.length; i++) {
2859+ Object obj = values[i];
2860+ String val = ((obj == null) ? "" : obj.toString());
2861+ plainBuf.append(val + "\n");
2862+ htmlBuf.append(" <li>" + val + "\n");
2863+ }
2864+
2865+ // remove the last newline
2866+ plainBuf.deleteCharAt(plainBuf.length() - 1);
2867+ htmlBuf.append("</ul>\n</body>\n</html>");
2868+
2869+ return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());
2870+ }
2871+
2872+ return null;
2873+ }
2874+
2875+ public int getSourceActions(JComponent c) {
2876+ return COPY;
2877+ }
2878+
2879+ }
2880+}