ch.randelshofer.quaqua.QuaquaComboBoxUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Quaqua Show documentation
Show all versions of Quaqua Show documentation
A Mavenisation of the Quaqua Mac OSX Swing Look and Feel (Java library)
Quaqua Look and Feel (C) 2003-2010, Werner Randelshofer.
Mavenisation by Matt Gumbley, DevZendo.org - for problems with
Mavenisation, see Matt; for issues with Quaqua, see the Quaqua home page.
For full license details, see http://randelshofer.ch/quaqua/license.html
The newest version!
/*
* @(#)QuaquaComboBoxUI.java
*
* Copyright (c) 2004-2010 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package ch.randelshofer.quaqua;
import ch.randelshofer.quaqua.util.*;
import ch.randelshofer.quaqua.util.Debug;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.border.*;
import javax.swing.plaf.basic.*;
import java.beans.*;
/**
* Quaqua UI for JComboBox.
*
* @author Werner Randelshofer
* @version $Id: QuaquaComboBoxUI.java 361 2010-11-21 11:19:20Z wrandelshofer $
*/
public class QuaquaComboBoxUI extends BasicComboBoxUI implements VisuallyLayoutable {
//private HierarchyListener hierarchyListener;
//MetalComboBoxUI
// Control the selection behavior of the JComboBox when it is used
// in the JTable DefaultCellEditor.
private boolean isTableCellEditor = false;
public static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
private final static Border tableCellEditorBorder = new EmptyBorder(0, 2, 0, 0);
static final StringBuffer HIDE_POPUP_KEY = new StringBuffer("HidePopupKey");
/**
* Preferred spacing between combo boxes and other components.
* /
* private final static Insets regularSpacing = new Insets(12,12,12,12);
* private final static Insets smallSpacing = new Insets(10,10,10,10);
* private final static Insets miniSpacing = new Insets(8,8,8,8);
*/
public static ComponentUI createUI(JComponent c) {
return new QuaquaComboBoxUI();
}
@Override
public void installUI(JComponent c) {
super.installUI(c);
// Is this combo box a cell editor?
Boolean value = (Boolean) c.getClientProperty(IS_TABLE_CELL_EDITOR);
if (value == null) {
value = (Boolean) c.getClientProperty("JComboBox.lightweightKeyboardNavigation");
}
setTableCellEditor(value != null && value.equals(Boolean.TRUE));
// Note: we need to invoke c.setOpaque explicitly, installProperty does
// not seem to work.
//LookAndFeel.installProperty(c, "opaque", UIManager.get("ComboBox.opaque"));
c.setOpaque(UIManager.getBoolean("ComboBox.opaque"));
comboBox.setRequestFocusEnabled(UIManager.getBoolean("ComboBox.requestFocusEnabled"));
// We can't set this property because it breaks the behavior of editable
// combo boxes.
comboBox.setFocusable(comboBox.isEditable() || UIManager.getBoolean("ComboBox.focusable"));
}
@Override
protected void installDefaults() {
super.installDefaults();
comboBox.setMaximumRowCount(UIManager.getInt("ComboBox.maximumRowCount"));
}
/**
* Create and install the listeners for the combo box and its model.
* This method is called when the UI is installed.
*/
@Override
protected void installListeners() {
if ((itemListener = createItemListener()) != null) {
comboBox.addItemListener(itemListener);
}
if ((propertyChangeListener = createPropertyChangeListener()) != null) {
comboBox.addPropertyChangeListener(propertyChangeListener);
}
if ((keyListener = createKeyListener()) != null) {
comboBox.addKeyListener(keyListener);
}
if ((focusListener = createFocusListener()) != null) {
comboBox.addFocusListener(focusListener);
}
if ((popupMouseListener = popup.getMouseListener()) != null) {
comboBox.addMouseListener(popupMouseListener);
}
if ((popupMouseMotionListener = popup.getMouseMotionListener()) != null) {
comboBox.addMouseMotionListener(popupMouseMotionListener);
}
if ((popupKeyListener = popup.getKeyListener()) != null) {
comboBox.addKeyListener(popupKeyListener);
}
/*
if ((hierarchyListener = createHierarchyListener()) != null) {
comboBox.addHierarchyListener(hierarchyListener);
}
*/
if (comboBox.getModel() != null) {
if ((listDataListener = createListDataListener()) != null) {
comboBox.getModel().addListDataListener(listDataListener);
}
}
}
/**
* Remove the installed listeners from the combo box and its model.
* The number and types of listeners removed and in this method should be
* the same that was added in installListeners
*/
@Override
protected void uninstallListeners() {
super.uninstallListeners();
/*
if (hierarchyListener != null) {
comboBox.removeHierarchyListener(hierarchyListener);
hierarchyListener = null;
}*/
}
public KeyListener getKeyListener() {
return keyListener;
}
/*
protected HierarchyListener createHierarchyListener() {
return new ComponentActivationHandler(comboBox);
}*/
boolean isTableCellEditor() {
return isTableCellEditor;
}
@Override
protected ComboBoxEditor createEditor() {
return new QuaquaComboBoxEditor.UIResource();
}
@Override
protected ComboPopup createPopup() {
QuaquaComboPopup p = new QuaquaComboPopup(comboBox, this);
p.getAccessibleContext().setAccessibleParent(comboBox);
return p;
}
@Override
protected JButton createArrowButton() {
JButton button = new QuaquaComboBoxButton(this, comboBox, getArrowIcon(),
comboBox.isEditable(),
currentValuePane,
listBox);
button.setMargin(new Insets(0, 1, 1, 3));
return button;
}
@Override
public PropertyChangeListener createPropertyChangeListener() {
return new QuaquaPropertyChangeListener();
}
private void setTableCellEditor(boolean b) {
isTableCellEditor = b;
updateTableCellEditor();
}
private void updateTableCellEditor() {
boolean b = isTableCellEditor();
//comboBox.setOpaque(b);
if (editor instanceof JComponent) {
JComponent jeditor = (JComponent) editor;
jeditor.setBorder(b ? tableCellEditorBorder : UIManager.getBorder("TextField.border"));
}
}
@Override
public void paint(Graphics g, JComponent c) {
if (editor != null
&& UIManager.getBoolean("ComboBox.changeEditorForeground")) {
editor.setForeground(c.getForeground());
}
Debug.paint(g, c, this);
}
@Override
public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus) {
}
/**
* Paints the background of the currently selected item.
*/
@Override
public void paintCurrentValueBackground(Graphics g, Rectangle bounds, boolean hasFocus) {
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of .
*/
public class QuaquaPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler {
@Override
public void propertyChange(PropertyChangeEvent e) {
super.propertyChange(e);
String name = e.getPropertyName();
if (name.equals("editable")) {
QuaquaComboBoxButton button = (QuaquaComboBoxButton) arrowButton;
button.setIconOnly(comboBox.isEditable());
updateTableCellEditor();
// FIXME - This may cause mayhem!
comboBox.setFocusable(comboBox.isEditable() || UIManager.getBoolean("ComboBox.focusable"));
comboBox.repaint();
} else if (name.equals("background")) {
Color color = (Color) e.getNewValue();
arrowButton.setBackground(color);
} else if (name.equals("foreground")) {
Color color = (Color) e.getNewValue();
arrowButton.setForeground(color);
listBox.setForeground(color);
} else if (name.equals(IS_TABLE_CELL_EDITOR)) {
Boolean inTable = (Boolean) e.getNewValue();
setTableCellEditor(inTable.equals(Boolean.TRUE) ? true : false);
} else if (name.equals("JComboBox.lightweightKeyboardNavigation")) {
// In Java 1.3 we have to use this property to guess whether we
// are a table cell editor or not.
setTableCellEditor(e.getNewValue() != null && e.getNewValue().equals("Lightweight"));
} else if (name.equals("JComponent.sizeVariant")) {
QuaquaUtilities.applySizeVariant(comboBox);
}
}
}
/**
* As of Java 2 platform v1.4 this method is no longer used. Do not call or
* override. All the functionality of this method is in the
* QuaquaPropertyChangeListener.
*
* @deprecated As of Java 2 platform v1.4.
*/
protected void editablePropertyChanged(PropertyChangeEvent e) {
}
@Override
protected LayoutManager createLayoutManager() {
return new QuaquaComboBoxLayoutManager();
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of .
*/
public class QuaquaComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager {
@Override
public void layoutContainer(Container parent) {
layoutComboBox(parent, this);
}
public void superLayout(Container parent) {
JComboBox cb = (JComboBox) parent;
int width = cb.getWidth();
int height = cb.getHeight();
Insets insets = getInsets();
int buttonSize = height - (insets.top + insets.bottom);
Rectangle cvb;
if (arrowButton != null) {
if (QuaquaUtilities.isLeftToRight(cb)) {
// FIXME - This should be 6 minus 2, whereas two needs to be
// derived from the TextFieldUI
//int plusHeight = (isSmallSizeVariant()) ? 4 : 4;
int plusHeight = (isSmall()) ? - 2 : - 2;
arrowButton.setBounds(
width - getArrowWidth() - insets.right,
insets.top /*+ margin.top - 3*/,
getArrowWidth(),
buttonSize /*- margin.top - margin.bottom*/ + plusHeight);
} else {
arrowButton.setBounds(insets.left, insets.top,
getArrowWidth(), buttonSize);
}
}
if (editor != null) {
cvb = rectangleForCurrentValue();
editor.setBounds(cvb);
}
}
}
// This is here because of a bug in the compiler.
// When a protected-inner-class-savvy compiler comes out we
// should move this into QuaquaComboBoxLayoutManager.
public void layoutComboBox(Container parent, QuaquaComboBoxLayoutManager manager) {
if (comboBox.isEditable()) {
manager.superLayout(parent);
} else {
if (arrowButton != null) {
Insets insets = comboBox.getInsets();
int width = comboBox.getWidth();
int height = comboBox.getHeight();
arrowButton.setBounds(insets.left, insets.top,
width - (insets.left + insets.right),
height - (insets.top + insets.bottom));
}
}
}
protected Icon getArrowIcon() {
if (isTableCellEditor()) {
return UIManager.getIcon("ComboBox.smallPopupIcon");
/* The following does not work as expected:
if (comboBox.isEditable()) {
return UIManager.getIcon("ComboBox.smallDropDownIcon");
} else {
return UIManager.getIcon("ComboBox.smallPopupIcon");
}*/
} else {
if (comboBox.isEditable()) {
if (isSmall()) {
return UIManager.getIcon("ComboBox.smallDropDownIcon");
} else {
return UIManager.getIcon("ComboBox.dropDownIcon");
}
} else {
if (isSmall()) {
return UIManager.getIcon("ComboBox.smallPopupIcon");
} else {
return UIManager.getIcon("ComboBox.popupIcon");
}
}
}
}
protected int getArrowWidth() {
if (isTableCellEditor()) {
return 7;
} else {
if (comboBox.isEditable()) {
if (isSmall()) {
return 17;
} else {
return 19;
}
} else {
if (isSmall()) {
return 17 + 3;
} else {
return 19 + 4;
}
}
}
}
/**
* As of Java 2 platform v1.4 this method is no
* longer used.
*
* @deprecated As of Java 2 platform v1.4.
*/
protected void removeListeners() {
if (propertyChangeListener != null) {
comboBox.removePropertyChangeListener(propertyChangeListener);
}
}
protected boolean isSmall() {
return QuaquaUtilities.isSmallSizeVariant(comboBox);
}
/**
* Returns the area that is reserved for drawing the currently selected item.
* Note: Changes in this method also require changes in method getMinimumSize.
*/
@Override
protected Rectangle rectangleForCurrentValue() {
return rectangleForCurrentValue(comboBox.getWidth(), comboBox.getHeight());
}
/**
* Returns the area that is reserved for drawing the currently selected item.
* Note: Changes in this method also require changes in method getMinimumSize.
*/
protected Rectangle rectangleForCurrentValue(int width, int height) {
Insets insets = getInsets();
Insets margin = getMargin();
if (comboBox.isEditable()) {
if (!isTableCellEditor()) {
insets.right -= margin.right;
/*
insets.left--;
insets.top--;
insets.bottom--;*/
insets.left -= margin.left - 2;
insets.top -= margin.top - 2;
insets.bottom -= margin.bottom - 2;
}
} else {
if (isTableCellEditor()) {
insets.top -= 1;
} else {
//insets.right += margin.right; no right-margin because we
// want no gap between button and renderer!
insets.left += 6;
insets.top += margin.top;
insets.left += margin.left;
insets.bottom += margin.bottom;
}
}
return new Rectangle(
insets.left,
insets.top,
width - getArrowWidth() - insets.right - insets.left,
height - insets.top - insets.bottom);
}
protected Insets getMargin() {
Insets margin = (Insets) comboBox.getClientProperty("Quaqua.Component.visualMargin");
if (margin == null) {
margin = UIManager.getInsets("Component.visualMargin");
}
return (margin==null)?new Insets(0,0,0,0):(Insets) margin.clone();
}
/**
* Note: Changes in this method also require changes in method rectangelForCurrentValue.
*/
@Override
public Dimension getMinimumSize(JComponent c) {
if (!isMinimumSizeDirty) {
return new Dimension(cachedMinimumSize);
}
Dimension size = null;
if (!comboBox.isEditable()
&& arrowButton != null
&& arrowButton instanceof QuaquaComboBoxButton) {
Insets buttonInsets = new Insets(4, 11, 3, getArrowWidth() + 5);
if (isSmall()) {
buttonInsets.bottom -= 1;
}
Insets insets = getInsets();
size = getDisplaySize();
size.width += insets.left + insets.right
+ buttonInsets.left + buttonInsets.right;
size.height += insets.top + insets.bottom
+ buttonInsets.top + buttonInsets.bottom;
} else if (comboBox.isEditable()
&& arrowButton != null
&& editor != null) {
Insets buttonInsets;
Insets insets = comboBox.getInsets();
Insets margin = getMargin();
buttonInsets = new Insets(2 - margin.top, 4 - margin.left, 2 - margin.bottom, getArrowWidth());
// Margin is included in display size, therefore no need to add
// it to size. We subtract the margin at the right, because we
// want the text field's focus ring to glow over the right button.
size = getDisplaySize();
size.width += insets.left + insets.right
+ buttonInsets.left + buttonInsets.right;
size.height += insets.top + insets.bottom
+ buttonInsets.top + buttonInsets.bottom;
} else {
size = super.getMinimumSize(c);
if (size == null) {
size = new Dimension(0, 0);
}
}
cachedMinimumSize.setSize(size.width, size.height);
isMinimumSizeDirty = false;
return new Dimension(cachedMinimumSize);
}
@Override
public Dimension getMaximumSize(JComponent c) {
Dimension size = getPreferredSize(c);
if (size!=null && !(c.getParent() instanceof JToolBar)) {
size.width = Short.MAX_VALUE;
}
return size;
}
/**
* Creates a FocusListener
which will be added to the combo box.
* If this method returns null then it will not be added to the combo box.
*
* @return an instance of a FocusListener
or null
*/
@Override
protected FocusListener createFocusListener() {
return new GlowFocusHandler();
}
@Override
public int getBaseline(JComponent c, int width, int height) {
Rectangle vb = getVisualBounds(c, VisuallyLayoutable.TEXT_BOUNDS, width, height);
return (vb == null) ? -1 : vb.y + vb.height;
}
public Rectangle getVisualBounds(JComponent c, int layoutType, int width, int height) {
Rectangle bounds = new Rectangle(0, 0, width, height);
if (layoutType == VisuallyLayoutable.CLIP_BOUNDS) {
return bounds;
}
JComboBox cb = (JComboBox) c;
Rectangle buttonRect = new Rectangle();
Rectangle editorRect = null;
Insets insets = getInsets();
Insets margin = getMargin();
int buttonSize = height - (insets.top + insets.bottom);
Rectangle cvb;
if (arrowButton != null) {
if (QuaquaUtilities.isLeftToRight(cb)) {
int plusHeight = (isSmall()) ? 5 : 4;
buttonRect.setBounds(
width - getArrowWidth() - insets.right,
insets.top + margin.top - 2,
getArrowWidth(),
buttonSize - margin.top - margin.bottom + plusHeight);
} else {
buttonRect.setBounds(insets.left, insets.top,
getArrowWidth(), buttonSize);
}
}
editorRect = rectangleForCurrentValue(width, height);
// FIXME we shouldn't hardcode this and determine the real visual
// bounds of the renderer instead.
// Subtract 2 from x because of the insets of the renderer
editorRect.x += 1;
editorRect.width -= 2;
switch (layoutType) {
case VisuallyLayoutable.COMPONENT_BOUNDS:
if (!isTableCellEditor()) {
if (editor != null) {
bounds.x += margin.left;
bounds.y += margin.top;
bounds.width -= margin.left + margin.right;
bounds.height -= margin.top + margin.bottom + 1;
} else {
bounds.x += margin.left;
bounds.y += margin.top;
bounds.width -= margin.left + margin.right;
bounds.height -= margin.top + margin.bottom;
}
}
break;
case VisuallyLayoutable.TEXT_BOUNDS:
Object renderer = (editor == null)
? (Object) cb.getRenderer().getListCellRendererComponent(listBox, cb.getSelectedItem(), cb.getSelectedIndex(), false, cb.hasFocus())
: (Object) editor;
if ((renderer instanceof JComponent)
&& (Methods.invokeGetter(renderer, "getUI", null) instanceof VisuallyLayoutable)) {
bounds = ((VisuallyLayoutable) Methods.invokeGetter(renderer, "getUI", null)).getVisualBounds((JComponent) renderer, layoutType, editorRect.width, editorRect.height);
bounds.x += editorRect.x;
bounds.y += editorRect.y;
} else {
bounds.setBounds(editorRect);
}
break;
}
return bounds;
}
/**
* This listener hides the popup when the focus is lost. It also repaints
* when focus is gained or lost.
*
* This public inner class should be treated as protected.
* Instantiate it only within subclasses of
* BasicComboBoxUI
.
*/
public class GlowFocusHandler extends BasicComboBoxUI.FocusHandler {
@Override
public void focusGained(FocusEvent e) {
super.focusGained(e);
glowyRepaint();
}
@Override
public void focusLost(FocusEvent e) {
super.focusLost(e);
glowyRepaint();
}
private void glowyRepaint() {
if (comboBox.getParent() != null) {
Rectangle r = comboBox.getBounds();
r.grow(2, 2);
comboBox.getParent().repaint(r.x, r.y, r.width, r.height);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy