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