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