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