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