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