1 /* 2 * Copyright (c) 1999, 2014, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package test.java.awt.event.helpers.lwcomponents; 25 26 import java.awt.*; 27 import java.awt.event.*; 28 import java.util.Vector; 29 import java.util.Enumeration; 30 31 /** 32 * Remarks : Source for LeightWeight component - List. 33 * 34 * Scroll bar support is not available for this component, so if the 35 * items exceeds visibility those items will be truncated. Also, here 36 * double buffering is not used so there will be little bit flickering 37 * while it repaints. Item listener support is not enabled in this 38 * component. Listeners handled were Mouse, Key and Focus. 39 * 40 * @author R.Govindarajan (govind@siptech.co.in), G.N.V.Sekhar (sekharv@siptech.co.in) 41 */ 42 43 public class LWList extends LWComponent implements ItemSelectable { 44 45 // Constants used for component size 46 private final int MIN_WIDTH = 100; 47 private final int MIN_HEIGHT = 100; 48 private final int PREF_WIDTH = 100; 49 private final int PREF_HEIGHT = 100; 50 51 // Constants used for setting color for component 52 private final Color BACK_COLOR = Color.white; 53 private final Color FRONT_COLOR = Color.black; 54 private final Color BORDER_COLOR = Color.darkGray; 55 private final Color FOCUS_COLOR = Color.blue; 56 private final Color FOCUS_FORECOLOR = Color.white; 57 private final Color FOCUS_ENABLED_COLOR = Color.red; 58 private final int BORDER_WIDTH = 2; 59 60 private Vector stringList; // List of items 61 private Vector selList; // List of selected items 62 private int rows; // Visible rows 63 private int focusIndex, prevfocusIndex; 64 private Dimension minSize; 65 private Dimension prefSize; 66 private boolean pressed, eventOccurred, focusEnabled; 67 private boolean multipleMode; 68 69 // Listeners handled for this component 70 private ActionListener actionListener; 71 private KeyListener keyListener; 72 private FocusListener focusListener; 73 private ItemListener itemListener; 74 75 private static int nameCounter = 0; 76 77 /** 78 * Creates a new list. 79 */ 80 public LWList() { 81 this(0); 82 } 83 84 /** 85 * Creates a new list with the specified number of rows; 86 * multiple selection mode is disabled. 87 * 88 * @param i the number of rows 89 */ 90 public LWList(int i) { 91 this(i, false); 92 } 93 94 /** 95 * Creates a new list with the specified number of rows and multiple selection mode. 96 * 97 * @param rows the number of rows 98 * @param flag determines whether the list allows multiple selections 99 */ 100 public LWList(int rows, boolean flag) { 101 multipleMode = flag; 102 this.rows = rows; 103 minSize = new Dimension(MIN_WIDTH, MIN_HEIGHT); 104 prefSize = new Dimension(PREF_WIDTH, PREF_HEIGHT); 105 stringList = new Vector(); 106 selList = new Vector(); 107 selList.addElement(0); 108 focusIndex = -1; 109 prevfocusIndex = focusIndex; 110 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 111 enableEvents(AWTEvent.KEY_EVENT_MASK); 112 enableEvents(AWTEvent.FOCUS_EVENT_MASK); 113 enableEvents(AWTEvent.ITEM_EVENT_MASK); 114 setName(makeComponentName()); // set the name to the component 115 } 116 117 String makeComponentName() { 118 String s = "LWList" + nameCounter++; 119 return s; 120 } 121 122 /** 123 * Set whether the component is enabled or not. 124 * @param enabled if {@code true}, the component is to be enabled 125 */ 126 @Override 127 public void setEnabled(boolean enabled) { 128 super.setEnabled(enabled); 129 130 if (enabled) { 131 enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); 132 } else { 133 disableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); 134 } 135 repaint(1); 136 } 137 138 /** 139 * Set the selection mode. 140 * 141 * @param flag determines whether the list allows multiple selections 142 */ 143 public void setSelectionMode(boolean flag) { 144 multipleMode = flag; 145 } 146 147 /** 148 * Check if the list allows multiple selections. 149 * 150 * @return {@code true} if the list allows multiple selections 151 */ 152 public boolean isMultipleMode() { 153 return multipleMode; 154 } 155 156 /** 157 * Add the specified item. 158 * 159 * @param listItem the item 160 */ 161 public void add(String listItem) { 162 stringList.addElement(listItem); 163 invalidate(); 164 repaint(); 165 } 166 167 /** 168 * Get minimum dimension for the list. 169 * 170 * @return the minimum dimensions for displaying 171 */ 172 @Override 173 public Dimension getMinimumSize() { 174 return minSize; 175 } 176 177 /** 178 * Get the preferred size of the list. 179 * 180 * @return the preferred dimensions for displaying 181 */ 182 @Override 183 public Dimension getPreferredSize() { 184 return prefSize; 185 } 186 187 /** 188 * Get the background color for the component. 189 * 190 * @return the background color for the component 191 */ 192 @Override 193 public Color getBackground() { 194 return BACK_COLOR; 195 } 196 197 /** 198 * Get the foreground color for the component. 199 * 200 * @return the foreground color for the component 201 */ 202 @Override 203 public Color getForeground() { 204 return FRONT_COLOR; 205 } 206 207 /** 208 * Get the border color for the component. 209 * 210 * @return the border color for the component 211 */ 212 public Color getBorder() { 213 return BORDER_COLOR; 214 } 215 216 /** 217 * Get background color for the selected item. 218 * 219 * @return the color for the selected item 220 */ 221 public Color getFocusColor() { 222 return FOCUS_COLOR; 223 } 224 225 /** 226 * Get foreground color for the selected item. 227 * 228 * @return the foreground color for the selected item 229 */ 230 public Color getFocusForeColor() { 231 return FOCUS_FORECOLOR; 232 } 233 234 /** 235 * Get a "focus enabled" color - a small rectangle around the item 236 * should be drawn when the component got the focus. 237 * 238 * @return the "focus enabled" color 239 */ 240 public Color getFocusEnabledColor() { 241 return FOCUS_ENABLED_COLOR; 242 } 243 244 /** 245 * Get border width. 246 * 247 * @return the border width 248 */ 249 public int getBorderWidth() { 250 return BORDER_WIDTH; 251 } 252 253 /** 254 * Get the list item count. 255 * 256 * @return the count of items 257 */ 258 public int getItemCount() { 259 return stringList.size(); 260 } 261 262 /** 263 * Get the specified item from the list. 264 * 265 * @param index the index 266 * @return the item string 267 */ 268 public String getItem(int index) { 269 return (String)stringList.elementAt(index); 270 } 271 272 /** 273 * Get array of items from the list. 274 * 275 * @return the array of item strings 276 */ 277 public String[] getItems() { 278 String str[] = new String[getItemCount()]; 279 int count = 0; 280 for (Enumeration e = stringList.elements(); e.hasMoreElements(); ) { 281 str[count++] = (String)e.nextElement(); 282 } 283 return str; 284 } 285 286 /** 287 * Check whether the component can be a focus owner (explicitly enabled here). 288 * 289 * @return {@code true} if the component is focusable 290 */ 291 @Override 292 public boolean isFocusTraversable() { 293 return true; 294 } 295 296 /** 297 * Check whether mouse click point lies within the list of items. 298 * 299 * @param pt the click point 300 * @return {@code true} if the click point lies within the list of items 301 */ 302 @Override 303 public boolean contains(Point pt) { 304 Rectangle rect = new Rectangle(); 305 Dimension d = getSize(); 306 rect.x = getBorderWidth(); 307 rect.y = getBorderWidth(); 308 rect.width = d.width - (getBorderWidth() * 2); 309 rect.height = d.height - (getBorderWidth() * 2); 310 return rect.contains(pt); 311 } 312 313 /** 314 * Given a click point the item that has to be selected is found from the list 315 * and focusIndex variable is set accordingly. 316 * 317 * @param pt the click point 318 */ 319 private void findSelectedIndex(Point pt) { 320 Font f = getFont(); 321 FontMetrics fm = getFontMetrics(f); 322 focusIndex = pt.y / fm.getHeight() - 1; 323 if (multipleMode) { 324 Integer fi = focusIndex; 325 if (selList.contains(fi)) { 326 int i = selList.indexOf(fi); 327 selList.removeElementAt(i); 328 } else { 329 selList.addElement(fi); 330 } 331 } 332 } 333 334 /** 335 * Set index of the selected item. 336 * 337 * @param index the index 338 */ 339 public void setSelectedIndex(int index) { 340 prevfocusIndex = focusIndex; 341 focusIndex = index; 342 } 343 344 /** 345 * Get the selected item index. 346 * 347 * @return the selected item index. 348 */ 349 public int getSelectedIndex() { 350 return focusIndex; 351 } 352 353 /** 354 * Get an array of the selected Objects. 355 * 356 * @return array of the Objects 357 */ 358 @Override 359 public Object[] getSelectedObjects() { 360 int ai[] = getSelectedIndexes(); 361 Object aobj[] = new Object[selList.size()]; 362 for (int i = 0; i < selList.size(); i++) { 363 aobj[i] = stringList.elementAt(ai[i]); 364 } 365 return aobj; 366 } 367 368 /** 369 * Get an array of the selected item indices. 370 * 371 * @return the array of the indices 372 */ 373 public int[] getSelectedIndexes() { 374 int ai[] = new int[selList.size()]; 375 for (int i = 0; i < selList.size(); i++) { 376 ai[i] = ((Integer)selList.elementAt(i)); 377 } 378 return ai; 379 } 380 381 /** 382 * Add the specified item listener to receive item events from the list. 383 * 384 * @param itemlistener the item listener 385 */ 386 @Override 387 public synchronized void addItemListener(ItemListener itemlistener) { 388 itemListener = AWTEventMulticaster.add(itemListener, itemlistener); 389 enableEvents(AWTEvent.ITEM_EVENT_MASK); 390 } 391 392 /** 393 * Remove the specified item listener so 394 * that it no longer receives item events from this list. 395 * 396 * @param itemlistener the item listener 397 */ 398 @Override 399 public synchronized void removeItemListener(ItemListener itemlistener) { 400 itemListener = AWTEventMulticaster.remove(itemListener, itemlistener); 401 } 402 403 /** 404 * Add the specified action listener to receive action events from this list. 405 * 406 * @param listener the action listener 407 */ 408 public synchronized void addActionListener(ActionListener listener) { 409 actionListener = AWTEventMulticaster.add(actionListener, listener); 410 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 411 } 412 413 /** 414 * Remove the specified action listener so 415 * that it no longer receives action events from this list. 416 * 417 * @param listener the action listener 418 */ 419 public synchronized void removeActionListener(ActionListener listener) { 420 actionListener = AWTEventMulticaster.remove(actionListener, listener); 421 } 422 423 /** 424 * Add the specified key listener to receive key events from this component. 425 * 426 * @param listener the key listener 427 */ 428 @Override 429 public synchronized void addKeyListener(KeyListener listener) { 430 keyListener = AWTEventMulticaster.add(keyListener, listener); 431 enableEvents(AWTEvent.KEY_EVENT_MASK); 432 } 433 434 /** 435 * Remove the specified key listener so 436 * that it no longer receives key events from this component. 437 * 438 * @param listener the key listener 439 */ 440 @Override 441 public synchronized void removeKeyListener(KeyListener listener) { 442 keyListener = AWTEventMulticaster.remove(keyListener, listener); 443 } 444 445 /** 446 * Add the specified focus listener to receive focus events 447 * from this component when it gains input focus. 448 * 449 * @param listener the focus listener 450 */ 451 @Override 452 public synchronized void addFocusListener(FocusListener listener) { 453 focusListener = AWTEventMulticaster.add(focusListener, listener); 454 enableEvents(AWTEvent.FOCUS_EVENT_MASK); 455 } 456 457 /** 458 * Remove the specified focus listener so 459 * that it no longer receives focus events from this component. 460 * 461 * @param listener the focus listener 462 */ 463 @Override 464 public synchronized void removeFocusListener(FocusListener listener) { 465 focusListener = AWTEventMulticaster.remove(focusListener, listener); 466 } 467 468 @Override 469 protected void processEvent(AWTEvent awtevent) { 470 471 if (awtevent instanceof FocusEvent) { 472 processFocusEvent((FocusEvent)awtevent); 473 } else if (awtevent instanceof ItemEvent) { 474 processItemEvent((ItemEvent)awtevent); 475 } else if (awtevent instanceof KeyEvent) { 476 processKeyEvent((KeyEvent)awtevent); 477 } else if (awtevent instanceof MouseEvent) { 478 switch (awtevent.getID()) { 479 case MouseEvent.MOUSE_CLICKED: 480 case MouseEvent.MOUSE_PRESSED: 481 case MouseEvent.MOUSE_RELEASED: 482 case MouseEvent.MOUSE_ENTERED: 483 case MouseEvent.MOUSE_EXITED: 484 processMouseEvent((MouseEvent)awtevent); 485 break; 486 487 case MouseEvent.MOUSE_MOVED: 488 case MouseEvent.MOUSE_DRAGGED: 489 super.processEvent((MouseEvent)awtevent); 490 break; 491 } 492 } else { 493 if (awtevent instanceof ComponentEvent) 494 super.processComponentEvent((ComponentEvent)awtevent); 495 else 496 super.processEvent(awtevent); 497 } 498 } 499 500 protected void processItemEvent(ItemEvent itemevent) { 501 if (itemListener != null) { 502 itemListener.itemStateChanged(itemevent); 503 } 504 } 505 506 @Override 507 protected void processFocusEvent(FocusEvent e) { 508 switch (e.getID()) { 509 case FocusEvent.FOCUS_GAINED: 510 if (focusListener != null) { focusListener.focusGained(e); } 511 if (getSelectedIndex() == -1) { setSelectedIndex(0); } 512 focusEnabled = true; 513 repaint(); 514 break; 515 case FocusEvent.FOCUS_LOST: 516 if (focusListener != null) { 517 focusListener.focusLost(e); 518 } 519 focusEnabled = false; 520 repaint(); 521 break; 522 } 523 super.processFocusEvent(e); 524 } 525 526 @Override 527 protected void processKeyEvent(KeyEvent e) { 528 rows = getItemCount(); 529 530 switch (e.getID()) { 531 532 case KeyEvent.KEY_TYPED: 533 if (keyListener != null) { 534 keyListener.keyTyped(e); 535 } 536 break; 537 538 case KeyEvent.KEY_PRESSED: 539 if (keyListener != null) { 540 keyListener.keyPressed(e); 541 } 542 if (e.getKeyCode() == KeyEvent.VK_DOWN) { 543 prevfocusIndex = focusIndex; 544 int index = getSelectedIndex() + 1; 545 if (index > rows) { break; } 546 setSelectedIndex(index); 547 processItemEvent(new ItemEvent(this, 0, index, 0)); 548 eventOccurred = true; 549 repaint(); 550 } else if (e.getKeyCode() == KeyEvent.VK_UP) { 551 int index = getSelectedIndex()-1; 552 if (index >= 0) { 553 setSelectedIndex(index); 554 if (e.getID() != 400) { 555 processItemEvent(new ItemEvent(this, 0, index, 0)); 556 } 557 eventOccurred = true; 558 repaint(); 559 } 560 } 561 break; 562 563 case KeyEvent.KEY_RELEASED: 564 if (keyListener != null) { 565 keyListener.keyReleased(e); 566 } 567 if (e.getKeyCode() == KeyEvent.VK_ENTER) { 568 eventOccurred = true; 569 570 // ActionEvent is fired here 571 if (actionListener != null) { 572 actionListener.actionPerformed( new ActionEvent( 573 this, ActionEvent.ACTION_PERFORMED, null)); 574 } 575 repaint(); 576 } 577 break; 578 } // switch 579 super.processKeyEvent(e); 580 } 581 582 @Override 583 protected void processMouseEvent(MouseEvent e) { 584 switch (e.getID()) { 585 case MouseEvent.MOUSE_PRESSED: 586 pressed = true; 587 if (contains(e.getPoint())) { 588 findSelectedIndex(e.getPoint()); 589 processItemEvent(new ItemEvent(this, 0, focusIndex, 0)); 590 eventOccurred = true; 591 } 592 repaint(); 593 break; 594 595 case MouseEvent.MOUSE_RELEASED: 596 if (pressed) { requestFocus(); } 597 598 if (contains(e.getPoint())) { 599 findSelectedIndex(e.getPoint()); 600 eventOccurred = true; 601 } 602 // ActionEvent is fired here 603 if (actionListener != null) { 604 actionListener.actionPerformed(new ActionEvent( 605 this, ActionEvent.ACTION_PERFORMED, null)); 606 } 607 608 if (pressed) { 609 pressed = false; 610 repaint(); 611 } 612 break; 613 } 614 super.processMouseEvent(e); 615 } 616 617 @Override 618 /** 619 * Paint the list. 620 * 621 * @param g the graphics context to be used for testing 622 */ 623 public void paint(Graphics g) { 624 super.paint(g); 625 restrictGraphicsToClientArea(g); 626 627 Point loc = getClientLocation(); 628 Dimension dim = getClientSize(); 629 Color prevColor = g.getColor(); 630 631 // List border is drawn here 632 g.setColor(getBackground()); 633 g.fillRect(0, 0, dim.width - 2, dim.height - 2); 634 g.setColor(getBorder()); 635 g.drawRect(0, 0, dim.width - 2, dim.height - 2); 636 637 if (getItemCount() > 0) { 638 Font f = getFont(); 639 if (f != null) { 640 String str[] = getItems(); 641 FontMetrics fm = getFontMetrics(f); 642 int drawRow = loc.x + getBorderWidth() + fm.getAscent(); 643 int drawCol = loc.y + getBorderWidth(); 644 int rectRow = loc.y + getBorderWidth(); 645 int i = 0; 646 647 // Draw items (if the items exceeds visibility those items will be truncated 648 // as scrollbar support is not enabled 649 650 for (; 651 i < str.length && drawRow < (dim.height - getBorderWidth()); 652 i++) { 653 if (fm.stringWidth(str[i]) < (dim.width - (getBorderWidth() * 2))) { 654 drawItem(g, i, drawCol, drawRow, rectRow, fm); 655 drawRow += fm.getHeight(); 656 rectRow += fm.getHeight(); 657 } else { 658 LWComponent.errorMsg("string width exceeds list width"); 659 LWComponent.errorMsg("Horizontal scrollbar support is not available"); 660 } 661 } // for 662 663 if ( (drawRow > (dim.height - getBorderWidth())) && (str.length > i) ) { 664 //LWComponent.errorMsg("no of strings exceeds list height"); 665 //LWComponent.errorMsg("Vertical scrollbar support is not available"); 666 } 667 } else { LWComponent.errorMsg("Font not available.."); } 668 } 669 670 eventOccurred = false; 671 g.setColor(prevColor); 672 unrestrictGraphicsFromClientArea(g); 673 } 674 675 // Draw String items 676 private void drawItem(Graphics g, int listIndex, int drawCol, 677 int drawRow, int rectRow, FontMetrics fm) { 678 Point loc = getClientLocation(); 679 Dimension dim = getClientSize(); 680 String str = getItem(listIndex); 681 if (multipleMode) { 682 for (int i1 = 0; i1 < selList.size(); i1++) { 683 if (listIndex == ((Integer)selList.elementAt(i1))) { 684 g.setColor(getFocusColor()); 685 g.fillRect(loc.x + getBorderWidth(), 686 rectRow, 687 dim.width - getBorderWidth() * 2, 688 fm.getHeight()); 689 g.setColor(getFocusEnabledColor()); 690 g.drawRect(loc.x + getBorderWidth(), 691 rectRow, 692 dim.width - getBorderWidth() * 2, 693 fm.getHeight()); 694 } 695 } // for 696 } else { 697 if (listIndex == getSelectedIndex() && !multipleMode) { 698 g.setColor(getFocusColor()); 699 g.fillRect(loc.x + getBorderWidth(), 700 rectRow, 701 dim.width - getBorderWidth() * 2, 702 fm.getHeight()); 703 g.setColor(getFocusForeColor()); 704 } 705 if ((listIndex == prevfocusIndex) && (prevfocusIndex != getSelectedIndex()) && !multipleMode) { 706 g.setColor(getBackground()); 707 g.fillRect(loc.x + getBorderWidth(), 708 rectRow, 709 dim.width - getBorderWidth() * 2, 710 fm.getHeight()); 711 prevfocusIndex = getSelectedIndex(); 712 } 713 if (focusEnabled && listIndex == getSelectedIndex() && !multipleMode) { 714 g.setColor(getFocusEnabledColor()); 715 g.drawRect(loc.x + getBorderWidth(), 716 rectRow, 717 dim.width - getBorderWidth() * 2, 718 fm.getHeight()); 719 } 720 } 721 g.setColor(getForeground()); 722 g.drawString(str,drawCol,drawRow); 723 } 724 725 } 726