GNU Classpath (0.20) | |
Frames | No Frames |
1: /* BasicComboBoxUI.java -- 2: Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.plaf.basic; 40: 41: import java.awt.Color; 42: import java.awt.Component; 43: import java.awt.Container; 44: import java.awt.Dimension; 45: import java.awt.Font; 46: import java.awt.FontMetrics; 47: import java.awt.Graphics; 48: import java.awt.Insets; 49: import java.awt.LayoutManager; 50: import java.awt.Rectangle; 51: import java.awt.event.FocusEvent; 52: import java.awt.event.FocusListener; 53: import java.awt.event.ItemEvent; 54: import java.awt.event.ItemListener; 55: import java.awt.event.KeyAdapter; 56: import java.awt.event.KeyEvent; 57: import java.awt.event.KeyListener; 58: import java.awt.event.MouseAdapter; 59: import java.awt.event.MouseEvent; 60: import java.awt.event.MouseListener; 61: import java.awt.event.MouseMotionListener; 62: import java.beans.PropertyChangeEvent; 63: import java.beans.PropertyChangeListener; 64: 65: import javax.accessibility.Accessible; 66: import javax.swing.CellRendererPane; 67: import javax.swing.ComboBoxEditor; 68: import javax.swing.ComboBoxModel; 69: import javax.swing.JButton; 70: import javax.swing.JComboBox; 71: import javax.swing.JComponent; 72: import javax.swing.JList; 73: import javax.swing.ListCellRenderer; 74: import javax.swing.LookAndFeel; 75: import javax.swing.SwingUtilities; 76: import javax.swing.UIManager; 77: import javax.swing.event.ListDataEvent; 78: import javax.swing.event.ListDataListener; 79: import javax.swing.plaf.ComboBoxUI; 80: import javax.swing.plaf.ComponentUI; 81: import javax.swing.plaf.UIResource; 82: 83: /** 84: * A UI delegate for the {@link JComboBox} component. 85: * 86: * @author Olga Rodimina 87: * @author Robert Schuster 88: */ 89: public class BasicComboBoxUI extends ComboBoxUI 90: { 91: /** 92: * The arrow button that is displayed in the right side of JComboBox. This 93: * button is used to hide and show combo box's list of items. 94: */ 95: protected JButton arrowButton; 96: 97: /** 98: * The combo box represented by this UI delegate. 99: */ 100: protected JComboBox comboBox; 101: 102: /** 103: * The component that is responsible for displaying/editing the selected 104: * item of the combo box. 105: * 106: * @see BasicComboBoxEditor#getEditorComponent() 107: */ 108: protected Component editor; 109: 110: /** 111: * A listener listening to focus events occurring in the {@link JComboBox}. 112: */ 113: protected FocusListener focusListener; 114: 115: /** 116: * A flag indicating whether JComboBox currently has the focus. 117: */ 118: protected boolean hasFocus; 119: 120: /** 121: * A listener listening to item events fired by the {@link JComboBox}. 122: */ 123: protected ItemListener itemListener; 124: 125: /** 126: * A listener listening to key events that occur while {@link JComboBox} has 127: * the focus. 128: */ 129: protected KeyListener keyListener; 130: 131: /** 132: * A listener listening to mouse events occuring in the {@link JComboBox}. 133: */ 134: private MouseListener mouseListener; 135: 136: /** 137: * List used when rendering selected item of the combo box. The selection 138: * and foreground colors for combo box renderer are configured from this 139: * list. 140: */ 141: protected JList listBox; 142: 143: /** 144: * ListDataListener listening to JComboBox model 145: */ 146: protected ListDataListener listDataListener; 147: 148: /** 149: * Popup list containing the combo box's menu items. 150: */ 151: protected ComboPopup popup; 152: 153: protected KeyListener popupKeyListener; 154: 155: protected MouseListener popupMouseListener; 156: 157: protected MouseMotionListener popupMouseMotionListener; 158: 159: /** 160: * Listener listening to changes in the bound properties of JComboBox 161: */ 162: protected PropertyChangeListener propertyChangeListener; 163: 164: /** 165: * The button background. 166: * @see #installDefaults() 167: */ 168: private Color buttonBackground; 169: 170: /** 171: * The button shadow. 172: * @see #installDefaults() 173: */ 174: private Color buttonShadow; 175: 176: /** 177: * The button dark shadow. 178: * @see #installDefaults() 179: */ 180: private Color buttonDarkShadow; 181: 182: /** 183: * The button highlight. 184: * @see #installDefaults() 185: */ 186: private Color buttonHighlight; 187: 188: /* Size of the largest item in the comboBox 189: * This is package-private to avoid an accessor method. 190: */ 191: Dimension displaySize; 192: 193: // FIXME: This field isn't used anywhere at this moment. 194: protected CellRendererPane currentValuePane; 195: 196: /** 197: * The current minimum size if isMinimumSizeDirty is false. 198: * Setup by getMinimumSize() and invalidated by the various listeners. 199: */ 200: protected Dimension cachedMinimumSize; 201: 202: /** 203: * Indicates whether or not the cachedMinimumSize field is valid or not. 204: */ 205: protected boolean isMinimumSizeDirty = true; 206: 207: /** 208: * Creates a new <code>BasicComboBoxUI</code> object. 209: */ 210: public BasicComboBoxUI() 211: { 212: // Nothing to do here. 213: } 214: 215: /** 216: * A factory method to create a UI delegate for the given 217: * {@link JComponent}, which should be a {@link JComboBox}. 218: * 219: * @param c The {@link JComponent} a UI is being created for. 220: * 221: * @return A UI delegate for the {@link JComponent}. 222: */ 223: public static ComponentUI createUI(JComponent c) 224: { 225: return new BasicComboBoxUI(); 226: } 227: 228: /** 229: * Installs the UI for the given {@link JComponent}. 230: * 231: * @param c the JComponent to install a UI for. 232: * 233: * @see #uninstallUI(JComponent) 234: */ 235: public void installUI(JComponent c) 236: { 237: super.installUI(c); 238: 239: if (c instanceof JComboBox) 240: { 241: comboBox = (JComboBox) c; 242: comboBox.setOpaque(true); 243: comboBox.setLayout(createLayoutManager()); 244: installDefaults(); 245: installComponents(); 246: installListeners(); 247: installKeyboardActions(); 248: } 249: } 250: 251: /** 252: * Uninstalls the UI for the given {@link JComponent}. 253: * 254: * @param c The JComponent that is having this UI removed. 255: * 256: * @see #installUI(JComponent) 257: */ 258: public void uninstallUI(JComponent c) 259: { 260: uninstallKeyboardActions(); 261: uninstallListeners(); 262: uninstallComponents(); 263: uninstallDefaults(); 264: comboBox = null; 265: } 266: 267: /** 268: * Installs the defaults that are defined in the {@link BasicLookAndFeel} 269: * for this {@link JComboBox}. 270: * 271: * @see #uninstallDefaults() 272: */ 273: protected void installDefaults() 274: { 275: LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background", 276: "ComboBox.foreground", "ComboBox.font"); 277: 278: // fetch the button color scheme 279: buttonBackground = UIManager.getColor("ComboBox.buttonBackground"); 280: buttonShadow = UIManager.getColor("ComboBox.buttonShadow"); 281: buttonDarkShadow = UIManager.getColor("ComboBox.buttonDarkShadow"); 282: buttonHighlight = UIManager.getColor("ComboBox.buttonHighlight"); 283: } 284: 285: /** 286: * Creates and installs the listeners for this UI. 287: * 288: * @see #uninstallListeners() 289: */ 290: protected void installListeners() 291: { 292: // install combo box's listeners 293: propertyChangeListener = createPropertyChangeListener(); 294: comboBox.addPropertyChangeListener(propertyChangeListener); 295: 296: focusListener = createFocusListener(); 297: editor.addFocusListener(focusListener); 298: 299: itemListener = createItemListener(); 300: comboBox.addItemListener(itemListener); 301: 302: keyListener = createKeyListener(); 303: comboBox.addKeyListener(keyListener); 304: 305: mouseListener = createMouseListener(); 306: arrowButton.addMouseListener(mouseListener); 307: 308: // install listeners that listen to combo box model 309: listDataListener = createListDataListener(); 310: comboBox.getModel().addListDataListener(listDataListener); 311: } 312: 313: /** 314: * Uninstalls the defaults and sets any objects created during 315: * install to <code>null</code>. 316: * 317: * @see #installDefaults() 318: */ 319: protected void uninstallDefaults() 320: { 321: if (comboBox.getFont() instanceof UIResource) 322: comboBox.setFont(null); 323: 324: if (comboBox.getForeground() instanceof UIResource) 325: comboBox.setForeground(null); 326: 327: if (comboBox.getBackground() instanceof UIResource) 328: comboBox.setBackground(null); 329: 330: buttonBackground = null; 331: buttonShadow = null; 332: buttonDarkShadow = null; 333: buttonHighlight = null; 334: } 335: 336: /** 337: * Detaches all the listeners we attached in {@link #installListeners}. 338: * 339: * @see #installListeners() 340: */ 341: protected void uninstallListeners() 342: { 343: comboBox.removePropertyChangeListener(propertyChangeListener); 344: propertyChangeListener = null; 345: 346: comboBox.removeFocusListener(focusListener); 347: listBox.removeFocusListener(focusListener); 348: focusListener = null; 349: 350: comboBox.removeItemListener(itemListener); 351: itemListener = null; 352: 353: comboBox.removeKeyListener(keyListener); 354: keyListener = null; 355: 356: arrowButton.removeMouseListener(mouseListener); 357: mouseListener = null; 358: 359: comboBox.getModel().removeListDataListener(listDataListener); 360: listDataListener = null; 361: } 362: 363: /** 364: * Creates the popup that will contain list of combo box's items. 365: * 366: * @return popup containing list of combo box's items 367: */ 368: protected ComboPopup createPopup() 369: { 370: return new BasicComboPopup(comboBox); 371: } 372: 373: /** 374: * Creates a {@link KeyListener} to listen to key events. 375: * 376: * @return KeyListener that listens to key events. 377: */ 378: protected KeyListener createKeyListener() 379: { 380: return new KeyHandler(); 381: } 382: 383: /** 384: * Creates a {@link MouseListener} that will listen to mouse events occurring 385: * in the combo box. 386: * 387: * @return the MouseListener 388: */ 389: private MouseListener createMouseListener() 390: { 391: return new MouseHandler(); 392: } 393: 394: /** 395: * Creates the {@link FocusListener} that will listen to changes in this 396: * JComboBox's focus. 397: * 398: * @return the FocusListener. 399: */ 400: protected FocusListener createFocusListener() 401: { 402: return new FocusHandler(); 403: } 404: 405: /** 406: * Creates a {@link ListDataListener} to listen to the combo box's data model. 407: * 408: * @return The new listener. 409: */ 410: protected ListDataListener createListDataListener() 411: { 412: return new ListDataHandler(); 413: } 414: 415: /** 416: * Creates an {@link ItemListener} that will listen to the changes in 417: * the JComboBox's selection. 418: * 419: * @return The ItemListener 420: */ 421: protected ItemListener createItemListener() 422: { 423: return new ItemHandler(); 424: } 425: 426: /** 427: * Creates a {@link PropertyChangeListener} to listen to the changes in 428: * the JComboBox's bound properties. 429: * 430: * @return The PropertyChangeListener 431: */ 432: protected PropertyChangeListener createPropertyChangeListener() 433: { 434: return new PropertyChangeHandler(); 435: } 436: 437: /** 438: * Creates and returns a layout manager for the combo box. Subclasses can 439: * override this method to provide a different layout. 440: * 441: * @return a layout manager for the combo box. 442: */ 443: protected LayoutManager createLayoutManager() 444: { 445: return new ComboBoxLayoutManager(); 446: } 447: 448: /** 449: * Creates a component that will be responsible for rendering the 450: * selected component in the combo box. 451: * 452: * @return A renderer for the combo box. 453: */ 454: protected ListCellRenderer createRenderer() 455: { 456: return new BasicComboBoxRenderer(); 457: } 458: 459: /** 460: * Creates the component that will be responsible for displaying/editing 461: * the selected item in the combo box. This editor is used only when combo 462: * box is editable. 463: * 464: * @return A new component that will be responsible for displaying/editing 465: * the selected item in the combo box. 466: */ 467: protected ComboBoxEditor createEditor() 468: { 469: return new BasicComboBoxEditor.UIResource(); 470: } 471: 472: /** 473: * Installs the components for this JComboBox. ArrowButton, main 474: * part of combo box (upper part) and popup list of items are created and 475: * configured here. 476: */ 477: protected void installComponents() 478: { 479: // create drop down list of items 480: popup = createPopup(); 481: listBox = popup.getList(); 482: 483: // set editor and renderer for the combo box. Editor is used 484: // only if combo box becomes editable, otherwise renderer is used 485: // to paint the selected item; combobox is not editable by default. 486: comboBox.setRenderer(createRenderer()); 487: 488: // create and install arrow button 489: arrowButton = createArrowButton(); 490: configureArrowButton(); 491: comboBox.add(arrowButton); 492: 493: ComboBoxEditor currentEditor = comboBox.getEditor(); 494: if (currentEditor == null || currentEditor instanceof UIResource) 495: { 496: currentEditor = createEditor(); 497: comboBox.setEditor(currentEditor); 498: } 499: editor = currentEditor.getEditorComponent(); 500: 501: comboBox.revalidate(); 502: } 503: 504: /** 505: * Uninstalls components from this {@link JComboBox}. 506: * 507: * @see #installComponents() 508: */ 509: protected void uninstallComponents() 510: { 511: // uninstall arrow button 512: unconfigureArrowButton(); 513: comboBox.remove(arrowButton); 514: arrowButton = null; 515: 516: listBox = null; 517: popup = null; 518: 519: comboBox.setRenderer(null); 520: 521: // if the editor is not an instanceof UIResource, it was not set by the 522: // UI delegate, so don't clear it... 523: ComboBoxEditor currentEditor = comboBox.getEditor(); 524: if (currentEditor instanceof UIResource) 525: { 526: comboBox.setEditor(null); 527: editor = null; 528: } 529: } 530: 531: /** 532: * Adds the current editor to the combo box. 533: */ 534: public void addEditor() 535: { 536: comboBox.add(editor); 537: } 538: 539: /** 540: * Removes the current editor from the combo box. 541: */ 542: public void removeEditor() 543: { 544: comboBox.remove(editor); 545: } 546: 547: /** 548: * Configures the editor for this combo box. 549: */ 550: protected void configureEditor() 551: { 552: editor.setFont(comboBox.getFont()); 553: comboBox.getEditor().setItem(comboBox.getSelectedItem()); 554: // FIXME: Need to implement. Set font and add listeners. 555: } 556: 557: /** 558: * Unconfigures the editor for this combo nox. This method is not implemented. 559: */ 560: protected void unconfigureEditor() 561: { 562: // FIXME: Need to implement 563: } 564: 565: /** 566: * Configures the arrow button. 567: * 568: * @see #configureArrowButton() 569: */ 570: public void configureArrowButton() 571: { 572: arrowButton.setEnabled(comboBox.isEnabled()); 573: arrowButton.setFont(comboBox.getFont()); 574: arrowButton.setFocusable(false); 575: } 576: 577: /** 578: * Unconfigures the arrow button. 579: * 580: * @see #configureArrowButton() 581: * 582: * @specnote The specification says this method is implementation specific 583: * and should not be used or overridden. 584: */ 585: public void unconfigureArrowButton() 586: { 587: // Nothing to do here yet. 588: } 589: 590: /** 591: * Creates an arrow button for this {@link JComboBox}. The arrow button is 592: * displayed at the right end of the combo box and is used to display/hide 593: * the drop down list of items. 594: * 595: * @return A new button. 596: */ 597: protected JButton createArrowButton() 598: { 599: return new BasicArrowButton(BasicArrowButton.SOUTH, buttonBackground, 600: buttonShadow, buttonDarkShadow, buttonHighlight); 601: } 602: 603: /** 604: * Returns <code>true</code> if the popup is visible, and <code>false</code> 605: * otherwise. 606: * 607: * @param c The JComboBox to check 608: * 609: * @return <code>true</code> if popup part of the JComboBox is visible and 610: * <code>false</code> otherwise. 611: */ 612: public boolean isPopupVisible(JComboBox c) 613: { 614: return popup.isVisible(); 615: } 616: 617: /** 618: * Displays/hides the {@link JComboBox}'s list of items on the screen. 619: * 620: * @param c The combo box, for which list of items should be 621: * displayed/hidden 622: * @param v true if show popup part of the jcomboBox and false to hide. 623: */ 624: public void setPopupVisible(JComboBox c, boolean v) 625: { 626: if (v) 627: popup.show(); 628: else 629: popup.hide(); 630: 631: if (comboBox.isEditable()) 632: editor.requestFocus(); 633: else 634: comboBox.requestFocus(); 635: } 636: 637: /** 638: * JComboBox is focus traversable if it is editable and not otherwise. 639: * 640: * @param c combo box for which to check whether it is focus traversable 641: * 642: * @return true if focus tranversable and false otherwise 643: */ 644: public boolean isFocusTraversable(JComboBox c) 645: { 646: if (!comboBox.isEditable()) 647: return true; 648: 649: return false; 650: } 651: 652: /** 653: * Paints given menu item using specified graphics context 654: * 655: * @param g The graphics context used to paint this combo box 656: * @param c comboBox which needs to be painted. 657: */ 658: public void paint(Graphics g, JComponent c) 659: { 660: Rectangle rect = rectangleForCurrentValue(); 661: paintCurrentValueBackground(g, rect, hasFocus); 662: paintCurrentValue(g, rect, hasFocus); 663: } 664: 665: /** 666: * Returns preferred size for the combo box. 667: * 668: * @param c comboBox for which to get preferred size 669: * 670: * @return The preferred size for the given combo box 671: */ 672: public Dimension getPreferredSize(JComponent c) 673: { 674: // note: overriding getMinimumSize() (for example in the MetalComboBoxUI 675: // class) affects the getPreferredSize() result, so it seems logical that 676: // this method is implemented by delegating to the getMinimumSize() method 677: return getMinimumSize(c); 678: } 679: 680: /** 681: * Returns the minimum size for this {@link JComboBox} for this 682: * look and feel. Also makes sure cachedMinimimSize is setup correctly. 683: * 684: * @param c The {@link JComponent} to find the minimum size for. 685: * 686: * @return The dimensions of the minimum size. 687: */ 688: public Dimension getMinimumSize(JComponent c) 689: { 690: if (isMinimumSizeDirty) 691: { 692: Dimension d = getDisplaySize(); 693: int arrowButtonWidth = d.height; 694: cachedMinimumSize = new Dimension(d.width + arrowButtonWidth, 695: d.height); 696: isMinimumSizeDirty = false; 697: } 698: return new Dimension(cachedMinimumSize); 699: } 700: 701: /** The value returned by the getMaximumSize() method. */ 702: private static final Dimension MAXIMUM_SIZE = new Dimension(32767, 32767); 703: 704: /** 705: * Returns the maximum size for this {@link JComboBox} for this 706: * look and feel. 707: * 708: * @param c The {@link JComponent} to find the maximum size for 709: * 710: * @return The maximum size (<code>Dimension(32767, 32767)</code>). 711: */ 712: public Dimension getMaximumSize(JComponent c) 713: { 714: return MAXIMUM_SIZE; 715: } 716: 717: public int getAccessibleChildrenCount(JComponent c) 718: { 719: // FIXME: Need to implement 720: return 0; 721: } 722: 723: public Accessible getAccessibleChild(JComponent c, int i) 724: { 725: // FIXME: Need to implement 726: return null; 727: } 728: 729: /** 730: * Returns true if the specified key is a navigation key and false otherwise 731: * 732: * @param keyCode a key for which to check whether it is navigation key or 733: * not. 734: * 735: * @return true if the specified key is a navigation key and false otherwis 736: */ 737: protected boolean isNavigationKey(int keyCode) 738: { 739: return false; 740: } 741: 742: /** 743: * Selects next possible item relative to the current selection 744: * to be next selected item in the combo box. 745: */ 746: protected void selectNextPossibleValue() 747: { 748: int index = comboBox.getSelectedIndex(); 749: if (index != comboBox.getItemCount() - 1) 750: comboBox.setSelectedIndex(index + 1); 751: } 752: 753: /** 754: * Selects previous item relative to current selection to be 755: * next selected item. 756: */ 757: protected void selectPreviousPossibleValue() 758: { 759: int index = comboBox.getSelectedIndex(); 760: if (index != 0) 761: comboBox.setSelectedIndex(index - 1); 762: } 763: 764: /** 765: * Displays combo box popup if the popup is not currently shown 766: * on the screen and hides it if it is currently shown 767: */ 768: protected void toggleOpenClose() 769: { 770: setPopupVisible(comboBox, ! isPopupVisible(comboBox)); 771: } 772: 773: /** 774: * Returns the bounds in which comboBox's selected item will be 775: * displayed. 776: * 777: * @return rectangle bounds in which comboBox's selected Item will be 778: * displayed 779: */ 780: protected Rectangle rectangleForCurrentValue() 781: { 782: Rectangle cbBounds = SwingUtilities.getLocalBounds(comboBox); 783: Rectangle abBounds = arrowButton.getBounds(); 784: Rectangle rectForCurrentValue = new Rectangle(cbBounds.x, cbBounds.y, 785: cbBounds.width - abBounds.width, cbBounds.height); 786: return rectForCurrentValue; 787: } 788: 789: /** 790: * Returns the insets of the current border. 791: * 792: * @return Insets representing space between combo box and its border 793: */ 794: protected Insets getInsets() 795: { 796: return new Insets(0, 0, 0, 0); 797: } 798: 799: /** 800: * Paints currently selected value in the main part of the combo 801: * box (part without popup). 802: * 803: * @param g graphics context 804: * @param bounds Rectangle representing the size of the area in which 805: * selected item should be drawn 806: * @param hasFocus true if combo box has focus and false otherwise 807: */ 808: public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus) 809: { 810: if (! comboBox.isEditable()) 811: { 812: Object currentValue = comboBox.getSelectedItem(); 813: boolean isPressed = arrowButton.getModel().isPressed(); 814: 815: /* Gets the component to be drawn for the current value. 816: * If there is currently no selected item we will take an empty 817: * String as replacement. 818: */ 819: Component comp = comboBox.getRenderer().getListCellRendererComponent( 820: listBox, (currentValue != null ? currentValue : ""), -1, 821: isPressed, hasFocus); 822: if (! comboBox.isEnabled()) 823: { 824: comp.setBackground(UIManager.getColor( 825: "ComboBox.disabledBackground")); 826: comp.setForeground(UIManager.getColor( 827: "ComboBox.disabledForeground")); 828: comp.setEnabled(false); 829: } 830: comp.setBounds(0, 0, bounds.width, bounds.height); 831: comp.setFont(comboBox.getFont()); 832: comp.paint(g); 833: 834: comboBox.revalidate(); 835: } 836: else 837: comboBox.getEditor().setItem(comboBox.getSelectedItem()); 838: } 839: 840: /** 841: * Paints the background of part of the combo box, where currently 842: * selected value is displayed. If the combo box has focus this method 843: * should also paint focus rectangle around the combo box. 844: * 845: * @param g graphics context 846: * @param bounds Rectangle representing the size of the largest item in the 847: * comboBox 848: * @param hasFocus true if combo box has fox and false otherwise 849: */ 850: public void paintCurrentValueBackground(Graphics g, Rectangle bounds, 851: boolean hasFocus) 852: { 853: // background is painted by renderer, so it seems that nothing 854: // should be done here. 855: } 856: 857: /** 858: * Returns the default size for the display area of a combo box that does 859: * not contain any elements. This method returns the width and height of 860: * a single space in the current font, plus a margin of 1 pixel. 861: * 862: * @return The default display size. 863: * 864: * @see #getDisplaySize() 865: */ 866: protected Dimension getDefaultSize() 867: { 868: // There is nothing in the spec to say how this method should be 869: // implemented...so I've done some guessing, written some Mauve tests, 870: // and written something that gives dimensions that are close to the 871: // reference implementation. 872: FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); 873: int w = fm.charWidth(' ') + 2; 874: int h = fm.getHeight() + 2; 875: return new Dimension(w, h); 876: } 877: 878: /** 879: * Returns the size of the display area for the combo box. This size will be 880: * the size of the combo box, not including the arrowButton. 881: * 882: * @return The size of the display area for the combo box. 883: */ 884: protected Dimension getDisplaySize() 885: { 886: if (!comboBox.isEditable()) 887: { 888: Object prototype = comboBox.getPrototypeDisplayValue(); 889: if (prototype != null) 890: { 891: // calculate result based on prototype 892: ListCellRenderer renderer = comboBox.getRenderer(); 893: Component comp = renderer.getListCellRendererComponent(listBox, 894: prototype, -1, false, false); 895: Dimension compSize = comp.getPreferredSize(); 896: compSize.width += 2; // add 1 pixel margin around area 897: compSize.height += 2; 898: return compSize; 899: } 900: else 901: { 902: ComboBoxModel model = comboBox.getModel(); 903: int numItems = model.getSize(); 904: 905: // if combo box doesn't have any items then simply 906: // return its default size 907: if (numItems == 0) 908: { 909: displaySize = getDefaultSize(); 910: return displaySize; 911: } 912: 913: Dimension size = new Dimension(0, 0); 914: 915: // ComboBox's display size should be equal to the 916: // size of the largest item in the combo box. 917: ListCellRenderer renderer = comboBox.getRenderer(); 918: 919: for (int i = 0; i < numItems; i++) 920: { 921: Object item = model.getElementAt(i); 922: Component comp = renderer.getListCellRendererComponent(listBox, 923: item, -1, false, false); 924: 925: Dimension compSize = comp.getPreferredSize(); 926: if (compSize.width + 2 > size.width) 927: size.width = compSize.width + 2; 928: if (compSize.height + 2 > size.height) 929: size.height = compSize.height + 2; 930: } 931: displaySize = size; 932: return displaySize; 933: } 934: } 935: else // an editable combo, 936: { 937: Component comp = comboBox.getEditor().getEditorComponent(); 938: Dimension prefSize = comp.getPreferredSize(); 939: int width = prefSize.width; 940: int height = prefSize.height + 2; 941: Object prototype = comboBox.getPrototypeDisplayValue(); 942: if (prototype != null) 943: { 944: FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); 945: width = Math.max(width, fm.stringWidth(prototype.toString()) + 2); 946: } 947: displaySize = new Dimension(width, height); 948: return displaySize; 949: } 950: } 951: 952: /** 953: * Installs the keyboard actions for the {@link JComboBox} as specified 954: * by the look and feel. 955: */ 956: protected void installKeyboardActions() 957: { 958: // FIXME: Need to implement. 959: } 960: 961: /** 962: * Uninstalls the keyboard actions for the {@link JComboBox} there were 963: * installed by in {@link #installListeners}. 964: */ 965: protected void uninstallKeyboardActions() 966: { 967: // FIXME: Need to implement. 968: } 969: 970: /** 971: * A {@link LayoutManager} used to position the sub-components of the 972: * {@link JComboBox}. 973: * 974: * @see BasicComboBoxUI#createLayoutManager() 975: */ 976: public class ComboBoxLayoutManager implements LayoutManager 977: { 978: /** 979: * Creates a new ComboBoxLayoutManager object. 980: */ 981: public ComboBoxLayoutManager() 982: { 983: // Nothing to do here. 984: } 985: 986: /** 987: * Adds a component to the layout. This method does nothing, since the 988: * layout manager doesn't need to track the components. 989: * 990: * @param name the name to associate the component with (ignored). 991: * @param comp the component (ignored). 992: */ 993: public void addLayoutComponent(String name, Component comp) 994: { 995: // Do nothing 996: } 997: 998: /** 999: * Removes a component from the layout. This method does nothing, since 1000: * the layout manager doesn't need to track the components. 1001: * 1002: * @param comp the component. 1003: */ 1004: public void removeLayoutComponent(Component comp) 1005: { 1006: // Do nothing 1007: } 1008: 1009: /** 1010: * Returns preferred layout size of the JComboBox. 1011: * 1012: * @param parent the Container for which the preferred size should be 1013: * calculated. 1014: * 1015: * @return The preferred size for the given container 1016: */ 1017: public Dimension preferredLayoutSize(Container parent) 1018: { 1019: return getPreferredSize((JComponent) parent); 1020: } 1021: 1022: /** 1023: * Returns the minimum layout size. 1024: * 1025: * @param parent the container. 1026: * 1027: * @return The minimum size. 1028: */ 1029: public Dimension minimumLayoutSize(Container parent) 1030: { 1031: return preferredLayoutSize(parent); 1032: } 1033: 1034: /** 1035: * Arranges the components in the container. It puts arrow 1036: * button right end part of the comboBox. If the comboBox is editable 1037: * then editor is placed to the left of arrow button, starting from the 1038: * beginning. 1039: * 1040: * @param parent Container that should be layed out. 1041: */ 1042: public void layoutContainer(Container parent) 1043: { 1044: // Position editor component to the left of arrow button if combo box is 1045: // editable 1046: int arrowSize = comboBox.getHeight(); 1047: int editorWidth = comboBox.getBounds().width - arrowSize; 1048: 1049: if (comboBox.isEditable()) 1050: editor.setBounds(0, 0, editorWidth, comboBox.getBounds().height); 1051: 1052: arrowButton.setBounds(editorWidth, 0, arrowSize, arrowSize); 1053: comboBox.revalidate(); 1054: } 1055: } 1056: 1057: /** 1058: * Handles focus changes occuring in the combo box. This class is 1059: * responsible for repainting combo box whenever focus is gained or lost 1060: * and also for hiding popup list of items whenever combo box loses its 1061: * focus. 1062: */ 1063: public class FocusHandler extends Object implements FocusListener 1064: { 1065: /** 1066: * Creates a new FocusHandler object. 1067: */ 1068: public FocusHandler() 1069: { 1070: // Nothing to do here. 1071: } 1072: 1073: /** 1074: * Invoked when combo box gains focus. It repaints main 1075: * part of combo box accordingly. 1076: * 1077: * @param e the FocusEvent 1078: */ 1079: public void focusGained(FocusEvent e) 1080: { 1081: // Lets assume every change invalidates the minimumsize. 1082: isMinimumSizeDirty = true; 1083: 1084: hasFocus = true; 1085: comboBox.repaint(); 1086: } 1087: 1088: /** 1089: * Invoked when the combo box loses focus. It repaints the main part 1090: * of the combo box accordingly and hides the popup list of items. 1091: * 1092: * @param e the FocusEvent 1093: */ 1094: public void focusLost(FocusEvent e) 1095: { 1096: // Lets assume every change invalidates the minimumsize. 1097: isMinimumSizeDirty = true; 1098: 1099: hasFocus = false; 1100: setPopupVisible(comboBox, false); 1101: comboBox.repaint(); 1102: } 1103: } 1104: 1105: /** 1106: * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its 1107: * selected item changes. 1108: */ 1109: public class ItemHandler extends Object implements ItemListener 1110: { 1111: /** 1112: * Creates a new ItemHandler object. 1113: */ 1114: public ItemHandler() 1115: { 1116: // Nothing to do here. 1117: } 1118: 1119: /** 1120: * Invoked when selected item becomes deselected or when 1121: * new item becomes selected. 1122: * 1123: * @param e the ItemEvent representing item's state change. 1124: */ 1125: public void itemStateChanged(ItemEvent e) 1126: { 1127: // Lets assume every change invalidates the minimumsize. 1128: isMinimumSizeDirty = true; 1129: 1130: if (e.getStateChange() == ItemEvent.SELECTED && comboBox.isEditable()) 1131: comboBox.getEditor().setItem(e.getItem()); 1132: comboBox.repaint(); 1133: } 1134: } 1135: 1136: /** 1137: * KeyHandler handles key events occuring while JComboBox has focus. 1138: */ 1139: public class KeyHandler extends KeyAdapter 1140: { 1141: public KeyHandler() 1142: { 1143: // Nothing to do here. 1144: } 1145: 1146: /** 1147: * Invoked whenever key is pressed while JComboBox is in focus. 1148: */ 1149: public void keyPressed(KeyEvent e) 1150: { 1151: // FIXME: This method calls JComboBox.selectWithKeyChar if the key that was 1152: // pressed is not a navigation key. 1153: } 1154: } 1155: 1156: /** 1157: * Handles the changes occurring in the JComboBox's data model. 1158: */ 1159: public class ListDataHandler extends Object implements ListDataListener 1160: { 1161: /** 1162: * Creates a new ListDataHandler object. 1163: */ 1164: public ListDataHandler() 1165: { 1166: // Nothing to do here. 1167: } 1168: 1169: /** 1170: * Invoked if the content's of JComboBox's data model are changed. 1171: * 1172: * @param e ListDataEvent describing the change. 1173: */ 1174: public void contentsChanged(ListDataEvent e) 1175: { 1176: // if the item is selected or deselected 1177: 1178: // Lets assume every change invalidates the minimumsize. 1179: isMinimumSizeDirty = true; 1180: } 1181: 1182: /** 1183: * Invoked when items are added to the JComboBox's data model. 1184: * 1185: * @param e ListDataEvent describing the change. 1186: */ 1187: public void intervalAdded(ListDataEvent e) 1188: { 1189: // Lets assume every change invalidates the minimumsize. 1190: isMinimumSizeDirty = true; 1191: 1192: ComboBoxModel model = comboBox.getModel(); 1193: ListCellRenderer renderer = comboBox.getRenderer(); 1194: 1195: if (displaySize == null) 1196: displaySize = getDisplaySize(); 1197: if (displaySize.width < getDefaultSize().width) 1198: displaySize.width = getDefaultSize().width; 1199: if (displaySize.height < getDefaultSize().height) 1200: displaySize.height = getDefaultSize().height; 1201: 1202: comboBox.repaint(); 1203: } 1204: 1205: /** 1206: * Invoked when items are removed from the JComboBox's 1207: * data model. 1208: * 1209: * @param e ListDataEvent describing the change. 1210: */ 1211: public void intervalRemoved(ListDataEvent e) 1212: { 1213: // Lets assume every change invalidates the minimumsize. 1214: isMinimumSizeDirty = true; 1215: 1216: // recalculate display size of the JComboBox. 1217: displaySize = getDisplaySize(); 1218: comboBox.repaint(); 1219: } 1220: } 1221: 1222: /** 1223: * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}. 1224: */ 1225: public class PropertyChangeHandler extends Object 1226: implements PropertyChangeListener 1227: { 1228: /** 1229: * Creates a new instance. 1230: */ 1231: public PropertyChangeHandler() 1232: { 1233: // Nothing to do here. 1234: } 1235: 1236: /** 1237: * Invoked whenever bound property of JComboBox changes. 1238: * 1239: * @param e the event. 1240: */ 1241: public void propertyChange(PropertyChangeEvent e) 1242: { 1243: // Lets assume every change invalidates the minimumsize. 1244: isMinimumSizeDirty = true; 1245: 1246: if (e.getPropertyName().equals("enabled")) 1247: { 1248: arrowButton.setEnabled(comboBox.isEnabled()); 1249: 1250: if (comboBox.isEditable()) 1251: comboBox.getEditor().getEditorComponent().setEnabled(comboBox 1252: .isEnabled()); 1253: } 1254: else if (e.getPropertyName().equals("editable")) 1255: { 1256: if (comboBox.isEditable()) 1257: { 1258: configureEditor(); 1259: addEditor(); 1260: } 1261: else 1262: { 1263: unconfigureEditor(); 1264: removeEditor(); 1265: } 1266: 1267: comboBox.revalidate(); 1268: comboBox.repaint(); 1269: } 1270: else if (e.getPropertyName().equals("dataModel")) 1271: { 1272: // remove ListDataListener from old model and add it to new model 1273: ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue(); 1274: if (oldModel != null) 1275: oldModel.removeListDataListener(listDataListener); 1276: 1277: if ((ComboBoxModel) e.getNewValue() != null) 1278: comboBox.getModel().addListDataListener(listDataListener); 1279: } 1280: else if (e.getPropertyName().equals("font")) 1281: { 1282: Font font = (Font) e.getNewValue(); 1283: editor.setFont(font); 1284: listBox.setFont(font); 1285: arrowButton.setFont(font); 1286: comboBox.revalidate(); 1287: comboBox.repaint(); 1288: } 1289: 1290: // FIXME: Need to handle changes in other bound properties. 1291: } 1292: } 1293: 1294: /** 1295: * A handler for mouse events occurring in the combo box. An instance of 1296: * this class is returned by the <code>createMouseListener()</code> method. 1297: */ 1298: private class MouseHandler extends MouseAdapter 1299: { 1300: /** 1301: * Invoked when mouse is pressed over the combo box. It toggles the 1302: * visibility of the popup list. 1303: * 1304: * @param e the event 1305: */ 1306: public void mousePressed(MouseEvent e) 1307: { 1308: if (comboBox.isEnabled()) 1309: toggleOpenClose(); 1310: } 1311: } 1312: }
GNU Classpath (0.20) |