/*
* Copyright (c) 2008, 2015 , Oracle. All rights reserved.
*
* This software is the proprietary information of Oracle Corporation.
* Use is subject to license terms.
*/
package org.eclipse.persistence.tools.workbench.uitools.swing;
import java.awt.Component;
import java.awt.Dimension;
import javax.accessibility.AccessibleContext;
import javax.swing.ComboBoxEditor;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.plaf.basic.ComboPopup;
import org.eclipse.persistence.tools.workbench.uitools.cell.CellRendererAdapter;
/**
* This extension over the Swing <code>JComboBox</code> simply adds the ease of
* using a <code>CellRendererAdapter</code> since it is not recommended to use
* a sub-class of <code>DefaultListCellRenderer</code>. The installed UI
* delegate can customize the default implementation for better look on the
* operating system.
*
* @see Combo
* @see CellRendererAdapter
*
* @version 11.0.0
* @since 11.0.0
* @author Pascal Filion
*/
@SuppressWarnings("nls")
public class Combo extends JComboBox
{
/**
* Keep a reference to the <code>CellRendererAdapter</code> in order to
* re-install it if the look and feel changes.
*/
private CellRendererAdapter cellRendererAdapter;
/**
* An object that will be used as the prototype display value of this combo,
* which will speed up the calculation of its size.
*/
public static final Component PROTOTYPE_DISPLAY_VALUE = buildPrototypeDisplayValue();
/**
* Creates a new <code>Combo</code>.
*/
public Combo()
{
super();
}
/**
* Creates a new <code>Combo</code>.
*
* @param model The model containing the items and the selected item
*/
public Combo(ComboBoxModel model)
{
super(model);
}
/**
* Creates a simple renderer that will be used to calculate the default
* height of a cell. Since the only way items are rendered is by using the
* default renderer and formatting it with a text and icon. This will speed
* up the calculation of the combo's height and its popup size.
*
* @return The component used as the prototype display value
*/
private static Component buildPrototypeDisplayValue()
{
String item = "item";
JComboBox comboBox = new JComboBox();
comboBox.addItem(item);
ComboPopup comboPopup = (ComboPopup) comboBox.getUI().getAccessibleChild(comboBox, 0);
ListCellRenderer cellRenderer = comboBox.getRenderer();
JLabel label = (JLabel) cellRenderer.getListCellRendererComponent(comboPopup.getList(), item, 0, true, true);
label.setIcon(EmptyIcon.SMALL_ICON);
return label;
}
@SuppressWarnings("unchecked")
private ListCellRenderer buildCellRenderer(CellRendererAdapter cellRendererAdapter)
{
return new ComboCellRenderer
(
getRenderer(),
(CellRendererAdapter) cellRendererAdapter,
PROTOTYPE_DISPLAY_VALUE
);
}
/**
* {@inheritDoc}
*/
@Override
public void configureEditor(ComboBoxEditor editor, Object item)
{
// This should be done automatically by changing the text field's document
// and using a DocumentAdapter, this will help to update the underlying
// model right away
}
/**
* Fixes the size of this combo, which can be done when the model data is
* fixed. The UI delegate uses the cell renderer to calculate the width by
* using the widest item.
*/
public final void fixSize()
{
// Make sure there is no prototype display value to ensure the
Object oldPrototypeDisplayValue = getPrototypeDisplayValue();
setPrototypeDisplayValue(null);
// Add some extra space to the calculated width and
// make it is at least 100 pixels
Dimension size = getPreferredSize();
size.width = Math.max(size.width + 20, 100);
setPreferredSize(size);
setPrototypeDisplayValue(oldPrototypeDisplayValue);
}
/**
* {@inheritDoc}
*/
@Override
public AccessibleContext getAccessibleContext()
{
if (accessibleContext == null)
{
accessibleContext = new AccessibleCombo();
}
return accessibleContext;
}
/**
* Retrieves from the UI delegate the list used in the combo popup.
*
* @return The popup list
*/
final JList popupList()
{
ComboPopup comboPopup = (ComboPopup) getAccessibleContext().getAccessibleChild(0);
return comboPopup.getList();
}
/**
* {@inheritDoc}
*/
@Override
public void setEnabled(boolean enabled)
{
popupList().setEnabled(enabled);
super.setEnabled(enabled);
}
/**
* Installs a <code>ListCellRenderer</code> that will use the given
* <code>CellRendererAdapter</code> but still use the <code>ListCellRenderer</code>
* defined by the look and feel for retrieving the <code>Component</code>
* and formatting its properties (colors, font, etc).
*
* @param cellRendererAdapter The adapter used to format the items and show a
* representative description (text, icon, tooltip, accessible name)
*/
public final void setRendererAdapter(CellRendererAdapter cellRendererAdapter)
{
if (this.cellRendererAdapter != cellRendererAdapter)
{
this.cellRendererAdapter = cellRendererAdapter;
// Update the renderer in order to use the CellRendererAdapter
setPrototypeDisplayValue(PROTOTYPE_DISPLAY_VALUE);
setRenderer(buildCellRenderer(cellRendererAdapter));
// The CellRendererAdapter changed and the preferred size was set,
// then we update it
if (isPreferredSizeSet())
{
fixSize();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void updateUI()
{
super.updateUI();
// Re-set the ListCellRenderer in order to use the CellRendererAdapter
if (cellRendererAdapter != null)
{
setRenderer(buildCellRenderer(cellRendererAdapter));
}
}
/**
* The <code>AccessibleContext</code> of this combo.
*/
protected class AccessibleCombo extends AccessibleJComboBox
{
}
}