| /* BasicComboBoxUI.java -- |
| Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.plaf.basic; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.Container; |
| import java.awt.Dimension; |
| import java.awt.Font; |
| import java.awt.FontMetrics; |
| import java.awt.Graphics; |
| import java.awt.Insets; |
| import java.awt.LayoutManager; |
| import java.awt.Rectangle; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.FocusListener; |
| import java.awt.event.ItemEvent; |
| import java.awt.event.ItemListener; |
| import java.awt.event.KeyAdapter; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.KeyListener; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.MouseListener; |
| import java.awt.event.MouseMotionListener; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import javax.accessibility.Accessible; |
| import javax.swing.CellRendererPane; |
| import javax.swing.ComboBoxEditor; |
| import javax.swing.ComboBoxModel; |
| import javax.swing.JButton; |
| import javax.swing.JComboBox; |
| import javax.swing.JComponent; |
| import javax.swing.JList; |
| import javax.swing.ListCellRenderer; |
| import javax.swing.LookAndFeel; |
| import javax.swing.SwingUtilities; |
| import javax.swing.UIManager; |
| import javax.swing.event.ListDataEvent; |
| import javax.swing.event.ListDataListener; |
| import javax.swing.plaf.ComboBoxUI; |
| import javax.swing.plaf.ComponentUI; |
| import javax.swing.plaf.UIResource; |
| |
| /** |
| * A UI delegate for the {@link JComboBox} component. |
| * |
| * @author Olga Rodimina |
| * @author Robert Schuster |
| */ |
| public class BasicComboBoxUI extends ComboBoxUI |
| { |
| /** |
| * The arrow button that is displayed in the right side of JComboBox. This |
| * button is used to hide and show combo box's list of items. |
| */ |
| protected JButton arrowButton; |
| |
| /** |
| * The combo box represented by this UI delegate. |
| */ |
| protected JComboBox comboBox; |
| |
| /** |
| * The component that is responsible for displaying/editing the selected |
| * item of the combo box. |
| * |
| * @see BasicComboBoxEditor#getEditorComponent() |
| */ |
| protected Component editor; |
| |
| /** |
| * A listener listening to focus events occurring in the {@link JComboBox}. |
| */ |
| protected FocusListener focusListener; |
| |
| /** |
| * A flag indicating whether JComboBox currently has the focus. |
| */ |
| protected boolean hasFocus; |
| |
| /** |
| * A listener listening to item events fired by the {@link JComboBox}. |
| */ |
| protected ItemListener itemListener; |
| |
| /** |
| * A listener listening to key events that occur while {@link JComboBox} has |
| * the focus. |
| */ |
| protected KeyListener keyListener; |
| |
| /** |
| * A listener listening to mouse events occuring in the {@link JComboBox}. |
| */ |
| private MouseListener mouseListener; |
| |
| /** |
| * List used when rendering selected item of the combo box. The selection |
| * and foreground colors for combo box renderer are configured from this |
| * list. |
| */ |
| protected JList listBox; |
| |
| /** |
| * ListDataListener listening to JComboBox model |
| */ |
| protected ListDataListener listDataListener; |
| |
| /** |
| * Popup list containing the combo box's menu items. |
| */ |
| protected ComboPopup popup; |
| |
| protected KeyListener popupKeyListener; |
| |
| protected MouseListener popupMouseListener; |
| |
| protected MouseMotionListener popupMouseMotionListener; |
| |
| /** |
| * Listener listening to changes in the bound properties of JComboBox |
| */ |
| protected PropertyChangeListener propertyChangeListener; |
| |
| /** |
| * The button background. |
| * @see #installDefaults() |
| */ |
| private Color buttonBackground; |
| |
| /** |
| * The button shadow. |
| * @see #installDefaults() |
| */ |
| private Color buttonShadow; |
| |
| /** |
| * The button dark shadow. |
| * @see #installDefaults() |
| */ |
| private Color buttonDarkShadow; |
| |
| /** |
| * The button highlight. |
| * @see #installDefaults() |
| */ |
| private Color buttonHighlight; |
| |
| /* Size of the largest item in the comboBox |
| * This is package-private to avoid an accessor method. |
| */ |
| Dimension displaySize; |
| |
| // FIXME: This fields aren't used anywhere at this moment. |
| protected Dimension cachedMinimumSize; |
| protected CellRendererPane currentValuePane; |
| protected boolean isMinimumSizeDirty; |
| |
| /** |
| * Creates a new <code>BasicComboBoxUI</code> object. |
| */ |
| public BasicComboBoxUI() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * A factory method to create a UI delegate for the given |
| * {@link JComponent}, which should be a {@link JComboBox}. |
| * |
| * @param c The {@link JComponent} a UI is being created for. |
| * |
| * @return A UI delegate for the {@link JComponent}. |
| */ |
| public static ComponentUI createUI(JComponent c) |
| { |
| return new BasicComboBoxUI(); |
| } |
| |
| /** |
| * Installs the UI for the given {@link JComponent}. |
| * |
| * @param c the JComponent to install a UI for. |
| * |
| * @see #uninstallUI(JComponent) |
| */ |
| public void installUI(JComponent c) |
| { |
| super.installUI(c); |
| |
| if (c instanceof JComboBox) |
| { |
| comboBox = (JComboBox) c; |
| comboBox.setOpaque(true); |
| comboBox.setLayout(createLayoutManager()); |
| installDefaults(); |
| installComponents(); |
| installListeners(); |
| installKeyboardActions(); |
| } |
| } |
| |
| /** |
| * Uninstalls the UI for the given {@link JComponent}. |
| * |
| * @param c The JComponent that is having this UI removed. |
| * |
| * @see #installUI(JComponent) |
| */ |
| public void uninstallUI(JComponent c) |
| { |
| uninstallKeyboardActions(); |
| uninstallListeners(); |
| uninstallComponents(); |
| uninstallDefaults(); |
| comboBox = null; |
| } |
| |
| /** |
| * Installs the defaults that are defined in the {@link BasicLookAndFeel} |
| * for this {@link JComboBox}. |
| * |
| * @see #uninstallDefaults() |
| */ |
| protected void installDefaults() |
| { |
| LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background", |
| "ComboBox.foreground", "ComboBox.font"); |
| |
| // fetch the button color scheme |
| buttonBackground = UIManager.getColor("ComboBox.buttonBackground"); |
| buttonShadow = UIManager.getColor("ComboBox.buttonShadow"); |
| buttonDarkShadow = UIManager.getColor("ComboBox.buttonDarkShadow"); |
| buttonHighlight = UIManager.getColor("ComboBox.buttonHighlight"); |
| } |
| |
| /** |
| * Creates and installs the listeners for this UI. |
| * |
| * @see #uninstallListeners() |
| */ |
| protected void installListeners() |
| { |
| // install combo box's listeners |
| propertyChangeListener = createPropertyChangeListener(); |
| comboBox.addPropertyChangeListener(propertyChangeListener); |
| |
| focusListener = createFocusListener(); |
| comboBox.addFocusListener(focusListener); |
| listBox.addFocusListener(focusListener); |
| |
| itemListener = createItemListener(); |
| comboBox.addItemListener(itemListener); |
| |
| keyListener = createKeyListener(); |
| comboBox.addKeyListener(keyListener); |
| |
| mouseListener = createMouseListener(); |
| arrowButton.addMouseListener(mouseListener); |
| |
| // install listeners that listen to combo box model |
| listDataListener = createListDataListener(); |
| comboBox.getModel().addListDataListener(listDataListener); |
| } |
| |
| /** |
| * Uninstalls the defaults and sets any objects created during |
| * install to <code>null</code>. |
| * |
| * @see #installDefaults() |
| */ |
| protected void uninstallDefaults() |
| { |
| if (comboBox.getFont() instanceof UIResource) |
| comboBox.setFont(null); |
| |
| if (comboBox.getForeground() instanceof UIResource) |
| comboBox.setForeground(null); |
| |
| if (comboBox.getBackground() instanceof UIResource) |
| comboBox.setBackground(null); |
| |
| buttonBackground = null; |
| buttonShadow = null; |
| buttonDarkShadow = null; |
| buttonHighlight = null; |
| } |
| |
| /** |
| * Detaches all the listeners we attached in {@link #installListeners}. |
| * |
| * @see #installListeners() |
| */ |
| protected void uninstallListeners() |
| { |
| comboBox.removePropertyChangeListener(propertyChangeListener); |
| propertyChangeListener = null; |
| |
| comboBox.removeFocusListener(focusListener); |
| listBox.removeFocusListener(focusListener); |
| focusListener = null; |
| |
| comboBox.removeItemListener(itemListener); |
| itemListener = null; |
| |
| comboBox.removeKeyListener(keyListener); |
| keyListener = null; |
| |
| arrowButton.removeMouseListener(mouseListener); |
| mouseListener = null; |
| |
| comboBox.getModel().removeListDataListener(listDataListener); |
| listDataListener = null; |
| } |
| |
| /** |
| * Creates the popup that will contain list of combo box's items. |
| * |
| * @return popup containing list of combo box's items |
| */ |
| protected ComboPopup createPopup() |
| { |
| return new BasicComboPopup(comboBox); |
| } |
| |
| /** |
| * Creates a {@link KeyListener} to listen to key events. |
| * |
| * @return KeyListener that listens to key events. |
| */ |
| protected KeyListener createKeyListener() |
| { |
| return new KeyHandler(); |
| } |
| |
| /** |
| * Creates a {@link MouseListener} that will listen to mouse events occurring |
| * in the combo box. |
| * |
| * @return the MouseListener |
| */ |
| private MouseListener createMouseListener() |
| { |
| return new MouseHandler(); |
| } |
| |
| /** |
| * Creates the {@link FocusListener} that will listen to changes in this |
| * JComboBox's focus. |
| * |
| * @return the FocusListener. |
| */ |
| protected FocusListener createFocusListener() |
| { |
| return new FocusHandler(); |
| } |
| |
| /** |
| * Creates a {@link ListDataListener} to listen to the combo box's data model. |
| * |
| * @return The new listener. |
| */ |
| protected ListDataListener createListDataListener() |
| { |
| return new ListDataHandler(); |
| } |
| |
| /** |
| * Creates an {@link ItemListener} that will listen to the changes in |
| * the JComboBox's selection. |
| * |
| * @return The ItemListener |
| */ |
| protected ItemListener createItemListener() |
| { |
| return new ItemHandler(); |
| } |
| |
| /** |
| * Creates a {@link PropertyChangeListener} to listen to the changes in |
| * the JComboBox's bound properties. |
| * |
| * @return The PropertyChangeListener |
| */ |
| protected PropertyChangeListener createPropertyChangeListener() |
| { |
| return new PropertyChangeHandler(); |
| } |
| |
| /** |
| * Creates and returns a layout manager for the combo box. Subclasses can |
| * override this method to provide a different layout. |
| * |
| * @return a layout manager for the combo box. |
| */ |
| protected LayoutManager createLayoutManager() |
| { |
| return new ComboBoxLayoutManager(); |
| } |
| |
| /** |
| * Creates a component that will be responsible for rendering the |
| * selected component in the combo box. |
| * |
| * @return A renderer for the combo box. |
| */ |
| protected ListCellRenderer createRenderer() |
| { |
| return new BasicComboBoxRenderer(); |
| } |
| |
| /** |
| * Creates the component that will be responsible for displaying/editing |
| * the selected item in the combo box. This editor is used only when combo |
| * box is editable. |
| * |
| * @return A new component that will be responsible for displaying/editing |
| * the selected item in the combo box. |
| */ |
| protected ComboBoxEditor createEditor() |
| { |
| return new BasicComboBoxEditor.UIResource(); |
| } |
| |
| /** |
| * Installs the components for this JComboBox. ArrowButton, main |
| * part of combo box (upper part) and popup list of items are created and |
| * configured here. |
| */ |
| protected void installComponents() |
| { |
| // create drop down list of items |
| popup = createPopup(); |
| listBox = popup.getList(); |
| |
| // set editor and renderer for the combo box. Editor is used |
| // only if combo box becomes editable, otherwise renderer is used |
| // to paint the selected item; combobox is not editable by default. |
| comboBox.setRenderer(createRenderer()); |
| |
| // create and install arrow button |
| arrowButton = createArrowButton(); |
| configureArrowButton(); |
| comboBox.add(arrowButton); |
| |
| ComboBoxEditor currentEditor = comboBox.getEditor(); |
| if (currentEditor == null || currentEditor instanceof UIResource) |
| { |
| currentEditor = createEditor(); |
| comboBox.setEditor(currentEditor); |
| } |
| editor = currentEditor.getEditorComponent(); |
| |
| comboBox.revalidate(); |
| } |
| |
| /** |
| * Uninstalls components from this {@link JComboBox}. |
| * |
| * @see #installComponents() |
| */ |
| protected void uninstallComponents() |
| { |
| // uninstall arrow button |
| unconfigureArrowButton(); |
| comboBox.remove(arrowButton); |
| arrowButton = null; |
| |
| listBox = null; |
| popup = null; |
| |
| comboBox.setRenderer(null); |
| |
| // if the editor is not an instanceof UIResource, it was not set by the |
| // UI delegate, so don't clear it... |
| ComboBoxEditor currentEditor = comboBox.getEditor(); |
| if (currentEditor instanceof UIResource) |
| { |
| comboBox.setEditor(null); |
| editor = null; |
| } |
| } |
| |
| /** |
| * Adds the current editor to the combo box. |
| */ |
| public void addEditor() |
| { |
| comboBox.add(editor); |
| } |
| |
| /** |
| * Removes the current editor from the combo box. |
| */ |
| public void removeEditor() |
| { |
| comboBox.remove(editor); |
| } |
| |
| /** |
| * Configures the editor for this combo box. |
| */ |
| protected void configureEditor() |
| { |
| editor.setFont(comboBox.getFont()); |
| comboBox.getEditor().setItem(comboBox.getSelectedItem()); |
| // FIXME: Need to implement. Set font and add listeners. |
| } |
| |
| /** |
| * Unconfigures the editor for this combo nox. This method is not implemented. |
| */ |
| protected void unconfigureEditor() |
| { |
| // FIXME: Need to implement |
| } |
| |
| /** |
| * Configures the arrow button. |
| * |
| * @see #configureArrowButton() |
| */ |
| public void configureArrowButton() |
| { |
| arrowButton.setEnabled(comboBox.isEnabled()); |
| arrowButton.setFont(comboBox.getFont()); |
| } |
| |
| /** |
| * Unconfigures the arrow button. |
| * |
| * @see #configureArrowButton() |
| * |
| * @specnote The specification says this method is implementation specific |
| * and should not be used or overridden. |
| */ |
| public void unconfigureArrowButton() |
| { |
| // Nothing to do here yet. |
| } |
| |
| /** |
| * Creates an arrow button for this {@link JComboBox}. The arrow button is |
| * displayed at the right end of the combo box and is used to display/hide |
| * the drop down list of items. |
| * |
| * @return A new button. |
| */ |
| protected JButton createArrowButton() |
| { |
| return new BasicArrowButton(BasicArrowButton.SOUTH, buttonBackground, |
| buttonShadow, buttonDarkShadow, buttonHighlight); |
| } |
| |
| /** |
| * Returns <code>true</code> if the popup is visible, and <code>false</code> |
| * otherwise. |
| * |
| * @param c The JComboBox to check |
| * |
| * @return <code>true</code> if popup part of the JComboBox is visible and |
| * <code>false</code> otherwise. |
| */ |
| public boolean isPopupVisible(JComboBox c) |
| { |
| return popup.isVisible(); |
| } |
| |
| /** |
| * Displays/hides the {@link JComboBox}'s list of items on the screen. |
| * |
| * @param c The combo box, for which list of items should be |
| * displayed/hidden |
| * @param v true if show popup part of the jcomboBox and false to hide. |
| */ |
| public void setPopupVisible(JComboBox c, boolean v) |
| { |
| if (v) |
| { |
| popup.show(); |
| popup.getList().requestFocus(); |
| } |
| else |
| popup.hide(); |
| } |
| |
| /** |
| * JComboBox is focus traversable if it is editable and not otherwise. |
| * |
| * @param c combo box for which to check whether it is focus traversable |
| * |
| * @return true if focus tranversable and false otherwise |
| */ |
| public boolean isFocusTraversable(JComboBox c) |
| { |
| if (!comboBox.isEditable()) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * Paints given menu item using specified graphics context |
| * |
| * @param g The graphics context used to paint this combo box |
| * @param c comboBox which needs to be painted. |
| */ |
| public void paint(Graphics g, JComponent c) |
| { |
| Rectangle rect = rectangleForCurrentValue(); |
| paintCurrentValueBackground(g, rect, hasFocus); |
| paintCurrentValue(g, rect, hasFocus); |
| } |
| |
| /** |
| * Returns preferred size for the combo box. |
| * |
| * @param c comboBox for which to get preferred size |
| * |
| * @return The preferred size for the given combo box |
| */ |
| public Dimension getPreferredSize(JComponent c) |
| { |
| // note: overriding getMinimumSize() (for example in the MetalComboBoxUI |
| // class) affects the getPreferredSize() result, so it seems logical that |
| // this method is implemented by delegating to the getMinimumSize() method |
| return getMinimumSize(c); |
| } |
| |
| /** |
| * Returns the minimum size for this {@link JComboBox} for this |
| * look and feel. |
| * |
| * @param c The {@link JComponent} to find the minimum size for. |
| * |
| * @return The dimensions of the minimum size. |
| */ |
| public Dimension getMinimumSize(JComponent c) |
| { |
| Dimension d = getDisplaySize(); |
| int arrowButtonWidth = d.height; |
| Dimension result = new Dimension(d.width + arrowButtonWidth, d.height); |
| return result; |
| } |
| |
| /** The value returned by the getMaximumSize() method. */ |
| private static final Dimension MAXIMUM_SIZE = new Dimension(32767, 32767); |
| |
| /** |
| * Returns the maximum size for this {@link JComboBox} for this |
| * look and feel. |
| * |
| * @param c The {@link JComponent} to find the maximum size for |
| * |
| * @return The maximum size (<code>Dimension(32767, 32767)</code>). |
| */ |
| public Dimension getMaximumSize(JComponent c) |
| { |
| return MAXIMUM_SIZE; |
| } |
| |
| public int getAccessibleChildrenCount(JComponent c) |
| { |
| // FIXME: Need to implement |
| return 0; |
| } |
| |
| public Accessible getAccessibleChild(JComponent c, int i) |
| { |
| // FIXME: Need to implement |
| return null; |
| } |
| |
| /** |
| * Returns true if the specified key is a navigation key and false otherwise |
| * |
| * @param keyCode a key for which to check whether it is navigation key or |
| * not. |
| * |
| * @return true if the specified key is a navigation key and false otherwis |
| */ |
| protected boolean isNavigationKey(int keyCode) |
| { |
| return false; |
| } |
| |
| /** |
| * Selects next possible item relative to the current selection |
| * to be next selected item in the combo box. |
| */ |
| protected void selectNextPossibleValue() |
| { |
| int index = comboBox.getSelectedIndex(); |
| if (index != comboBox.getItemCount() - 1) |
| comboBox.setSelectedIndex(index + 1); |
| } |
| |
| /** |
| * Selects previous item relative to current selection to be |
| * next selected item. |
| */ |
| protected void selectPreviousPossibleValue() |
| { |
| int index = comboBox.getSelectedIndex(); |
| if (index != 0) |
| comboBox.setSelectedIndex(index - 1); |
| } |
| |
| /** |
| * Displays combo box popup if the popup is not currently shown |
| * on the screen and hides it if it is currently shown |
| */ |
| protected void toggleOpenClose() |
| { |
| setPopupVisible(comboBox, ! isPopupVisible(comboBox)); |
| } |
| |
| /** |
| * Returns the bounds in which comboBox's selected item will be |
| * displayed. |
| * |
| * @return rectangle bounds in which comboBox's selected Item will be |
| * displayed |
| */ |
| protected Rectangle rectangleForCurrentValue() |
| { |
| Rectangle cbBounds = SwingUtilities.getLocalBounds(comboBox); |
| Rectangle abBounds = arrowButton.getBounds(); |
| Rectangle rectForCurrentValue = new Rectangle(cbBounds.x, cbBounds.y, |
| cbBounds.width - abBounds.width, cbBounds.height); |
| return rectForCurrentValue; |
| } |
| |
| /** |
| * Returns the insets of the current border. |
| * |
| * @return Insets representing space between combo box and its border |
| */ |
| protected Insets getInsets() |
| { |
| return new Insets(0, 0, 0, 0); |
| } |
| |
| /** |
| * Paints currently selected value in the main part of the combo |
| * box (part without popup). |
| * |
| * @param g graphics context |
| * @param bounds Rectangle representing the size of the area in which |
| * selected item should be drawn |
| * @param hasFocus true if combo box has focus and false otherwise |
| */ |
| public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus) |
| { |
| if (! comboBox.isEditable()) |
| { |
| Object currentValue = comboBox.getSelectedItem(); |
| boolean isPressed = arrowButton.getModel().isPressed(); |
| |
| /* Gets the component to be drawn for the current value. |
| * If there is currently no selected item we will take an empty |
| * String as replacement. |
| */ |
| Component comp = comboBox.getRenderer().getListCellRendererComponent( |
| listBox, (currentValue != null ? currentValue : ""), -1, |
| isPressed, hasFocus); |
| if (! comboBox.isEnabled()) |
| { |
| comp.setBackground(UIManager.getColor( |
| "ComboBox.disabledBackground")); |
| comp.setForeground(UIManager.getColor( |
| "ComboBox.disabledForeground")); |
| comp.setEnabled(false); |
| } |
| comp.setBounds(0, 0, bounds.width, bounds.height); |
| comp.setFont(comboBox.getFont()); |
| comp.paint(g); |
| |
| comboBox.revalidate(); |
| } |
| else |
| comboBox.getEditor().setItem(comboBox.getSelectedItem()); |
| } |
| |
| /** |
| * Paints the background of part of the combo box, where currently |
| * selected value is displayed. If the combo box has focus this method |
| * should also paint focus rectangle around the combo box. |
| * |
| * @param g graphics context |
| * @param bounds Rectangle representing the size of the largest item in the |
| * comboBox |
| * @param hasFocus true if combo box has fox and false otherwise |
| */ |
| public void paintCurrentValueBackground(Graphics g, Rectangle bounds, |
| boolean hasFocus) |
| { |
| // background is painted by renderer, so it seems that nothing |
| // should be done here. |
| } |
| |
| /** |
| * Returns the default size for the display area of a combo box that does |
| * not contain any elements. This method returns the width and height of |
| * a single space in the current font, plus a margin of 1 pixel. |
| * |
| * @return The default display size. |
| * |
| * @see #getDisplaySize() |
| */ |
| protected Dimension getDefaultSize() |
| { |
| // There is nothing in the spec to say how this method should be |
| // implemented...so I've done some guessing, written some Mauve tests, |
| // and written something that gives dimensions that are close to the |
| // reference implementation. |
| FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); |
| int w = fm.charWidth(' ') + 2; |
| int h = fm.getHeight() + 2; |
| return new Dimension(w, h); |
| } |
| |
| /** |
| * Returns the size of the display area for the combo box. This size will be |
| * the size of the combo box, not including the arrowButton. |
| * |
| * @return The size of the display area for the combo box. |
| */ |
| protected Dimension getDisplaySize() |
| { |
| if (!comboBox.isEditable()) |
| { |
| Object prototype = comboBox.getPrototypeDisplayValue(); |
| if (prototype != null) |
| { |
| // calculate result based on prototype |
| ListCellRenderer renderer = comboBox.getRenderer(); |
| Component comp = renderer.getListCellRendererComponent(listBox, |
| prototype, -1, false, false); |
| Dimension compSize = comp.getPreferredSize(); |
| compSize.width += 2; // add 1 pixel margin around area |
| compSize.height += 2; |
| return compSize; |
| } |
| else |
| { |
| ComboBoxModel model = comboBox.getModel(); |
| int numItems = model.getSize(); |
| |
| // if combo box doesn't have any items then simply |
| // return its default size |
| if (numItems == 0) |
| { |
| displaySize = getDefaultSize(); |
| return displaySize; |
| } |
| |
| Dimension size = new Dimension(0, 0); |
| |
| // ComboBox's display size should be equal to the |
| // size of the largest item in the combo box. |
| ListCellRenderer renderer = comboBox.getRenderer(); |
| |
| for (int i = 0; i < numItems; i++) |
| { |
| Object item = model.getElementAt(i); |
| Component comp = renderer.getListCellRendererComponent(listBox, |
| item, -1, false, false); |
| |
| Dimension compSize = comp.getPreferredSize(); |
| if (compSize.width + 2 > size.width) |
| size.width = compSize.width + 2; |
| if (compSize.height + 2 > size.height) |
| size.height = compSize.height + 2; |
| } |
| displaySize = size; |
| return displaySize; |
| } |
| } |
| else // an editable combo, |
| { |
| Component comp = comboBox.getEditor().getEditorComponent(); |
| Dimension prefSize = comp.getPreferredSize(); |
| int width = prefSize.width; |
| int height = prefSize.height + 2; |
| Object prototype = comboBox.getPrototypeDisplayValue(); |
| if (prototype != null) |
| { |
| FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); |
| width = Math.max(width, fm.stringWidth(prototype.toString()) + 2); |
| } |
| displaySize = new Dimension(width, height); |
| return displaySize; |
| } |
| } |
| |
| /** |
| * Installs the keyboard actions for the {@link JComboBox} as specified |
| * by the look and feel. |
| */ |
| protected void installKeyboardActions() |
| { |
| // FIXME: Need to implement. |
| } |
| |
| /** |
| * Uninstalls the keyboard actions for the {@link JComboBox} there were |
| * installed by in {@link #installListeners}. |
| */ |
| protected void uninstallKeyboardActions() |
| { |
| // FIXME: Need to implement. |
| } |
| |
| /** |
| * A {@link LayoutManager} used to position the sub-components of the |
| * {@link JComboBox}. |
| * |
| * @see BasicComboBoxUI#createLayoutManager() |
| */ |
| public class ComboBoxLayoutManager implements LayoutManager |
| { |
| /** |
| * Creates a new ComboBoxLayoutManager object. |
| */ |
| public ComboBoxLayoutManager() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Adds a component to the layout. This method does nothing, since the |
| * layout manager doesn't need to track the components. |
| * |
| * @param name the name to associate the component with (ignored). |
| * @param comp the component (ignored). |
| */ |
| public void addLayoutComponent(String name, Component comp) |
| { |
| // Do nothing |
| } |
| |
| /** |
| * Removes a component from the layout. This method does nothing, since |
| * the layout manager doesn't need to track the components. |
| * |
| * @param comp the component. |
| */ |
| public void removeLayoutComponent(Component comp) |
| { |
| // Do nothing |
| } |
| |
| /** |
| * Returns preferred layout size of the JComboBox. |
| * |
| * @param parent the Container for which the preferred size should be |
| * calculated. |
| * |
| * @return The preferred size for the given container |
| */ |
| public Dimension preferredLayoutSize(Container parent) |
| { |
| return getPreferredSize((JComponent) parent); |
| } |
| |
| /** |
| * Returns the minimum layout size. |
| * |
| * @param parent the container. |
| * |
| * @return The minimum size. |
| */ |
| public Dimension minimumLayoutSize(Container parent) |
| { |
| return preferredLayoutSize(parent); |
| } |
| |
| /** |
| * Arranges the components in the container. It puts arrow |
| * button right end part of the comboBox. If the comboBox is editable |
| * then editor is placed to the left of arrow button, starting from the |
| * beginning. |
| * |
| * @param parent Container that should be layed out. |
| */ |
| public void layoutContainer(Container parent) |
| { |
| // Position editor component to the left of arrow button if combo box is |
| // editable |
| int arrowSize = comboBox.getHeight(); |
| int editorWidth = comboBox.getBounds().width - arrowSize; |
| |
| if (comboBox.isEditable()) |
| editor.setBounds(0, 0, editorWidth, comboBox.getBounds().height); |
| |
| arrowButton.setBounds(editorWidth, 0, arrowSize, arrowSize); |
| comboBox.revalidate(); |
| } |
| } |
| |
| /** |
| * Handles focus changes occuring in the combo box. This class is |
| * responsible for repainting combo box whenever focus is gained or lost |
| * and also for hiding popup list of items whenever combo box loses its |
| * focus. |
| */ |
| public class FocusHandler extends Object implements FocusListener |
| { |
| /** |
| * Creates a new FocusHandler object. |
| */ |
| public FocusHandler() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Invoked when combo box gains focus. It repaints main |
| * part of combo box accordingly. |
| * |
| * @param e the FocusEvent |
| */ |
| public void focusGained(FocusEvent e) |
| { |
| hasFocus = true; |
| comboBox.repaint(); |
| } |
| |
| /** |
| * Invoked when the combo box loses focus. It repaints the main part |
| * of the combo box accordingly and hides the popup list of items. |
| * |
| * @param e the FocusEvent |
| */ |
| public void focusLost(FocusEvent e) |
| { |
| hasFocus = false; |
| setPopupVisible(comboBox, false); |
| comboBox.repaint(); |
| } |
| } |
| |
| /** |
| * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its |
| * selected item changes. |
| */ |
| public class ItemHandler extends Object implements ItemListener |
| { |
| /** |
| * Creates a new ItemHandler object. |
| */ |
| public ItemHandler() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Invoked when selected item becomes deselected or when |
| * new item becomes selected. |
| * |
| * @param e the ItemEvent representing item's state change. |
| */ |
| public void itemStateChanged(ItemEvent e) |
| { |
| if (e.getStateChange() == ItemEvent.SELECTED && comboBox.isEditable()) |
| comboBox.getEditor().setItem(e.getItem()); |
| comboBox.repaint(); |
| } |
| } |
| |
| /** |
| * KeyHandler handles key events occuring while JComboBox has focus. |
| */ |
| public class KeyHandler extends KeyAdapter |
| { |
| public KeyHandler() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Invoked whenever key is pressed while JComboBox is in focus. |
| */ |
| public void keyPressed(KeyEvent e) |
| { |
| // FIXME: This method calls JComboBox.selectWithKeyChar if the key that was |
| // pressed is not a navigation key. |
| } |
| } |
| |
| /** |
| * Handles the changes occurring in the JComboBox's data model. |
| */ |
| public class ListDataHandler extends Object implements ListDataListener |
| { |
| /** |
| * Creates a new ListDataHandler object. |
| */ |
| public ListDataHandler() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Invoked if the content's of JComboBox's data model are changed. |
| * |
| * @param e ListDataEvent describing the change. |
| */ |
| public void contentsChanged(ListDataEvent e) |
| { |
| // if the item is selected or deselected |
| } |
| |
| /** |
| * Invoked when items are added to the JComboBox's data model. |
| * |
| * @param e ListDataEvent describing the change. |
| */ |
| public void intervalAdded(ListDataEvent e) |
| { |
| ComboBoxModel model = comboBox.getModel(); |
| ListCellRenderer renderer = comboBox.getRenderer(); |
| |
| if (displaySize == null) |
| displaySize = getDisplaySize(); |
| if (displaySize.width < getDefaultSize().width) |
| displaySize.width = getDefaultSize().width; |
| if (displaySize.height < getDefaultSize().height) |
| displaySize.height = getDefaultSize().height; |
| |
| comboBox.repaint(); |
| } |
| |
| /** |
| * Invoked when items are removed from the JComboBox's |
| * data model. |
| * |
| * @param e ListDataEvent describing the change. |
| */ |
| public void intervalRemoved(ListDataEvent e) |
| { |
| // recalculate display size of the JComboBox. |
| displaySize = getDisplaySize(); |
| comboBox.repaint(); |
| } |
| } |
| |
| /** |
| * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}. |
| */ |
| public class PropertyChangeHandler extends Object |
| implements PropertyChangeListener |
| { |
| /** |
| * Creates a new instance. |
| */ |
| public PropertyChangeHandler() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Invoked whenever bound property of JComboBox changes. |
| * |
| * @param e the event. |
| */ |
| public void propertyChange(PropertyChangeEvent e) |
| { |
| if (e.getPropertyName().equals("enabled")) |
| { |
| arrowButton.setEnabled(comboBox.isEnabled()); |
| |
| if (comboBox.isEditable()) |
| comboBox.getEditor().getEditorComponent().setEnabled(comboBox |
| .isEnabled()); |
| } |
| else if (e.getPropertyName().equals("editable")) |
| { |
| if (comboBox.isEditable()) |
| { |
| configureEditor(); |
| addEditor(); |
| } |
| else |
| { |
| unconfigureEditor(); |
| removeEditor(); |
| } |
| |
| comboBox.revalidate(); |
| comboBox.repaint(); |
| } |
| else if (e.getPropertyName().equals("dataModel")) |
| { |
| // remove ListDataListener from old model and add it to new model |
| ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue(); |
| if (oldModel != null) |
| oldModel.removeListDataListener(listDataListener); |
| |
| if ((ComboBoxModel) e.getNewValue() != null) |
| comboBox.getModel().addListDataListener(listDataListener); |
| } |
| else if (e.getPropertyName().equals("font")) |
| { |
| Font font = (Font) e.getNewValue(); |
| editor.setFont(font); |
| listBox.setFont(font); |
| arrowButton.setFont(font); |
| comboBox.revalidate(); |
| comboBox.repaint(); |
| } |
| |
| // FIXME: Need to handle changes in other bound properties. |
| } |
| } |
| |
| /** |
| * A handler for mouse events occurring in the combo box. An instance of |
| * this class is returned by the <code>createMouseListener()</code> method. |
| */ |
| private class MouseHandler extends MouseAdapter |
| { |
| /** |
| * Invoked when mouse is pressed over the combo box. It toggles the |
| * visibility of the popup list. |
| * |
| * @param e the event |
| */ |
| public void mousePressed(MouseEvent e) |
| { |
| if (comboBox.isEnabled()) |
| toggleOpenClose(); |
| } |
| } |
| } |