diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java | |
download | cbb-gcc-4.6.4-upstream.tar.bz2 cbb-gcc-4.6.4-upstream.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java')
-rw-r--r-- | libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java | 1410 |
1 files changed, 1410 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java new file mode 100644 index 000000000..07d4f42cd --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -0,0 +1,1410 @@ +/* BasicComboBoxUI.java -- + Copyright (C) 2004, 2005, 2006, 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.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.MouseListener; +import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.swing.CellRendererPane; +import javax.swing.ComboBoxEditor; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.InputMap; +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; + + /** + * 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; + + /* Size of the largest item in the comboBox + * This is package-private to avoid an accessor method. + */ + Dimension displaySize = new Dimension(); + + /** + * Used to render the combo box values. + */ + protected CellRendererPane currentValuePane; + + /** + * The current minimum size if isMinimumSizeDirty is false. + * Setup by getMinimumSize() and invalidated by the various listeners. + */ + protected Dimension cachedMinimumSize; + + /** + * Indicates whether or not the cachedMinimumSize field is valid or not. + */ + protected boolean isMinimumSizeDirty = true; + + /** + * Creates a new <code>BasicComboBoxUI</code> object. + */ + public BasicComboBoxUI() + { + currentValuePane = new CellRendererPane(); + cachedMinimumSize = new Dimension(); + } + + /** + * 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) + { + isMinimumSizeDirty = true; + comboBox = (JComboBox) c; + installDefaults(); + 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. + ListCellRenderer renderer = comboBox.getRenderer(); + if (renderer == null || renderer instanceof UIResource) + comboBox.setRenderer(createRenderer()); + + ComboBoxEditor currentEditor = comboBox.getEditor(); + if (currentEditor == null || currentEditor instanceof UIResource) + { + currentEditor = createEditor(); + comboBox.setEditor(currentEditor); + } + + installComponents(); + installListeners(); + comboBox.setLayout(createLayoutManager()); + comboBox.setFocusable(true); + installKeyboardActions(); + comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, + Boolean.TRUE); + } + } + + /** + * 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) + { + setPopupVisible(comboBox, false); + popup.uninstallingUI(); + uninstallKeyboardActions(); + comboBox.setLayout(null); + uninstallComponents(); + uninstallListeners(); + 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"); + LookAndFeel.installBorder(comboBox, "ComboBox.border"); + } + + /** + * 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); + + itemListener = createItemListener(); + comboBox.addItemListener(itemListener); + + keyListener = createKeyListener(); + comboBox.addKeyListener(keyListener); + + // install listeners that listen to combo box model + listDataListener = createListDataListener(); + comboBox.getModel().addListDataListener(listDataListener); + + // Install mouse and key listeners from the popup. + popupMouseListener = popup.getMouseListener(); + comboBox.addMouseListener(popupMouseListener); + + popupMouseMotionListener = popup.getMouseMotionListener(); + comboBox.addMouseMotionListener(popupMouseMotionListener); + + popupKeyListener = popup.getKeyListener(); + comboBox.addKeyListener(popupKeyListener); + } + + /** + * 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); + + LookAndFeel.uninstallBorder(comboBox); + } + + /** + * 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; + + comboBox.getModel().removeListDataListener(listDataListener); + listDataListener = null; + + if (popupMouseListener != null) + comboBox.removeMouseListener(popupMouseListener); + popupMouseListener = null; + + if (popupMouseMotionListener != null) + comboBox.removeMouseMotionListener(popupMouseMotionListener); + popupMouseMotionListener = null; + + if (popupKeyListener != null) + comboBox.removeKeyListener(popupKeyListener); + popupKeyListener = 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 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.UIResource(); + } + + /** + * 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 and install arrow button + arrowButton = createArrowButton(); + comboBox.add(arrowButton); + if (arrowButton != null) + configureArrowButton(); + + if (comboBox.isEditable()) + addEditor(); + + comboBox.add(currentValuePane); + } + + /** + * Uninstalls components from this {@link JComboBox}. + * + * @see #installComponents() + */ + protected void uninstallComponents() + { + // Unconfigure arrow button. + if (arrowButton != null) + { + unconfigureArrowButton(); + } + + // Unconfigure editor. + if (editor != null) + { + unconfigureEditor(); + } + + comboBox.removeAll(); + arrowButton = null; + } + + /** + * Adds the current editor to the combo box. + */ + public void addEditor() + { + removeEditor(); + editor = comboBox.getEditor().getEditorComponent(); + if (editor != null) + { + configureEditor(); + comboBox.add(editor); + } + } + + /** + * Removes the current editor from the combo box. + */ + public void removeEditor() + { + if (editor != null) + { + unconfigureEditor(); + comboBox.remove(editor); + } + } + + /** + * Configures the editor for this combo box. + */ + protected void configureEditor() + { + editor.setFont(comboBox.getFont()); + if (popupKeyListener != null) + editor.addKeyListener(popupKeyListener); + if (keyListener != null) + editor.addKeyListener(keyListener); + comboBox.configureEditor(comboBox.getEditor(), + comboBox.getSelectedItem()); + } + + /** + * Unconfigures the editor for this combo box. + */ + protected void unconfigureEditor() + { + if (popupKeyListener != null) + editor.removeKeyListener(popupKeyListener); + if (keyListener != null) + editor.removeKeyListener(keyListener); + } + + /** + * Configures the arrow button. + * + * @see #configureArrowButton() + */ + public void configureArrowButton() + { + if (arrowButton != null) + { + arrowButton.setEnabled(comboBox.isEnabled()); + arrowButton.setFocusable(false); + arrowButton.addMouseListener(popup.getMouseListener()); + arrowButton.addMouseMotionListener(popup.getMouseMotionListener()); + + // Mark the button as not closing the popup, we handle this ourselves. + arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, + Boolean.TRUE); + } + } + + /** + * 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() + { + if (arrowButton != null) + { + if (popupMouseListener != null) + arrowButton.removeMouseListener(popupMouseListener); + if (popupMouseMotionListener != null) + arrowButton.removeMouseMotionListener(popupMouseMotionListener); + } + } + + /** + * 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); + } + + /** + * 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(); + 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) + { + hasFocus = comboBox.hasFocus(); + if (! comboBox.isEditable()) + { + 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) + { + return getMinimumSize(c); + } + + /** + * Returns the minimum size for this {@link JComboBox} for this + * look and feel. Also makes sure cachedMinimimSize is setup correctly. + * + * @param c The {@link JComponent} to find the minimum size for. + * + * @return The dimensions of the minimum size. + */ + public Dimension getMinimumSize(JComponent c) + { + if (isMinimumSizeDirty) + { + Insets i = getInsets(); + Dimension d = getDisplaySize(); + d.width += i.left + i.right + d.height; + cachedMinimumSize = new Dimension(d.width, d.height + i.top + i.bottom); + isMinimumSizeDirty = false; + } + return new Dimension(cachedMinimumSize); + } + + /** + * 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 new Dimension(32767, 32767); + } + + /** + * Returns the number of accessible children of the combobox. + * + * @param c the component (combobox) to check, ignored + * + * @return the number of accessible children of the combobox + */ + public int getAccessibleChildrenCount(JComponent c) + { + int count = 1; + if (comboBox.isEditable()) + count = 2; + return count; + } + + /** + * Returns the accessible child with the specified index. + * + * @param c the component, this is ignored + * @param i the index of the accessible child to return + */ + public Accessible getAccessibleChild(JComponent c, int i) + { + Accessible child = null; + switch (i) + { + case 0: // The popup. + if (popup instanceof Accessible) + { + AccessibleContext ctx = ((Accessible) popup).getAccessibleContext(); + ctx.setAccessibleParent(comboBox); + child = (Accessible) popup; + } + break; + case 1: // The editor, if any. + if (comboBox.isEditable() && editor instanceof Accessible) + { + AccessibleContext ctx = + ((Accessible) editor).getAccessibleContext(); + ctx.setAccessibleParent(comboBox); + child = (Accessible) editor; + } + break; + } + return child; + } + + /** + * 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 keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN + || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT + || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE + || keyCode == KeyEvent.VK_TAB; + } + + /** + * 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() + { + int w = comboBox.getWidth(); + int h = comboBox.getHeight(); + Insets i = comboBox.getInsets(); + int arrowSize = h - (i.top + i.bottom); + if (arrowButton != null) + arrowSize = arrowButton.getWidth(); + return new Rectangle(i.left, i.top, w - (i.left + i.right + arrowSize), + h - (i.top + i.left)); + } + + /** + * Returns the insets of the current border. + * + * @return Insets representing space between combo box and its border + */ + protected Insets getInsets() + { + return comboBox.getInsets(); + } + + /** + * 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) + { + /* 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. + */ + ListCellRenderer renderer = comboBox.getRenderer(); + if (comboBox.getSelectedIndex() != -1) + { + Component comp; + if (hasFocus && ! isPopupVisible(comboBox)) + { + comp = renderer.getListCellRendererComponent(listBox, + comboBox.getSelectedItem(), -1, true, false); + } + else + { + comp = renderer.getListCellRendererComponent(listBox, + comboBox.getSelectedItem(), -1, false, false); + Color bg = UIManager.getColor("ComboBox.disabledForeground"); + comp.setBackground(bg); + } + comp.setFont(comboBox.getFont()); + if (hasFocus && ! isPopupVisible(comboBox)) + { + comp.setForeground(listBox.getSelectionForeground()); + comp.setBackground(listBox.getSelectionBackground()); + } + else if (comboBox.isEnabled()) + { + comp.setForeground(comboBox.getForeground()); + comp.setBackground(comboBox.getBackground()); + } + else + { + Color fg = UIManager.getColor("ComboBox.disabledForeground"); + comp.setForeground(fg); + Color bg = UIManager.getColor("ComboBox.disabledBackground"); + comp.setBackground(bg); + } + currentValuePane.paintComponent(g, comp, comboBox, bounds.x, bounds.y, + bounds.width, bounds.height); + } + } + + /** + * 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) + { + Color saved = g.getColor(); + if (comboBox.isEnabled()) + g.setColor(UIManager.getColor("UIManager.background")); + else + g.setColor(UIManager.getColor("UIManager.disabledBackground")); + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.setColor(saved); + } + + private static final ListCellRenderer DEFAULT_RENDERER + = new DefaultListCellRenderer(); + + /** + * 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() + { + Component comp = DEFAULT_RENDERER.getListCellRendererComponent(listBox, + " ", -1, false, false); + currentValuePane.add(comp); + comp.setFont(comboBox.getFont()); + Dimension d = comp.getPreferredSize(); + currentValuePane.remove(comp); + return d; + } + + /** + * 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() + { + Dimension dim = new Dimension(); + ListCellRenderer renderer = comboBox.getRenderer(); + if (renderer == null) + { + renderer = DEFAULT_RENDERER; + } + + Object prototype = comboBox.getPrototypeDisplayValue(); + if (prototype != null) + { + Component comp = renderer.getListCellRendererComponent(listBox, + prototype, -1, false, false); + currentValuePane.add(comp); + comp.setFont(comboBox.getFont()); + Dimension renderSize = comp.getPreferredSize(); + currentValuePane.remove(comp); + dim.height = renderSize.height; + dim.width = renderSize.width; + } + else + { + ComboBoxModel model = comboBox.getModel(); + int size = model.getSize(); + if (size > 0) + { + for (int i = 0; i < size; ++i) + { + Component comp = renderer.getListCellRendererComponent(listBox, + model.getElementAt(i), -1, false, false); + currentValuePane.add(comp); + comp.setFont(comboBox.getFont()); + Dimension renderSize = comp.getPreferredSize(); + currentValuePane.remove(comp); + dim.width = Math.max(dim.width, renderSize.width); + dim.height = Math.max(dim.height, renderSize.height); + } + } + else + { + dim = getDefaultSize(); + if (comboBox.isEditable()) + dim.width = 100; + } + } + if (comboBox.isEditable()) + { + Dimension editSize = editor.getPreferredSize(); + dim.width = Math.max(dim.width, editSize.width); + dim.height = Math.max(dim.height, editSize.height); + } + displaySize.setSize(dim.width, dim.height); + return dim; + } + + /** + * Installs the keyboard actions for the {@link JComboBox} as specified + * by the look and feel. + */ + protected void installKeyboardActions() + { + SwingUtilities.replaceUIInputMap(comboBox, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + (InputMap) UIManager.get("ComboBox.ancestorInputMap")); + // Install any action maps here. + } + + /** + * Uninstalls the keyboard actions for the {@link JComboBox} there were + * installed by in {@link #installListeners}. + */ + protected void uninstallKeyboardActions() + { + SwingUtilities.replaceUIInputMap(comboBox, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + // Uninstall any action maps here. + } + + /** + * 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 parent.getPreferredSize(); + } + + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum size. + */ + public Dimension minimumLayoutSize(Container parent) + { + return parent.getMinimumSize(); + } + + /** + * 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 + Insets i = getInsets(); + int arrowSize = comboBox.getHeight() - (i.top + i.bottom); + + if (arrowButton != null) + arrowButton.setBounds(comboBox.getWidth() - (i.right + arrowSize), + i.top, arrowSize, arrowSize); + if (editor != null) + editor.setBounds(rectangleForCurrentValue()); + } + } + + /** + * 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; + if (! e.isTemporary() && comboBox.isLightWeightPopupEnabled()) + 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) + { + ComboBoxModel model = comboBox.getModel(); + Object v = model.getSelectedItem(); + if (editor != null) + comboBox.configureEditor(comboBox.getEditor(), v); + 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) + { + if (comboBox.getModel().getSize() != 0 && comboBox.isEnabled()) + { + if (! isNavigationKey(e.getKeyCode())) + { + if (! comboBox.isEditable()) + if (comboBox.selectWithKeyChar(e.getKeyChar())) + e.consume(); + } + else + { + if (e.getKeyCode() == KeyEvent.VK_UP && comboBox.isPopupVisible()) + selectPreviousPossibleValue(); + else if (e.getKeyCode() == KeyEvent.VK_DOWN) + { + if (comboBox.isPopupVisible()) + selectNextPossibleValue(); + else + comboBox.showPopup(); + } + else if (e.getKeyCode() == KeyEvent.VK_ENTER + || e.getKeyCode() == KeyEvent.VK_ESCAPE) + popup.hide(); + } + } + } + } + + /** + * 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 (e.getIndex0() != -1 || e.getIndex1() != -1) + { + isMinimumSizeDirty = true; + comboBox.revalidate(); + } + if (editor != null) + comboBox.configureEditor(comboBox.getEditor(), + comboBox.getSelectedItem()); + comboBox.repaint(); + } + + /** + * Invoked when items are added to the JComboBox's data model. + * + * @param e ListDataEvent describing the change. + */ + public void intervalAdded(ListDataEvent e) + { + int start = e.getIndex0(); + int end = e.getIndex1(); + if (start == 0 && comboBox.getItemCount() - (end - start + 1) == 0) + contentsChanged(e); + else if (start != -1 || end != -1) + { + ListCellRenderer renderer = comboBox.getRenderer(); + ComboBoxModel model = comboBox.getModel(); + int w = displaySize.width; + int h = displaySize.height; + // TODO: Optimize using prototype here. + for (int i = start; i <= end; ++i) + { + Component comp = renderer.getListCellRendererComponent(listBox, + model.getElementAt(i), -1, false, false); + currentValuePane.add(comp); + comp.setFont(comboBox.getFont()); + Dimension dim = comp.getPreferredSize(); + w = Math.max(w, dim.width); + h = Math.max(h, dim.height); + currentValuePane.remove(comp); + } + if (displaySize.width < w || displaySize.height < h) + { + if (displaySize.width < w) + displaySize.width = w; + if (displaySize.height < h) + displaySize.height = h; + comboBox.revalidate(); + if (editor != null) + { + comboBox.configureEditor(comboBox.getEditor(), + comboBox.getSelectedItem()); + } + } + } + + } + + /** + * Invoked when items are removed from the JComboBox's + * data model. + * + * @param e ListDataEvent describing the change. + */ + public void intervalRemoved(ListDataEvent e) + { + contentsChanged(e); + } + } + + /** + * 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) + { + // Lets assume every change invalidates the minimumsize. + String propName = e.getPropertyName(); + if (propName.equals("enabled")) + { + boolean enabled = comboBox.isEnabled(); + if (editor != null) + editor.setEnabled(enabled); + if (arrowButton != null) + arrowButton.setEnabled(enabled); + + comboBox.repaint(); + } + else if (propName.equals("editor") && comboBox.isEditable()) + { + addEditor(); + comboBox.revalidate(); + } + else if (e.getPropertyName().equals("editable")) + { + if (comboBox.isEditable()) + { + addEditor(); + } + else + { + removeEditor(); + } + + comboBox.revalidate(); + } + else if (propName.equals("model")) + { + // remove ListDataListener from old model and add it to new model + ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue(); + if (oldModel != null && listDataListener != null) + oldModel.removeListDataListener(listDataListener); + + ComboBoxModel newModel = (ComboBoxModel) e.getNewValue(); + if (newModel != null && listDataListener != null) + comboBox.getModel().addListDataListener(listDataListener); + + if (editor != null) + { + comboBox.configureEditor(comboBox.getEditor(), + comboBox.getSelectedItem()); + } + isMinimumSizeDirty = true; + comboBox.revalidate(); + comboBox.repaint(); + } + else if (propName.equals("font")) + { + Font font = (Font) e.getNewValue(); + if (editor != null) + { + editor.setFont(font); + } + listBox.setFont(font); + isMinimumSizeDirty = true; + comboBox.revalidate(); + } + else if (propName.equals("prototypeDisplayValue")) + { + isMinimumSizeDirty = true; + comboBox.revalidate(); + } + else if (propName.equals("renderer")) + { + isMinimumSizeDirty = true; + comboBox.revalidate(); + } + // FIXME: Need to handle changes in other bound properties. + } + } +} |