mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-13 03:29:50 +00:00
1411 lines
40 KiB
Java
1411 lines
40 KiB
Java
/* 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.
|
|
}
|
|
}
|
|
}
|