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