1 /*
2  * Copyright (c) 2003, 2013, 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 
27 // Very much based on XListPeer from javaos
28 
29 package sun.awt.X11;
30 
31 import java.awt.*;
32 import java.awt.event.*;
33 import java.awt.peer.*;
34 import java.util.Objects;
35 import java.util.Vector;
36 import java.awt.image.*;
37 import sun.util.logging.PlatformLogger;
38 
39 // TODO: some input actions should do nothing if Shift or Control are down
40 
41 class XListPeer extends XComponentPeer implements ListPeer, XScrollbarClient {
42 
43     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XListPeer");
44 
45     public final static int     MARGIN = 2;
46     public final static int     SPACE = 1;
47     public final static int     SCROLLBAR_AREA = 17;  // Area reserved for the
48                                                       // scrollbar
49     public final static int     SCROLLBAR_WIDTH = 13; // Actual width of the
50                                                       // scrollbar
51     public final static int     NONE = -1;
52     public final static int     WINDOW = 0;
53     public final static int     VERSCROLLBAR = 1;
54     public final static int     HORSCROLLBAR = 2;
55     public final static int     DEFAULT_VISIBLE_ROWS = 4; // From java.awt.List,
56     public final static int     HORIZ_SCROLL_AMT = 10;
57 
58     private final static int    PAINT_VSCROLL = 2;
59     private final static int    PAINT_HSCROLL = 4;
60     private final static int    PAINT_ITEMS = 8;
61     private final static int    PAINT_FOCUS = 16;
62     private final static int    PAINT_BACKGROUND = 32;
63     private final static int    PAINT_HIDEFOCUS = 64;
64     private final static int    PAINT_ALL =
65         PAINT_VSCROLL | PAINT_HSCROLL | PAINT_ITEMS | PAINT_FOCUS | PAINT_BACKGROUND;
66     private final static int    COPY_AREA = 128;
67 
68     XVerticalScrollbar       vsb;
69     XHorizontalScrollbar     hsb;
70     ListPainter painter;
71 
72     // TODO: ick - Vector?
73     Vector                      items;
74     boolean                     multipleSelections;
75     int                         active = NONE;
76 
77     // Holds the array of the indexes of the elements which is selected
78     // This array should be kept sorted, low to high.
79     int                         selected[];
80     int                         fontHeight;
81     int                         fontAscent;
82     int                         fontLeading;
83 
84     // Holds the index of the item used in the previous operation (selectItem, deselectItem)
85     // Adding of an item or clearing of the list sets this index to -1
86     // The index is used at the moment of the post of ACTION_PERFORMED event after the mouse double click event.
87     int                         currentIndex = -1;
88 
89     // Used for tracking selection/deselection between mousePress/Release
90     // and for ItemEvents
91     int                         eventIndex = -1;
92     int                         eventType = NONE;
93 
94     // Holds the index of the item that receive focus
95     // This variable is reasonable only for multiple list
96     // since 'focusIndex' and 'selected[0]' are equal for single-selection list
97     int                         focusIndex;
98 
99     int                         maxLength;
100     boolean                     vsbVis;  // visibility of scrollbars
101     boolean                     hsbVis;
102     int                         listWidth;  // Width of list portion of List
103     int                         listHeight; // Height of list portion of List
104     // (i.e. without scrollbars)
105 
106     private int firstTimeVisibleIndex = 0;
107 
108     // Motif Lists don't seem to inherit the background color from their
109     // parent when an app is first started up.  So, we track if the colors have
110     // been set.  See getListBackground()/getListForeground().
111     boolean bgColorSet;
112     boolean fgColorSet;
113 
114     // Holds the true if mouse is dragging outside of the area of the list
115     // The flag is used at the moment of the dragging and releasing mouse
116     // See 6243382 for more information
117     boolean mouseDraggedOutHorizontally = false;
118     boolean mouseDraggedOutVertically = false;
119 
120     // Holds the true if a mouse event was originated on the scrollbar
121     // See 6300527 for more information
122     boolean isScrollBarOriginated = false;
123 
124     // This variable is set to true after the "mouse pressed" event and to false after the "mouse released" event
125     // Fixed 6293432: Key events ('SPACE', 'UP', 'DOWN') aren't blocked if mouse is kept in 'PRESSED' state for List, XAWT
126     boolean isMousePressed = false;
127 
128     /**
129      * Create a list
130      */
XListPeer(List target)131     XListPeer(List target) {
132         super(target);
133     }
134 
135     /**
136      * Overridden from XWindow
137      */
preInit(XCreateWindowParams params)138     public void preInit(XCreateWindowParams params) {
139         super.preInit(params);
140 
141         // Stuff that must be initialized before layout() is called
142         items = new Vector();
143         createVerScrollbar();
144         createHorScrollbar();
145 
146         painter = new ListPainter();
147 
148         // See 6246467 for more information
149         bgColorSet = target.isBackgroundSet();
150         fgColorSet = target.isForegroundSet();
151     }
152 
postInit(XCreateWindowParams params)153     public void postInit(XCreateWindowParams params) {
154         super.postInit(params);
155         initFontMetrics();
156         // TODO: more efficient way?
157         //       do we really want/need a copy of all the items?
158         // get all items from target
159         List l = (List)target;
160         int stop = l.getItemCount();
161         for (int i = 0 ; i < stop; i++) {
162             items.addElement(l.getItem(i));
163         }
164 
165         /* make the visible position visible. */
166         int index = l.getVisibleIndex();
167         if (index >= 0) {
168             // Can't call makeVisible since it check scroll bar,
169             // initialize scroll bar instead
170             vsb.setValues(index, 0, 0, items.size());
171         }
172 
173         // NOTE: needs to have target set
174         maxLength = maxLength();
175 
176         // get the index containing all indexes to selected items
177         int sel[] = l.getSelectedIndexes();
178         selected = new int[sel.length];
179         // TODO: shouldn't this be arraycopy()?
180         for (int i = 0 ; i < sel.length ; i ++) {
181             selected[i] = sel[i];
182         }
183         // The select()ed item should become the focused item, but we don't
184         // get the select() call because the peer generally hasn't yet been
185         // created during app initialization.
186         // TODO: For multi-select lists, it should be the highest selected index
187         if (sel.length > 0) {
188             setFocusIndex(sel[sel.length - 1]);
189         }
190         else {
191             setFocusIndex(0);
192         }
193 
194         multipleSelections = l.isMultipleMode();
195     }
196 
197 
198     /**
199      * add Vertical Scrollbar
200      */
createVerScrollbar()201     void createVerScrollbar() {
202         vsb = new XVerticalScrollbar(this);
203         vsb.setValues(0, 0, 0, 0, 1, 1);
204     }
205 
206 
207     /**
208      * add Horizontal scrollbar
209      */
createHorScrollbar()210     void createHorScrollbar() {
211         hsb = new XHorizontalScrollbar(this);
212         hsb.setValues(0, 0, 0, 0, HORIZ_SCROLL_AMT, HORIZ_SCROLL_AMT);
213     }
214 
215     /* New method name for 1.1 */
add(String item, int index)216     public void add(String item, int index) {
217         addItem(item, index);
218     }
219 
220     /* New method name for 1.1 */
removeAll()221     public void removeAll() {
222         clear();
223         maxLength = 0;
224     }
225 
226     /* New method name for 1.1 */
setMultipleMode(boolean b)227     public void setMultipleMode (boolean b) {
228         setMultipleSelections(b);
229     }
230 
231     /* New method name for 1.1 */
getPreferredSize(int rows)232     public Dimension getPreferredSize(int rows) {
233         return preferredSize(rows);
234     }
235 
236     /* New method name for 1.1 */
getMinimumSize(int rows)237     public Dimension getMinimumSize(int rows) {
238         return minimumSize(rows);
239     }
240 
241     /**
242      * Minimum size.
243      */
minimumSize()244     public Dimension minimumSize() {
245         return minimumSize(DEFAULT_VISIBLE_ROWS);
246     }
247 
248     /**
249      * return the preferredSize
250      */
preferredSize(int v)251     public Dimension preferredSize(int v) {
252         return minimumSize(v);
253     }
254 
255     /**
256      * return the minimumsize
257      */
minimumSize(int v)258     public Dimension minimumSize(int v) {
259         FontMetrics fm = getFontMetrics(getFont());
260         initFontMetrics();
261         return new Dimension(20 + fm.stringWidth("0123456789abcde"),
262                              getItemHeight() * v + (2*MARGIN));
263     }
264 
265     /**
266      * Calculate font metrics
267      */
initFontMetrics()268     void initFontMetrics() {
269         FontMetrics fm = getFontMetrics(getFont());
270         fontHeight = fm.getHeight();
271         fontAscent = fm.getAscent();
272         fontLeading = fm.getLeading();
273     }
274 
275 
276     /**
277      * return the length of the largest item in the list
278      */
maxLength()279     int maxLength() {
280         FontMetrics fm = getFontMetrics(getFont());
281         int m = 0;
282         int end = items.size();
283         for(int i = 0 ; i < end ; i++) {
284             int l = fm.stringWidth(((String)items.elementAt(i)));
285             m = Math.max(m, l);
286         }
287         return m;
288     }
289 
290     /**
291      * Calculates the width of item's label
292      */
getItemWidth(int i)293     int getItemWidth(int i) {
294         FontMetrics fm = getFontMetrics(getFont());
295         return fm.stringWidth((String)items.elementAt(i));
296     }
297 
298     /**
299      * return the on-screen width of the given string "str"
300      */
stringLength(String str)301     int stringLength(String str) {
302         FontMetrics fm = getFontMetrics(target.getFont());
303         return fm.stringWidth(str);
304     }
305 
setForeground(Color c)306     public void setForeground(Color c) {
307         fgColorSet = true;
308         super.setForeground(c);
309     }
310 
setBackground(Color c)311     public void setBackground(Color c) {
312         bgColorSet = true;
313         super.setBackground(c);
314     }
315 
316     /**
317      * Returns the color that should be used to paint the background of
318      * the list of items.  Note that this is not the same as
319      * target.getBackground() which is the color of the scrollbars, and the
320      * lower-right corner of the Component when the scrollbars are displayed.
321      */
getListBackground(Color[] colors)322     private Color getListBackground(Color[] colors) {
323         if (bgColorSet) {
324             return colors[BACKGROUND_COLOR];
325         }
326         else {
327             return SystemColor.text;
328         }
329     }
330 
331     /**
332      * Returns the color that should be used to paint the list item text.
333      */
getListForeground(Color[] colors)334     private Color getListForeground(Color[] colors) {
335         if (fgColorSet) {
336             return colors[FOREGROUND_COLOR];
337         }
338         else {
339             return SystemColor.textText;
340         }
341     }
342 
getVScrollBarRec()343     Rectangle getVScrollBarRec() {
344         return new Rectangle(width - (SCROLLBAR_WIDTH), 0, SCROLLBAR_WIDTH+1, height);
345     }
346 
getHScrollBarRec()347     Rectangle getHScrollBarRec() {
348         return new Rectangle(0, height - SCROLLBAR_WIDTH, width, SCROLLBAR_WIDTH);
349     }
350 
getFirstVisibleItem()351     int getFirstVisibleItem() {
352         if (vsbVis) {
353             return vsb.getValue();
354         } else {
355             return 0;
356         }
357     }
358 
getLastVisibleItem()359     int getLastVisibleItem() {
360         if (vsbVis) {
361             return Math.min(items.size()-1, vsb.getValue() + itemsInWindow() -1);
362         } else {
363             return Math.min(items.size()-1, itemsInWindow()-1);
364         }
365     }
repaintScrollbarRequest(XScrollbar scrollbar)366     public void repaintScrollbarRequest(XScrollbar scrollbar) {
367         if (scrollbar == hsb)  {
368             repaint(PAINT_HSCROLL);
369         }
370         else if (scrollbar == vsb) {
371             repaint(PAINT_VSCROLL);
372         }
373     }
374     /**
375      * Overridden for performance
376      */
repaint()377     public void repaint() {
378         repaint(getFirstVisibleItem(), getLastVisibleItem(), PAINT_ALL);
379     }
380 
repaint(int options)381     private void repaint(int options) {
382         repaint(getFirstVisibleItem(), getLastVisibleItem(), options);
383     }
384 
repaint(int firstItem, int lastItem, int options)385     private void repaint(int firstItem, int lastItem, int options) {
386         repaint(firstItem, lastItem, options, null, null);
387     }
388 
389     /**
390      * In most cases the entire area of the component doesn't have
391      * to be repainted. The method repaints the particular areas of
392      * the component. The areas to repaint is specified by the option
393      * parameter. The possible values of the option parameter are:
394      * PAINT_VSCROLL, PAINT_HSCROLL, PAINT_ITEMS, PAINT_FOCUS,
395      * PAINT_HIDEFOCUS, PAINT_BACKGROUND, PAINT_ALL, COPY_AREA.
396      *
397      * Note that the COPY_AREA value initiates copy of a source area
398      * of the component by a distance by means of the copyArea method
399      * of the Graphics class.
400      *
401      * @param firstItem the position of the first item of the range to repaint
402      * @param lastItem the position of the last item of the range to repaint
403      * @param options specifies the particular area of the component to repaint
404      * @param source the area of the component to copy
405      * @param distance the distance to copy the source area
406      */
repaint(int firstItem, int lastItem, int options, Rectangle source, Point distance)407     private void repaint(int firstItem, int lastItem, int options, Rectangle source, Point distance) {
408         final Graphics g = getGraphics();
409         if (g != null) {
410             try {
411                 painter.paint(g, firstItem, lastItem, options, source, distance);
412                 postPaintEvent(target, 0, 0, getWidth(), getHeight());
413             } finally {
414                 g.dispose();
415             }
416         }
417     }
418     @Override
paintPeer(final Graphics g)419     void paintPeer(final Graphics g) {
420         painter.paint(g, getFirstVisibleItem(), getLastVisibleItem(), PAINT_ALL);
421     }
isFocusable()422     public boolean isFocusable() { return true; }
423 
424     // TODO: share/promote the Focus methods?
focusGained(FocusEvent e)425     public void focusGained(FocusEvent e) {
426         super.focusGained(e);
427         repaint(PAINT_FOCUS);
428     }
429 
focusLost(FocusEvent e)430     public void focusLost(FocusEvent e) {
431         super.focusLost(e);
432         repaint(PAINT_FOCUS);
433     }
434 
435     /**
436      * Layout the sub-components of the List - that is, the scrollbars and the
437      * list of items.
438      */
layout()439     public void layout() {
440         int vis, maximum;
441         boolean vsbWasVisible;
442         int origVSBVal;
443         assert(target != null);
444 
445         // Start with assumption there is not a horizontal scrollbar,
446         // see if we need a vertical scrollbar
447 
448         // Bug: If the list DOES have a horiz scrollbar and the value is set to
449         // the very bottom value, value is reset in setValues() because it isn't
450         // a valid value for cases when the list DOESN'T have a horiz scrollbar.
451         // This is currently worked-around with origVSGVal.
452         origVSBVal = vsb.getValue();
453         vis = itemsInWindow(false);
454         maximum = items.size() < vis ? vis : items.size();
455         vsb.setValues(vsb.getValue(), vis, vsb.getMinimum(), maximum);
456         vsbVis = vsbWasVisible = vsbIsVisible(false);
457         listHeight = height;
458 
459         // now see if we need a horizontal scrollbar
460         listWidth = getListWidth();
461         vis = listWidth - ((2 * SPACE) + (2 * MARGIN));
462         maximum = maxLength < vis ? vis : maxLength;
463         hsb.setValues(hsb.getValue(), vis, hsb.getMinimum(), maximum);
464         hsbVis = hsbIsVisible(vsbVis);
465 
466         if (hsbVis) {
467             // do need a horizontal scrollbar, so recalculate height of
468             // vertical s crollbar
469             listHeight = height - SCROLLBAR_AREA;
470             vis = itemsInWindow(true);
471             maximum = items.size() < vis ? vis : items.size();
472             vsb.setValues(origVSBVal, vis, vsb.getMinimum(), maximum);
473             vsbVis = vsbIsVisible(true);
474         }
475 
476         // now check to make sure we haven't changed need for vertical
477         // scrollbar - if we have, we need to
478         // recalculate horizontal scrollbar width - then we're done...
479         if (vsbWasVisible != vsbVis) {
480             listWidth = getListWidth();
481             vis = listWidth - ((2 * SPACE) + (2 * MARGIN));
482             maximum = maxLength < vis ? 0 : maxLength;
483             hsb.setValues(hsb.getValue(), vis, hsb.getMinimum(), maximum);
484             hsbVis = hsbIsVisible(vsbVis);
485         }
486 
487         vsb.setSize(SCROLLBAR_WIDTH, listHeight);
488         hsb.setSize(listWidth, SCROLLBAR_WIDTH);
489 
490         vsb.setBlockIncrement(itemsInWindow());
491         hsb.setBlockIncrement(width - ((2 * SPACE) + (2 * MARGIN) + (vsbVis ? SCROLLBAR_AREA : 0)));
492     }
493 
494     int getItemWidth() {
495         return width - ((2 * MARGIN) + (vsbVis ? SCROLLBAR_AREA : 0));
496     }
497 
498     /* Returns height of an item in the list */
499     int getItemHeight() {
500         return (fontHeight - fontLeading) + (2*SPACE);
501     }
502 
503     int getItemX() {
504         return MARGIN + SPACE;
505     }
506 
507     int getItemY(int item) {
508         return index2y(item);
509     }
510 
511     int getFocusIndex() {
512         return focusIndex;
513     }
514 
515     void setFocusIndex(int value) {
516         focusIndex = value;
517     }
518 
519     /**
520      * Update and return the focus rectangle.
521      * Focus is around the focused item, if it is visible, or
522      * around the border of the list if the focused item is scrolled off the top
523      * or bottom of the list.
524      */
525     Rectangle getFocusRect() {
526         Rectangle focusRect = new Rectangle();
527         // width is always only based on presence of vert sb
528         focusRect.x = 1;
529         focusRect.width = getListWidth() - 3;
530         // if focused item is not currently displayed in the list,  paint
531         // focus around entire list (not including scrollbars)
532         if (isIndexDisplayed(getFocusIndex())) {
533             // focus rect is around the item
534             focusRect.y = index2y(getFocusIndex()) - 2;
535             focusRect.height = getItemHeight()+1;
536         } else {
537             // focus rect is around the list
538             focusRect.y = 1;
539             focusRect.height = hsbVis ? height - SCROLLBAR_AREA : height;
540             focusRect.height -= 3;
541         }
542         return focusRect;
543     }
544 
545     public void handleConfigureNotifyEvent(XEvent xev) {
546         super.handleConfigureNotifyEvent(xev);
547 
548         // Update buffer
549         painter.invalidate();
550     }
551     public boolean handlesWheelScrolling() { return true; }
552 
553     // FIXME: need to support MouseWheel scrolling, too
554     void handleJavaMouseEvent(MouseEvent e) {
555         super.handleJavaMouseEvent(e);
556         int i = e.getID();
557         switch (i) {
558           case MouseEvent.MOUSE_PRESSED:
559               mousePressed(e);
560               break;
561           case MouseEvent.MOUSE_RELEASED:
562               mouseReleased(e);
563               break;
564           case MouseEvent.MOUSE_DRAGGED:
565               mouseDragged(e);
566               break;
567         }
568     }
569 
570     void handleJavaMouseWheelEvent(MouseWheelEvent e) {
571         if (ListHelper.doWheelScroll(vsbVis ? vsb : null,
572                                      hsbVis ? hsb : null, e)) {
573             repaint();
574         }
575     }
576 
577     void mousePressed(MouseEvent mouseEvent) {
578         if (log.isLoggable(PlatformLogger.Level.FINER)) {
579             log.finer(mouseEvent.toString() + ", hsb " + hsbVis + ", vsb " + vsbVis);
580         }
581         if (isEnabled() && mouseEvent.getButton() == MouseEvent.BUTTON1) {
582             if (inWindow(mouseEvent.getX(), mouseEvent.getY())) {
583                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
584                     log.fine("Mouse press in items area");
585                 }
586                 active = WINDOW;
587                 int i = y2index(mouseEvent.getY());
588                 if (i >= 0) {
589                     if (multipleSelections) {
590                         if (isSelected(i)) {
591                             // See 6243382 for more information
592                             deselectItem(i);
593                             eventIndex = i;
594                             eventType = ItemEvent.DESELECTED;
595                         }
596                         else {
597                             selectItem(i);
598                             eventIndex = i;
599                             eventType = ItemEvent.SELECTED;
600                         }
601                     }
602                     // Backward-compatible bug: even if a single-select
603                     // item is already selected, we send an ITEM_STATE_CHANGED/
604                     // SELECTED event.  Engineer's Toolbox appears to rely on
605                     // this.
606                     //else if (!isSelected(i)) {
607                     else {
608                         selectItem(i);
609                         eventIndex = i;
610                         eventType = ItemEvent.SELECTED;
611                     }
612                     // Restoring Windows behaviour
613                     // We should update focus index after "mouse pressed" event
614                     setFocusIndex(i);
615                     repaint(PAINT_FOCUS);
616                 } else {
617                     // 6426186: reset variable to prevent action event
618                     // if user clicks on unoccupied area of list
619                     currentIndex = -1;
620                 }
621             } else if (inVerticalScrollbar(mouseEvent.getX(), mouseEvent.getY())) {
622                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
623                     log.fine("Mouse press in vertical scrollbar");
624                 }
625                 active = VERSCROLLBAR;
626                 vsb.handleMouseEvent(mouseEvent.getID(),
627                                      mouseEvent.getModifiers(),
628                                      mouseEvent.getX() - (width - SCROLLBAR_WIDTH),
629                                      mouseEvent.getY());
630             } else if (inHorizontalScrollbar(mouseEvent.getX(), mouseEvent.getY())) {
631                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
632                     log.fine("Mouse press in horizontal scrollbar");
633                 }
634                 active = HORSCROLLBAR;
635                 hsb.handleMouseEvent(mouseEvent.getID(),
636                                      mouseEvent.getModifiers(),
637                                      mouseEvent.getX(),
638                                      mouseEvent.getY() - (height - SCROLLBAR_WIDTH));
639 
640             }
641             isMousePressed = true;
642         }
643     }
644     void mouseReleased(MouseEvent mouseEvent) {
645         if (isEnabled() && mouseEvent.getButton() == MouseEvent.BUTTON1) {
646             //winReleaseCursorFocus();
647             int clickCount = mouseEvent.getClickCount();
648             if (active == VERSCROLLBAR) {
649                 vsb.handleMouseEvent(mouseEvent.getID(),
650                                      mouseEvent.getModifiers(),
651                                      mouseEvent.getX()-(width-SCROLLBAR_WIDTH),
652                                      mouseEvent.getY());
653             } else if(active == HORSCROLLBAR) {
654                 hsb.handleMouseEvent(mouseEvent.getID(),
655                                      mouseEvent.getModifiers(),
656                                      mouseEvent.getX(),
657                                      mouseEvent.getY()-(height-SCROLLBAR_WIDTH));
658             } else if ( ( currentIndex >= 0 ) && ( clickCount >= 2 ) &&
659                         ( clickCount % 2 == 0 ) ) {
660                 postEvent(new ActionEvent(target,
661                                           ActionEvent.ACTION_PERFORMED,
662                                           (String)items.elementAt(currentIndex),
663                                           mouseEvent.getWhen(),
664                                           mouseEvent.getModifiers()));  // No ext mods
665             } else if (active == WINDOW) {
666                 // See 6243382 for more information
667                 trackMouseReleasedScroll();
668 
669                 if (eventType == ItemEvent.DESELECTED) {
670                     assert multipleSelections : "Shouldn't get a deselect for a single-select List";
671                     // Paint deselection the release
672                     deselectItem(eventIndex);
673                 }
674                 if (eventType != NONE) {
675                     postEvent(new ItemEvent((List)target,
676                                 ItemEvent.ITEM_STATE_CHANGED,
677                                 Integer.valueOf(eventIndex),
678                                 eventType));
679                 }
680             }
681             active = NONE;
682             eventIndex = -1;
683             eventType = NONE;
684             isMousePressed = false;
685         }
686     }
687 
688     void mouseDragged(MouseEvent mouseEvent) {
689         // TODO: can you drag w/ any other buttons?  what about multiple buttons?
690         if (isEnabled() &&
691             (mouseEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) {
692             if ((active == VERSCROLLBAR)) {
693                 vsb.handleMouseEvent(mouseEvent.getID(),
694                                      mouseEvent.getModifiers(),
695                                      mouseEvent.getX()-(width-SCROLLBAR_WIDTH),
696                                      mouseEvent.getY());
697             } else if ((active == HORSCROLLBAR)) {
698                 hsb.handleMouseEvent(mouseEvent.getID(),
699                                      mouseEvent.getModifiers(),
700                                      mouseEvent.getX(),
701                                      mouseEvent.getY()-(height-SCROLLBAR_WIDTH));
702             } else if (active == WINDOW) {
703                 int i = y2index(mouseEvent.getY());
704                 if (multipleSelections) {
705                     // Multi-select only:
706                     // If a selected item was pressed on and then dragged off
707                     // of, cancel the pending deselect.
708                     if (eventType == ItemEvent.DESELECTED) {
709                         if (i != eventIndex) {
710                             eventType = NONE;
711                             eventIndex = -1;
712                         }
713                     }
714                 }
715                 else if (eventType == ItemEvent.SELECTED) {
716                     // Single-select only:
717                     // If an unselected item was pressed on, track the drag
718                     // and select the item under the mouse
719 
720                     // See 6243382 for more information
721                     trackMouseDraggedScroll(mouseEvent);
722 
723                     if (i >= 0 && !isSelected(i)) {
724                         int oldSel = eventIndex;
725                         selectItem(i);
726                         eventIndex = i;
727                         repaint(oldSel, eventIndex, PAINT_ITEMS);
728                     }
729                 }
730                 // Restoring Windows behaviour
731                 // We should update focus index after "mouse dragged" event
732                 if (i >= 0) {
733                     setFocusIndex(i);
734                     repaint(PAINT_FOCUS);
735                 }
736             }
737         }
738     }
739 
740     /*
741      * Helper method for XListPeer with integrated vertical scrollbar.
742      * Start or stop vertical scrolling when mouse dragged in / out the area of the list if it's required
743      * Restoring Motif behavior
744      * See 6243382 for more information
745      */
746     void trackMouseDraggedScroll(MouseEvent mouseEvent){
747 
748         if (vsb.beforeThumb(mouseEvent.getX(), mouseEvent.getY())) {
749             vsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
750         } else {
751             vsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
752         }
753 
754         if(mouseEvent.getY() < 0 || mouseEvent.getY() >= listHeight){
755             if (!mouseDraggedOutVertically){
756                 mouseDraggedOutVertically = true;
757                 vsb.startScrollingInstance();
758             }
759         }else{
760             if (mouseDraggedOutVertically){
761                 mouseDraggedOutVertically = false;
762                 vsb.stopScrollingInstance();
763             }
764         }
765 
766         if (hsb.beforeThumb(mouseEvent.getX(), mouseEvent.getY())) {
767             hsb.setMode(AdjustmentEvent.UNIT_DECREMENT);
768         } else {
769             hsb.setMode(AdjustmentEvent.UNIT_INCREMENT);
770         }
771 
772         if (mouseEvent.getX() < 0 || mouseEvent.getX() >= listWidth) {
773             if (!mouseDraggedOutHorizontally){
774                 mouseDraggedOutHorizontally = true;
775                 hsb.startScrollingInstance();
776             }
777         }else{
778             if (mouseDraggedOutHorizontally){
779                 mouseDraggedOutHorizontally = false;
780                 hsb.stopScrollingInstance();
781             }
782         }
783     }
784 
785     /*
786      * Helper method for XListPeer with integrated vertical scrollbar.
787      * Stop vertical scrolling when mouse released in / out the area of the list if it's required
788      * Restoring Motif behavior
789      * see 6243382 for more information
790      */
791     void trackMouseReleasedScroll(){
792 
793         if (mouseDraggedOutVertically){
794             mouseDraggedOutVertically = false;
795             vsb.stopScrollingInstance();
796         }
797 
798         if (mouseDraggedOutHorizontally){
799             mouseDraggedOutHorizontally = false;
800             hsb.stopScrollingInstance();
801         }
802     }
803 
804     void handleJavaKeyEvent(KeyEvent e) {
805         switch(e.getID()) {
806           case KeyEvent.KEY_PRESSED:
807               if (!isMousePressed){
808                   keyPressed(e);
809               }
810               break;
811         }
812     }
813 
814     void keyPressed(KeyEvent e) {
815         int keyCode = e.getKeyCode();
816         if (log.isLoggable(PlatformLogger.Level.FINE)) {
817             log.fine(e.toString());
818         }
819         switch(keyCode) {
820           case KeyEvent.VK_UP:
821           case KeyEvent.VK_KP_UP: // TODO: I assume we also want this, too
822               if (getFocusIndex() > 0) {
823                   setFocusIndex(getFocusIndex()-1);
824                   repaint(PAINT_HIDEFOCUS);
825                   // If single-select, select the item
826                   if (!multipleSelections) {
827                       selectItem(getFocusIndex());
828                       postEvent(new ItemEvent((List)target,
829                                               ItemEvent.ITEM_STATE_CHANGED,
830                                               Integer.valueOf(getFocusIndex()),
831                                               ItemEvent.SELECTED));
832                   }
833                   if (isItemHidden(getFocusIndex())) {
834                       makeVisible(getFocusIndex());
835                   }
836                   else {
837                       repaint(PAINT_FOCUS);
838                   }
839               }
840               break;
841           case KeyEvent.VK_DOWN:
842           case KeyEvent.VK_KP_DOWN: // TODO: I assume we also want this, too
843               if (getFocusIndex() < items.size() - 1) {
844                   setFocusIndex(getFocusIndex()+1);
845                   repaint(PAINT_HIDEFOCUS);
846                   // If single-select, select the item
847                   if (!multipleSelections) {
848                       selectItem(getFocusIndex());
849                       postEvent(new ItemEvent((List)target,
850                                               ItemEvent.ITEM_STATE_CHANGED,
851                                               Integer.valueOf(getFocusIndex()),
852                                               ItemEvent.SELECTED));
853                   }
854                   if (isItemHidden(getFocusIndex())) {
855                       makeVisible(getFocusIndex());
856                   }
857                   else {
858                       repaint(PAINT_FOCUS);
859                   }
860               }
861               break;
862           case KeyEvent.VK_PAGE_UP: {
863               // Assumes that scrollbar does its own bounds-checking
864               int previousValue = vsb.getValue();
865               vsb.setValue(vsb.getValue() - vsb.getBlockIncrement());
866               int currentValue = vsb.getValue();
867               // 6190768 pressing pg-up on AWT multiple selection lists the items but no item event is triggered, on XToolkit
868               // Restoring Motif behavior
869               if (previousValue!=currentValue) {
870                   setFocusIndex(Math.max(getFocusIndex()-itemsInWindow(), 0));
871                   if (!multipleSelections){
872                       selectItem(getFocusIndex());
873                       postEvent(new ItemEvent((List)target,
874                                               ItemEvent.ITEM_STATE_CHANGED,
875                                               Integer.valueOf(getFocusIndex()),
876                                               ItemEvent.SELECTED));
877                   }
878               }
879               repaint();
880               break;
881           }
882           case KeyEvent.VK_PAGE_DOWN: {
883               // Assumes that scrollbar does its own bounds-checking
884               int previousValue = vsb.getValue();
885               vsb.setValue(vsb.getValue() + vsb.getBlockIncrement());
886               int currentValue = vsb.getValue();
887               // 6190768 pressing pg-down on AWT multiple selection list selects the items but no item event is triggered, on XToolkit
888               // Restoring Motif behavior
889               if (previousValue!=currentValue) {
890                   setFocusIndex(Math.min(getFocusIndex() + itemsInWindow(), items.size()-1));
891                   if (!multipleSelections){
892                       selectItem(getFocusIndex());
893                       postEvent(new ItemEvent((List)target,
894                                               ItemEvent.ITEM_STATE_CHANGED,
895                                               Integer.valueOf(getFocusIndex()),
896                                               ItemEvent.SELECTED));
897                   }
898               }
899               repaint();
900               break;
901           }
902           case KeyEvent.VK_LEFT:
903           case KeyEvent.VK_KP_LEFT:
904               if (hsbVis & hsb.getValue() > 0) {
905                   hsb.setValue(hsb.getValue() - HORIZ_SCROLL_AMT);
906                   repaint();
907               }
908               break;
909           case KeyEvent.VK_RIGHT:
910           case KeyEvent.VK_KP_RIGHT:
911               if (hsbVis) { // Should check if already at end
912                   hsb.setValue(hsb.getValue() + HORIZ_SCROLL_AMT);
913                   repaint();
914               }
915               break;
916           // 6190778 CTRL + HOME, CTRL + END keys do not work properly for list on XToolkit
917           // Restoring Motif behavior
918           case KeyEvent.VK_HOME:
919               if (!e.isControlDown() || ((List)target).getItemCount() <= 0)
920                   break;
921               if (vsbVis) {
922                   vsb.setValue(vsb.getMinimum());
923               }
924               setFocusIndex(0);
925               if (!multipleSelections) {
926                   selectItem(getFocusIndex());
927                   postEvent(new ItemEvent((List)target,
928                                           ItemEvent.ITEM_STATE_CHANGED,
929                                           Integer.valueOf(getFocusIndex()),
930                                           ItemEvent.SELECTED));
931               }
932               repaint();
933               break;
934           case KeyEvent.VK_END:
935               if (!e.isControlDown() || ((List)target).getItemCount() <= 0)
936                   break;
937               if (vsbVis) {
938                   vsb.setValue(vsb.getMaximum());
939               }
940               setFocusIndex(items.size()-1);
941               if (!multipleSelections) {
942                   selectItem(getFocusIndex());
943                   postEvent(new ItemEvent((List)target,
944                                           ItemEvent.ITEM_STATE_CHANGED,
945                                           Integer.valueOf(getFocusIndex()),
946                                           ItemEvent.SELECTED));
947               }
948               repaint();
949               break;
950           case KeyEvent.VK_SPACE:
951               // Fixed 6299853: XToolkit: Pressing space triggers ItemStateChanged event after List.removeAll called
952               // If getFocusIndex() is less than 0, the event will not be triggered when space pressed
953               if (getFocusIndex() < 0 || ((List)target).getItemCount() <= 0) {
954                   break;
955               }
956 
957               boolean isSelected = isSelected(getFocusIndex());
958 
959               // Spacebar only deselects for multi-select Lists
960               if (multipleSelections && isSelected) {
961                   deselectItem(getFocusIndex());
962                   postEvent(new ItemEvent((List)target,
963                                           ItemEvent.ITEM_STATE_CHANGED,
964                                           Integer.valueOf(getFocusIndex()),
965                                           ItemEvent.DESELECTED));
966               }
967               else if (!isSelected) { // Note: this changes the Solaris/Linux
968                   // behavior to match that of win32.
969                   // That is, pressing space bar on a
970                   // single-select list when the focused
971                   // item is already selected does NOT
972                   // send an ItemEvent.SELECTED event.
973                   selectItem(getFocusIndex());
974                   postEvent(new ItemEvent((List)target,
975                                           ItemEvent.ITEM_STATE_CHANGED,
976                                           Integer.valueOf(getFocusIndex()),
977                                           ItemEvent.SELECTED));
978               }
979               break;
980           case KeyEvent.VK_ENTER:
981               // It looks to me like there are bugs as well as inconsistencies
982               // in the way the Enter key is handled by both Solaris and Windows.
983               // So for now in XAWT, I'm going to simply go by what the List docs
984               // say: "AWT also generates an action event when the user presses
985               // the return key while an item in the list is selected."
986               if (selected.length > 0) {
987                   postEvent(new ActionEvent((List)target,
988                                             ActionEvent.ACTION_PERFORMED,
989                                             (String)items.elementAt(getFocusIndex()),
990                                             e.getWhen(),
991                                             e.getModifiers()));  // ActionEvent doesn't have
992                   // extended modifiers.
993               }
994               break;
995         }
996     }
997 
998     /**
999      * return value from the scrollbar
1000      */
1001     public void notifyValue(XScrollbar obj, int type, int v, boolean isAdjusting) {
1002 
1003         if (log.isLoggable(PlatformLogger.Level.FINE)) {
1004             log.fine("Notify value changed on " + obj + " to " + v);
1005         }
1006         int value = obj.getValue();
1007         if (obj == vsb) {
1008             scrollVertical(v - value);
1009 
1010             // See 6243382 for more information
1011             int oldSel = eventIndex;
1012             int newSel = eventIndex+v-value;
1013             if (mouseDraggedOutVertically && !isSelected(newSel)){
1014                 selectItem(newSel);
1015                 eventIndex = newSel;
1016                 repaint(oldSel, eventIndex, PAINT_ITEMS);
1017                 // Scrolling select() should also set the focus index
1018                 // Otherwise, the updating of the 'focusIndex' variable will be incorrect
1019                 // if user drag mouse out of the area of the list
1020                 setFocusIndex(newSel);
1021                 repaint(PAINT_FOCUS);
1022             }
1023 
1024         } else if ((XHorizontalScrollbar)obj == hsb) {
1025             scrollHorizontal(v - value);
1026         }
1027 
1028     }
1029 
1030     /**
1031      * deselect all items in List
1032      */
1033     private void deselectAllItems() {
1034         selected = new int [0];
1035         repaint(PAINT_ITEMS);
1036     }
1037 
1038     /**
1039      * set multiple selections
1040      */
1041     public void setMultipleSelections(boolean v) {
1042         if (multipleSelections != v) {
1043             if ( !v) {
1044                 int selPos = ( isSelected( focusIndex )) ? focusIndex: -1;
1045                 deselectAllItems();
1046                 if (selPos != -1){
1047                     selectItem(selPos);
1048                 }
1049             }
1050             multipleSelections = v;
1051         }
1052     }
1053 
1054     /**
1055      * add an item
1056      * if the index of the item is < 0 or >= than items.size()
1057      * then add the item to the end of the list
1058      */
1059     public void addItem(String item, int i) {
1060         int oldMaxLength = maxLength;
1061         boolean hsbWasVis = hsbVis;
1062         boolean vsbWasVis = vsbVis;
1063 
1064         int addedIndex = 0; // Index where the new item ended up
1065         if (i < 0 || i >= items.size()) {
1066             i = -1;
1067         }
1068 
1069         // Why we set this variable to -1 in spite of the fact that selected[] is changed in other way?
1070         // It's not clear how to reproduce incorrect behaviour based on this assignment
1071         // since before using this variable (mouseReleased) we certainly update it to correct value
1072         // So we don't modify this behaviour now
1073         currentIndex = -1;
1074 
1075         if (i == -1) {
1076             items.addElement(item);
1077             i = 0;              // fix the math for the paintItems test
1078             addedIndex = items.size() - 1;
1079         } else {
1080             items.insertElementAt(item, i);
1081             addedIndex = i;
1082             for (int j = 0 ; j < selected.length ; j++) {
1083                 if (selected[j] >= i) {
1084                     selected[j] += 1;
1085                 }
1086             }
1087         }
1088         if (log.isLoggable(PlatformLogger.Level.FINER)) {
1089             log.finer("Adding item '" + item + "' to " + addedIndex);
1090         }
1091 
1092         // Update maxLength
1093         boolean repaintItems = !isItemHidden(addedIndex);
1094         maxLength = Math.max(maxLength, getItemWidth(addedIndex));
1095         layout();
1096 
1097         int options = 0;
1098         if (vsbVis != vsbWasVis || hsbVis != hsbWasVis) {
1099             // Scrollbars are being added or removed, so we must repaint all
1100             options = PAINT_ALL;
1101         }
1102         else {
1103             options = (repaintItems ? (PAINT_ITEMS):0)
1104                 | ((maxLength != oldMaxLength || (hsbWasVis ^ hsbVis))?(PAINT_HSCROLL):0)
1105                 | ((vsb.needsRepaint())?(PAINT_VSCROLL):0);
1106 
1107         }
1108         if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1109             log.finest("Last visible: " + getLastVisibleItem() +
1110             ", hsb changed : " + (hsbWasVis ^ hsbVis) + ", items changed " + repaintItems);
1111         }
1112         repaint(addedIndex, getLastVisibleItem(), options);
1113     }
1114 
1115     /**
1116      * delete items starting with s (start position) to e (end position) including s and e
1117      * if s < 0 then s = 0
1118      * if e >= items.size() then e = items.size() - 1
1119      */
1120     public void delItems(int s, int e) {
1121         // save the current state of the scrollbars
1122         boolean hsbWasVisible = hsbVis;
1123         boolean vsbWasVisible = vsbVis;
1124         int oldLastDisplayed = lastItemDisplayed();
1125 
1126         if (log.isLoggable(PlatformLogger.Level.FINE)) {
1127             log.fine("Deleting from " + s + " to " + e);
1128         }
1129 
1130         if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1131             log.finest("Last displayed item: " + oldLastDisplayed + ", items in window " + itemsInWindow() +
1132             ", size " + items.size());
1133         }
1134 
1135         if (items.size() == 0) {
1136             return;
1137         }
1138 
1139         // if user passed in flipped args, reverse them
1140         if (s > e) {
1141             int tmp = s;
1142             s = e;
1143             e = tmp;
1144         }
1145 
1146         // check for starting point less than zero
1147         if (s < 0) {
1148             s = 0;
1149         }
1150 
1151         // check for end point greater than the size of the list
1152         if (e >= items.size()) {
1153             e = items.size() - 1;
1154         }
1155 
1156         // determine whether we're going to delete any visible elements
1157         // repaint must also be done if scrollbars appear/disappear, which
1158         // can happen from removing a non-showing list item
1159         /*
1160           boolean repaintNeeded =
1161           ((s <= lastItemDisplayed()) && (e >= vsb.getValue()));
1162         */
1163         boolean repaintNeeded = (s >= getFirstVisibleItem() && s <= getLastVisibleItem());
1164 
1165         // delete the items out of the items list and out of the selected list
1166         for (int i = s ; i <= e ; i++) {
1167             items.removeElementAt(s);
1168             int j = posInSel(i);
1169             if (j != -1) {
1170                 int newsel[] = new int[selected.length - 1];
1171                 System.arraycopy(selected, 0, newsel, 0, j);
1172                 System.arraycopy(selected, j + 1, newsel, j, selected.length - (j + 1));
1173                 selected = newsel;
1174             }
1175 
1176         }
1177 
1178         // update the indexes in the selected array
1179         int diff = (e - s) + 1;
1180         for (int i = 0 ; i < selected.length ; i++) {
1181             if (selected[i] > e) {
1182                 selected[i] -= diff;
1183             }
1184         }
1185 
1186         int options = PAINT_VSCROLL;
1187         // focusedIndex updating according to native (Window, Motif) behaviour
1188         if (getFocusIndex() > e) {
1189             setFocusIndex(getFocusIndex() - (e - s + 1));
1190             options |= PAINT_FOCUS;
1191         } else if (getFocusIndex() >= s && getFocusIndex() <= e) {
1192             // Fixed 6299858: PIT. Focused border not shown on List if selected item is removed, XToolkit
1193             // We should set focus to new first item if the current first item was removed
1194             // except if the list is empty
1195             int focusBound = (items.size() > 0) ? 0 : -1;
1196             setFocusIndex(Math.max(s-1, focusBound));
1197             options |= PAINT_FOCUS;
1198         }
1199 
1200         if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1201             log.finest("Multiple selections: " + multipleSelections);
1202         }
1203 
1204         // update vsb.val
1205         if (vsb.getValue() >= s) {
1206             if (vsb.getValue() <= e) {
1207                 vsb.setValue(e+1 - diff);
1208             } else {
1209                 vsb.setValue(vsb.getValue() - diff);
1210             }
1211         }
1212 
1213         int oldMaxLength = maxLength;
1214         maxLength = maxLength();
1215         if (maxLength != oldMaxLength) {
1216             // Width of the items changed affecting the range of
1217             // horizontal scrollbar
1218             options |= PAINT_HSCROLL;
1219         }
1220         layout();
1221         repaintNeeded |= (vsbWasVisible ^ vsbVis) || (hsbWasVisible ^ hsbVis); // If scrollbars visibility changed
1222         if (repaintNeeded) {
1223             options |= PAINT_ALL;
1224         }
1225         repaint(s, oldLastDisplayed, options);
1226     }
1227 
1228     /**
1229      * ListPeer method
1230      */
1231     public void select(int index) {
1232         // Programmatic select() should also set the focus index
1233         setFocusIndex(index);
1234         repaint(PAINT_FOCUS);
1235         selectItem(index);
1236     }
1237 
1238     /**
1239      * select the index
1240      * redraw the list to the screen
1241      */
1242     void selectItem(int index) {
1243         // NOTE: instead of recalculating and the calling repaint(), painting
1244         // is done immediately
1245 
1246         // 6190746 List does not trigger ActionEvent when double clicking a programmatically selected item, XToolkit
1247         // If we invoke select(int) before setVisible(boolean), then variable currentIndex will equals -1. At the same time isSelected may be true.
1248         // Restoring Motif behavior
1249         currentIndex = index;
1250 
1251         if (isSelected(index)) {
1252             return;
1253         }
1254         if (!multipleSelections) {
1255             if (selected.length == 0) { // No current selection
1256                 selected = new int[1];
1257                 selected[0] = index;
1258             }
1259             else {
1260                 int oldSel = selected[0];
1261                 selected[0] = index;
1262                 if (!isItemHidden(oldSel)) {
1263                     // Only bother painting if item is visible (4895367)
1264                     repaint(oldSel, oldSel, PAINT_ITEMS);
1265                 }
1266             }
1267         } else {
1268             // insert "index" into the selection array
1269             int newsel[] = new int[selected.length + 1];
1270             int i = 0;
1271             while (i < selected.length && index > selected[i]) {
1272                 newsel[i] = selected[i];
1273                 i++;
1274             }
1275             newsel[i] = index;
1276             System.arraycopy(selected, i, newsel, i+1, selected.length - i);
1277             selected = newsel;
1278         }
1279         if (!isItemHidden(index)) {
1280             // Only bother painting if item is visible (4895367)
1281             repaint(index, index, PAINT_ITEMS);
1282         }
1283     }
1284 
1285     /**
1286      * ListPeer method
1287      * focusedIndex isn't updated according to native (Window, Motif) behaviour
1288      */
1289     public void deselect(int index) {
1290         deselectItem(index);
1291     }
1292 
1293     /**
1294      * deselect the index
1295      * redraw the list to the screen
1296      */
1297     void deselectItem(int index) {
1298         if (!isSelected(index)) {
1299             return;
1300         }
1301         if (!multipleSelections) {
1302             // TODO: keep an int[0] and int[1] around and just use them instead
1303             // creating new ones all the time
1304             selected = new int[0];
1305         } else {
1306             int i = posInSel(index);
1307             int newsel[] = new int[selected.length - 1];
1308             System.arraycopy(selected, 0, newsel, 0, i);
1309             System.arraycopy(selected, i+1, newsel, i, selected.length - (i+1));
1310             selected = newsel;
1311         }
1312         currentIndex = index;
1313         if (!isItemHidden(index)) {
1314             // Only bother repainting if item is visible
1315             repaint(index, index, PAINT_ITEMS);
1316         }
1317     }
1318 
1319     /**
1320      * ensure that the given index is visible, scrolling the List
1321      * if necessary, or doing nothing if the item is already visible.
1322      * The List must be repainted for changes to be visible.
1323      */
1324     public void makeVisible(int index) {
1325         if (index < 0 || index >= items.size()) {
1326             return;
1327         }
1328         if (isItemHidden(index)) {  // Do I really need to call this?
1329             // If index is above the top, scroll up
1330             if (index < vsb.getValue()) {
1331                 scrollVertical(index - vsb.getValue());
1332             }
1333             // If index is below the bottom, scroll down
1334             else if (index > lastItemDisplayed()) {
1335                 int val = index - lastItemDisplayed();
1336                 scrollVertical(val);
1337             }
1338         }
1339     }
1340 
1341     /**
1342      * clear
1343      */
1344     public void clear() {
1345         selected = new int[0];
1346         items = new Vector();
1347         currentIndex = -1;
1348         // Fixed 6291736: ITEM_STATE_CHANGED triggered after List.removeAll(), XToolkit
1349         // We should update 'focusIndex' variable more carefully
1350         setFocusIndex(-1);
1351         vsb.setValue(0);
1352         maxLength = 0;
1353         layout();
1354         repaint();
1355     }
1356 
1357     /**
1358      * return the selected indexes
1359      */
1360     public int[] getSelectedIndexes() {
1361         return selected;
1362     }
1363 
1364     /**
1365      * return the y value of the given index "i".
1366      * the y value represents the top of the text
1367      * NOTE: index can be larger than items.size as long
1368      * as it can fit the window
1369      */
1370     int index2y(int index) {
1371         int h = getItemHeight();
1372 
1373         //if (index < vsb.getValue() || index > vsb.getValue() + itemsInWindow()) {
1374         return MARGIN + ((index - vsb.getValue()) * h) + SPACE;
1375     }
1376 
1377     /* return true if the y is a valid y coordinate for
1378      *  a VISIBLE list item, otherwise returns false
1379      */
1380     boolean validY(int y) {
1381 
1382         int shown = itemsDisplayed();
1383         int lastY = shown * getItemHeight() + MARGIN;
1384 
1385         if (shown == itemsInWindow()) {
1386             lastY += MARGIN;
1387         }
1388 
1389         if (y < 0 || y >= lastY) {
1390             return false;
1391         }
1392 
1393         return true;
1394     }
1395 
1396     /**
1397      * return the position of the index in the selected array
1398      * if the index isn't in the array selected return -1;
1399      */
1400     int posInSel(int index) {
1401         for (int i = 0 ; i < selected.length ; i++) {
1402             if (index == selected[i]) {
1403                 return i;
1404             }
1405         }
1406         return -1;
1407     }
1408 
1409     boolean isIndexDisplayed(int idx) {
1410         int lastDisplayed = lastItemDisplayed();
1411 
1412         return idx <= lastDisplayed &&
1413             idx >= Math.max(0, lastDisplayed - itemsInWindow() + 1);
1414     }
1415 
1416     /**
1417      * returns index of last item displayed in the List
1418      */
1419     int lastItemDisplayed() {
1420         int n = itemsInWindow();
1421         return (Math.min(items.size() - 1, (vsb.getValue() + n) - 1));
1422     }
1423 
1424     /**
1425      * returns whether the given index is currently scrolled off the top or
1426      * bottom of the List.
1427      */
1428     boolean isItemHidden(int index) {
1429         return index < vsb.getValue() ||
1430             index >= vsb.getValue() + itemsInWindow();
1431     }
1432 
1433     /**
1434      * returns the width of the list portion of the component (accounts for
1435      * presence of vertical scrollbar)
1436      */
1437     int getListWidth() {
1438         return vsbVis ? width - SCROLLBAR_AREA : width;
1439     }
1440 
1441     /**
1442      * returns number of  items actually displayed in the List
1443      */
1444     int itemsDisplayed() {
1445 
1446         return (Math.min(items.size()-vsb.getValue(), itemsInWindow()));
1447 
1448     }
1449 
1450     /**
1451      * scrollVertical
1452      * y is the number of items to scroll
1453      */
1454     void scrollVertical(int y) {
1455         if (log.isLoggable(PlatformLogger.Level.FINE)) {
1456             log.fine("Scrolling vertically by " + y);
1457         }
1458         int itemsInWin = itemsInWindow();
1459         int h = getItemHeight();
1460         int pixelsToScroll = y * h;
1461 
1462         if (vsb.getValue() < -y) {
1463             y = -vsb.getValue();
1464         }
1465         vsb.setValue(vsb.getValue() + y);
1466 
1467         Rectangle source = null;
1468         Point distance = null;
1469         int firstItem = 0, lastItem = 0;
1470         int options = PAINT_HIDEFOCUS | PAINT_ITEMS | PAINT_VSCROLL | PAINT_FOCUS;
1471         if (y > 0) {
1472             if (y < itemsInWin) {
1473                 source = new Rectangle(MARGIN, MARGIN + pixelsToScroll, width - SCROLLBAR_AREA, h * (itemsInWin - y - 1)-1);
1474                 distance = new Point(0, -pixelsToScroll);
1475                 options |= COPY_AREA;
1476             }
1477             firstItem = vsb.getValue() + itemsInWin - y - 1;
1478             lastItem = vsb.getValue() + itemsInWin - 1;
1479 
1480         } else if (y < 0) {
1481             if (y + itemsInWindow() > 0) {
1482                 source = new Rectangle(MARGIN, MARGIN, width - SCROLLBAR_AREA, h * (itemsInWin + y));
1483                 distance = new Point(0, -pixelsToScroll);
1484                 options |= COPY_AREA;
1485             }
1486             firstItem = vsb.getValue();
1487             lastItem = Math.min(getLastVisibleItem(), vsb.getValue() + -y);
1488         }
1489         repaint(firstItem, lastItem, options, source, distance);
1490     }
1491 
1492     /**
1493      * scrollHorizontal
1494      * x is the number of pixels to scroll
1495      */
1496     void scrollHorizontal(int x) {
1497         if (log.isLoggable(PlatformLogger.Level.FINE)) {
1498             log.fine("Scrolling horizontally by " + y);
1499         }
1500         int w = getListWidth();
1501         w -= ((2 * SPACE) + (2 * MARGIN));
1502         int h = height - (SCROLLBAR_AREA + (2 * MARGIN));
1503         hsb.setValue(hsb.getValue() + x);
1504 
1505         int options = PAINT_ITEMS | PAINT_HSCROLL;
1506 
1507         Rectangle source = null;
1508         Point distance = null;
1509         if (x < 0) {
1510             source = new Rectangle(MARGIN + SPACE, MARGIN, w + x, h);
1511             distance = new Point(-x, 0);
1512             options |= COPY_AREA;
1513         } else if (x > 0) {
1514             source = new Rectangle(MARGIN + SPACE + x, MARGIN, w - x, h);
1515             distance = new Point(-x, 0);
1516             options |= COPY_AREA;
1517         }
1518         repaint(vsb.getValue(), lastItemDisplayed(), options, source, distance);
1519     }
1520 
1521     /**
1522      * return the index
1523      */
1524     int y2index(int y) {
1525         if (!validY(y)) {
1526             return -1;
1527         }
1528 
1529         int i = (y - MARGIN) / getItemHeight() + vsb.getValue();
1530         int last = lastItemDisplayed();
1531 
1532         if (i > last) {
1533             i = last;
1534         }
1535 
1536         return i;
1537 
1538     }
1539 
1540     /**
1541      * is the index "index" selected
1542      */
1543     boolean isSelected(int index) {
1544         if (eventType == ItemEvent.SELECTED && index == eventIndex) {
1545             return true;
1546         }
1547         for (int i = 0 ; i < selected.length ; i++) {
1548             if (selected[i] == index) {
1549                 return true;
1550             }
1551         }
1552         return false;
1553     }
1554 
1555     /**
1556      * return the number of items that can fit
1557      * in the current window
1558      */
1559     int itemsInWindow(boolean scrollbarVisible) {
1560         int h;
1561         if (scrollbarVisible) {
1562             h = height - ((2 * MARGIN) + SCROLLBAR_AREA);
1563         } else {
1564             h = height - 2*MARGIN;
1565         }
1566         return (h / getItemHeight());
1567     }
1568 
1569     int itemsInWindow() {
1570         return itemsInWindow(hsbVis);
1571     }
1572 
1573     /**
1574      * return true if the x and y position is in the horizontal scrollbar
1575      */
1576     boolean inHorizontalScrollbar(int x, int y) {
1577         int w = getListWidth();
1578         int h = height - SCROLLBAR_WIDTH;
1579         return (hsbVis &&  (x >= 0) && (x <= w) && (y > h));
1580     }
1581 
1582     /**
1583      * return true if the x and y position is in the verticalscrollbar
1584      */
1585     boolean inVerticalScrollbar(int x, int y) {
1586         int w = width - SCROLLBAR_WIDTH;
1587         int h = hsbVis ? height - SCROLLBAR_AREA : height;
1588         return (vsbVis && (x > w) && (y >= 0) && (y <= h));
1589     }
1590 
1591     /**
1592      * return true if the x and y position is in the window
1593      */
1594     boolean inWindow(int x, int y) {
1595         int w = getListWidth();
1596         int h = hsbVis ? height - SCROLLBAR_AREA : height;
1597         return ((x >= 0) && (x <= w)) && ((y >= 0) && (y <= h));
1598     }
1599 
1600     /**
1601      * return true if vertical scrollbar is visible and false otherwise;
1602      * hsbVisible is the visibility of the horizontal scrollbar
1603      */
1604     boolean vsbIsVisible(boolean hsbVisible){
1605         return (items.size() > itemsInWindow(hsbVisible));
1606     }
1607 
1608     /**
1609      * return true if horizontal scrollbar is visible and false otherwise;
1610      * vsbVisible is the visibility of the vertical scrollbar
1611      */
1612     boolean hsbIsVisible(boolean vsbVisible){
1613         int w = width - ((2*SPACE) + (2*MARGIN) + (vsbVisible ? SCROLLBAR_AREA : 0));
1614         return (maxLength > w);
1615     }
1616 
1617     /*
1618      * Returns true if the event has been handled and should not be
1619      * posted to Java
1620      */
1621     boolean prePostEvent(final AWTEvent e) {
1622         if (e instanceof MouseEvent) {
1623             return prePostMouseEvent((MouseEvent)e);
1624         }
1625         return super.prePostEvent(e);
1626     }
1627 
1628     /*
1629      * Fixed 6240151: XToolkit: Dragging the List scrollbar initiates DnD
1630      * To be compatible with Motif, MouseEvent originated on the scrollbar
1631      * should be sent into Java in this way:
1632      * - post: MOUSE_ENTERED, MOUSE_EXITED, MOUSE_MOVED
1633      * - don't post: MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, MOUSE_DRAGGED
1634      */
1635     boolean prePostMouseEvent(final MouseEvent me){
1636         if (getToplevelXWindow().isModalBlocked()) {
1637             return false;
1638         }
1639 
1640         int eventId = me.getID();
1641 
1642         if (eventId == MouseEvent.MOUSE_MOVED)
1643         {
1644             // only for performance improvement
1645         }else if((eventId == MouseEvent.MOUSE_DRAGGED ||
1646                   eventId == MouseEvent.MOUSE_RELEASED) &&
1647                  isScrollBarOriginated)
1648         {
1649             if (eventId == MouseEvent.MOUSE_RELEASED) {
1650                 isScrollBarOriginated = false;
1651             }
1652             handleJavaMouseEventOnEDT(me);
1653             return true;
1654         }else if ((eventId == MouseEvent.MOUSE_PRESSED ||
1655                    eventId == MouseEvent.MOUSE_CLICKED) &&
1656                   (inVerticalScrollbar(me.getX(), me.getY()) ||
1657                    inHorizontalScrollbar(me.getX(), me.getY())))
1658         {
1659             if (eventId == MouseEvent.MOUSE_PRESSED) {
1660                 isScrollBarOriginated = true;
1661             }
1662             handleJavaMouseEventOnEDT(me);
1663             return true;
1664         }
1665         return false;
1666     }
1667 
1668     /*
1669      * Do handleJavaMouseEvent on EDT
1670      */
1671     void handleJavaMouseEventOnEDT(final MouseEvent me){
1672         InvocationEvent ev = new InvocationEvent(target, new Runnable() {
1673             public void run() {
1674                 handleJavaMouseEvent(me);
1675             }
1676         });
1677         postEvent(ev);
1678     }
1679 
1680     /*
1681      * Fixed 5010944: List's rows overlap one another
1682      * The bug is due to incorrent caching of the list item size
1683      * So we should recalculate font metrics on setFont
1684      */
1685     public void setFont(Font f) {
1686         if (!Objects.equals(getFont(), f)) {
1687             super.setFont(f);
1688             initFontMetrics();
1689             layout();
1690             repaint();
1691         }
1692     }
1693 
1694     /**
1695      * Sometimes painter is called on Toolkit thread, so the lock sequence is:
1696      *     awtLock -> Painter -> awtLock
1697      * Sometimes it is called on other threads:
1698      *     Painter -> awtLock
1699      * Since we can't guarantee the sequence, use awtLock.
1700      */
1701     class ListPainter {
1702         VolatileImage buffer;
1703         Color[] colors;
1704 
1705         private Color getListForeground() {
1706             if (fgColorSet) {
1707                 return colors[FOREGROUND_COLOR];
1708             }
1709             else {
1710             return SystemColor.textText;
1711             }
1712         }
1713         private Color getListBackground() {
1714             if (bgColorSet) {
1715                 return colors[BACKGROUND_COLOR];
1716             }
1717             else {
1718                 return SystemColor.text;
1719             }
1720         }
1721 
1722         private Color getDisabledColor() {
1723             Color backgroundColor = getListBackground();
1724             Color foregroundColor = getListForeground();
1725             return (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();
1726         }
1727 
1728         private boolean createBuffer() {
1729             VolatileImage localBuffer = null;
1730             XToolkit.awtLock();
1731             try {
1732                 localBuffer = buffer;
1733             } finally {
1734                 XToolkit.awtUnlock();
1735             }
1736 
1737             if (localBuffer == null) {
1738                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
1739                     log.fine("Creating buffer " + width + "x" + height);
1740                 }
1741                 // use GraphicsConfig.cCVI() instead of Component.cVI(),
1742                 // because the latter may cause a deadlock with the tree lock
1743                 localBuffer =
1744                     graphicsConfig.createCompatibleVolatileImage(width+1,
1745                                                                  height+1);
1746             }
1747             XToolkit.awtLock();
1748             try {
1749                 if (buffer == null) {
1750                     buffer = localBuffer;
1751                     return true;
1752                 }
1753             } finally {
1754                 XToolkit.awtUnlock();
1755             }
1756             return false;
1757         }
1758 
1759         public void invalidate() {
1760             XToolkit.awtLock();
1761             try {
1762                 if (buffer != null) {
1763                     buffer.flush();
1764                 }
1765                 buffer = null;
1766             } finally {
1767                 XToolkit.awtUnlock();
1768             }
1769         }
1770 
1771         private void paint(Graphics listG, int firstItem, int lastItem, int options) {
1772             paint(listG, firstItem, lastItem, options, null, null);
1773         }
1774 
1775         private void paint(Graphics listG, int firstItem, int lastItem, int options,
1776                            Rectangle source, Point distance) {
1777             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1778                 log.finer("Repaint from " + firstItem + " to " + lastItem + " options " + options);
1779             }
1780             if (firstItem > lastItem) {
1781                 int t = lastItem;
1782                 lastItem = firstItem;
1783                 firstItem = t;
1784             }
1785             if (firstItem < 0) {
1786                 firstItem = 0;
1787             }
1788             colors = getGUIcolors();
1789             VolatileImage localBuffer = null;
1790             do {
1791                 XToolkit.awtLock();
1792                 try {
1793                     if (createBuffer()) {
1794                         // First time created buffer should be painted over at full.
1795                         options = PAINT_ALL;
1796                     }
1797                     localBuffer = buffer;
1798                 } finally {
1799                     XToolkit.awtUnlock();
1800                 }
1801                 switch (localBuffer.validate(getGraphicsConfiguration())) {
1802                   case VolatileImage.IMAGE_INCOMPATIBLE:
1803                       invalidate();
1804                       options = PAINT_ALL;
1805                       continue;
1806                   case VolatileImage.IMAGE_RESTORED:
1807                       options = PAINT_ALL;
1808                 }
1809                 Graphics g = localBuffer.createGraphics();
1810 
1811                 // Note that the order of the following painting operations
1812                 // should not be modified
1813                 try {
1814                     g.setFont(getFont());
1815 
1816                     // hiding the focus rectangle must be done prior to copying
1817                     // area and so this is the first action to be performed
1818                     if ((options & (PAINT_HIDEFOCUS)) != 0) {
1819                         paintFocus(g, PAINT_HIDEFOCUS);
1820                     }
1821                     /*
1822                      * The shift of the component contents occurs while someone
1823                      * scrolls the component, the only purpose of the shift is to
1824                      * increase the painting performance. The shift should be done
1825                      * prior to painting any area (except hiding focus) and actually
1826                      * it should never be done jointly with erase background.
1827                      */
1828                     if ((options & COPY_AREA) != 0) {
1829                         g.copyArea(source.x, source.y, source.width, source.height,
1830                             distance.x, distance.y);
1831                     }
1832                     if ((options & PAINT_BACKGROUND) != 0) {
1833                         paintBackground(g);
1834                         // Since we made full erase update items
1835                         firstItem = getFirstVisibleItem();
1836                         lastItem = getLastVisibleItem();
1837                     }
1838                     if ((options & PAINT_ITEMS) != 0) {
1839                         paintItems(g, firstItem, lastItem, options);
1840                     }
1841                     if ((options & PAINT_VSCROLL) != 0 && vsbVis) {
1842                         g.setClip(getVScrollBarRec());
1843                         paintVerScrollbar(g, true);
1844                     }
1845                     if ((options & PAINT_HSCROLL) != 0 && hsbVis) {
1846                         g.setClip(getHScrollBarRec());
1847                         paintHorScrollbar(g, true);
1848                     }
1849                     if ((options & (PAINT_FOCUS)) != 0) {
1850                         paintFocus(g, PAINT_FOCUS);
1851                     }
1852                 } finally {
1853                     g.dispose();
1854                 }
1855             } while (localBuffer.contentsLost());
1856             listG.drawImage(localBuffer, 0, 0, null);
1857         }
1858 
1859         private void paintBackground(Graphics g) {
1860             g.setColor(SystemColor.window);
1861             g.fillRect(0, 0, width, height);
1862             g.setColor(getListBackground());
1863             g.fillRect(0, 0, listWidth, listHeight);
1864             draw3DRect(g, getSystemColors(), 0, 0, listWidth - 1, listHeight - 1, false);
1865         }
1866 
1867         private void paintItems(Graphics g, int firstItem, int lastItem, int options) {
1868             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1869                 log.finer("Painting items from " + firstItem + " to " + lastItem + ", focused " + focusIndex + ", first " + getFirstVisibleItem() + ", last " + getLastVisibleItem());
1870             }
1871 
1872             firstItem = Math.max(getFirstVisibleItem(), firstItem);
1873             if (firstItem > lastItem) {
1874                 int t = lastItem;
1875                 lastItem = firstItem;
1876                 firstItem = t;
1877             }
1878             firstItem = Math.max(getFirstVisibleItem(), firstItem);
1879             lastItem = Math.min(lastItem, items.size()-1);
1880 
1881             if (log.isLoggable(PlatformLogger.Level.FINER)) {
1882                 log.finer("Actually painting items from " + firstItem + " to " + lastItem +
1883                           ", items in window " + itemsInWindow());
1884             }
1885             for (int i = firstItem; i <= lastItem; i++) {
1886                 paintItem(g, i);
1887             }
1888         }
1889 
1890         private void paintItem(Graphics g, int index) {
1891             if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1892                 log.finest("Painting item " + index);
1893             }
1894             // 4895367 - only paint items which are visible
1895             if (!isItemHidden(index)) {
1896                 Shape clip = g.getClip();
1897                 int w = getItemWidth();
1898                 int h = getItemHeight();
1899                 int y = getItemY(index);
1900                 int x = getItemX();
1901                 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1902                     log.finest("Setting clip " + new Rectangle(x, y, w - (SPACE*2), h-(SPACE*2)));
1903                 }
1904                 g.setClip(x, y, w - (SPACE*2), h-(SPACE*2));
1905 
1906                 // Always paint the background so that focus is unpainted in
1907                 // multiselect mode
1908                 if (isSelected(index)) {
1909                     if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1910                         log.finest("Painted item is selected");
1911                     }
1912                     g.setColor(getListForeground());
1913                 } else {
1914                     g.setColor(getListBackground());
1915                 }
1916                 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1917                     log.finest("Filling " + new Rectangle(x, y, w, h));
1918                 }
1919                 g.fillRect(x, y, w, h);
1920 
1921                 if (index <= getLastVisibleItem() && index < items.size()) {
1922                     if (!isEnabled()){
1923                         g.setColor(getDisabledColor());
1924                     } else if (isSelected(index)) {
1925                         g.setColor(getListBackground());
1926                     } else {
1927                         g.setColor(getListForeground());
1928                     }
1929                     String str = (String)items.elementAt(index);
1930                     g.drawString(str, x - hsb.getValue(), y + fontAscent);
1931                 } else {
1932                     // Clear the remaining area around the item - focus area and the rest of border
1933                     g.setClip(x, y, listWidth, h);
1934                     g.setColor(getListBackground());
1935                     g.fillRect(x, y, listWidth, h);
1936                 }
1937                 g.setClip(clip);
1938             }
1939         }
1940 
1941         void paintScrollBar(XScrollbar scr, Graphics g, int x, int y, int width, int height, boolean paintAll) {
1942             if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1943                 log.finest("Painting scrollbar " + scr + " width " +
1944                 width + " height " + height + ", paintAll " + paintAll);
1945             }
1946             g.translate(x, y);
1947             scr.paint(g, getSystemColors(), paintAll);
1948             g.translate(-x, -y);
1949         }
1950 
1951         /**
1952          * Paint the horizontal scrollbar to the screen
1953          *
1954          * @param g the graphics context to draw into
1955          * @param colors the colors used to draw the scrollbar
1956          * @param paintAll paint the whole scrollbar if true, just the thumb if false
1957          */
1958         void paintHorScrollbar(Graphics g, boolean paintAll) {
1959             int w = getListWidth();
1960             paintScrollBar(hsb, g, 0, height - (SCROLLBAR_WIDTH), w, SCROLLBAR_WIDTH, paintAll);
1961         }
1962 
1963         /**
1964          * Paint the vertical scrollbar to the screen
1965          *
1966          * @param g the graphics context to draw into
1967          * @param colors the colors used to draw the scrollbar
1968          * @param paintAll paint the whole scrollbar if true, just the thumb if false
1969          */
1970         void paintVerScrollbar(Graphics g, boolean paintAll) {
1971             int h = height - (hsbVis ? (SCROLLBAR_AREA-2) : 0);
1972             paintScrollBar(vsb, g, width - SCROLLBAR_WIDTH, 0, SCROLLBAR_WIDTH - 2, h, paintAll);
1973         }
1974 
1975 
1976         private Rectangle prevFocusRect;
1977         private void paintFocus(Graphics g, int options) {
1978             boolean paintFocus = (options & PAINT_FOCUS) != 0;
1979             if (paintFocus && !hasFocus()) {
1980                 paintFocus = false;
1981             }
1982             if (log.isLoggable(PlatformLogger.Level.FINE)) {
1983                 log.fine("Painting focus, focus index " + getFocusIndex() + ", focus is " +
1984                          (isItemHidden(getFocusIndex())?("invisible"):("visible")) + ", paint focus is " + paintFocus);
1985             }
1986             Shape clip = g.getClip();
1987             g.setClip(0, 0, listWidth, listHeight);
1988             if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1989                 log.finest("Setting focus clip " + new Rectangle(0, 0, listWidth, listHeight));
1990             }
1991             Rectangle rect = getFocusRect();
1992             if (prevFocusRect != null) {
1993                 // Erase focus rect
1994                 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
1995                     log.finest("Erasing previous focus rect " + prevFocusRect);
1996                 }
1997                 g.setColor(getListBackground());
1998                 g.drawRect(prevFocusRect.x, prevFocusRect.y, prevFocusRect.width, prevFocusRect.height);
1999                 prevFocusRect = null;
2000             }
2001             if (paintFocus) {
2002                 // Paint new
2003                 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
2004                     log.finest("Painting focus rect " + rect);
2005                 }
2006                 g.setColor(getListForeground());  // Focus color is always black on Linux
2007                 g.drawRect(rect.x, rect.y, rect.width, rect.height);
2008                 prevFocusRect = rect;
2009             }
2010             g.setClip(clip);
2011         }
2012     }
2013 }
2014