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