package com.onaro.util.jfc; import java.awt.Component; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JSeparator; import javax.swing.ListCellRenderer; import com.onaro.commons.lang.CommonStringUtils; import com.onaro.util.enumeration.EnumPresentation; /** * Enables selection of one of the values of an enum or value in the table */ public class EnumJComboBox extends JComboBox { private static final long serialVersionUID = 1L; /** * Used as an indicator for painting a separator in the combobox. Selection of this value will be ignored */ private Object SEPARATER_VALUE = new Object(); private Object[] attributes; /** * the user should later call to {@link #setValues(List, Set, Object)} */ public EnumJComboBox() { //Set key selection manager so text with HTML markup (such as "Empty") can be easily selected. setKeySelectionManager(new HtmlAwareKeySelectionManager()); } // Note unchecked types must be used because Jide's AutoCompletionComboBox is not a generic type. public void setValues(List enumAttributes, Set nonEnumValuesInQuery, Object initialSelectionValue) { this.attributes = createObjectsList(enumAttributes,nonEnumValuesInQuery); setModel(new EnumComboBoxModel(SEPARATER_VALUE)); //if initialSelectionValue is provided, select the right entry in the combo box setSelectedValue(initialSelectionValue); final ListCellRenderer cellRenderer = getRenderer(); //set the renders to show the enum name and icon setRenderer(new EnumCellRenderer(cellRenderer, SEPARATER_VALUE)); } private Object[] createObjectsList(List enumAttributes, Set nonEnumValuesInQuery) { LinkedList list = new LinkedList(enumAttributes); if(nonEnumValuesInQuery != null && nonEnumValuesInQuery.size() > 0) { list.add(SEPARATER_VALUE); list.addAll(nonEnumValuesInQuery); } return list.toArray(new Object[list.size()]); } private void setSelectedValue(Object selectedEnum) { if (selectedEnum != null) { for (int i = 0; i < attributes.length; i++) { if (selectedEnum.equals(attributes[i])) { setSelectedIndex(i); return; } } } } public Object getSelectedValue() { return getSelectedItem(); } private class EnumComboBoxModel extends DefaultComboBoxModel { private static final long serialVersionUID = 1L; /** * This value will be ignored if selected. This is being used for values that are merely indicators for rendering of something like a separator */ private Object ignoreIfSelected; public EnumComboBoxModel(Object ignoreIfSelected) { super(EnumJComboBox.this.attributes); this.ignoreIfSelected = ignoreIfSelected; } public void setSelectedItem(Object selection) { if (!ignoreIfSelected.equals(selection)) { super.setSelectedItem(selection); } } } private static class EnumCellRenderer implements ListCellRenderer { private final ListCellRenderer renderer; /** * Used as an indicator for painting a separator in the combobox. Selection of this value will be ignored */ private Object separaterValue; private JSeparator separator; /** * * @param renderer the original renderer of the combo box. the call is delegated to it * @param separaterValue Used as an indicator for painting a separator in the combobox. Selection of this value will be ignored */ public EnumCellRenderer(ListCellRenderer renderer, Object separaterValue) { this.renderer = renderer; this.separaterValue = separaterValue; separator = new JSeparator(JSeparator.HORIZONTAL); } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (!isSeparator(value)) { boolean isEnumValue = value instanceof EnumPresentation.Attributes; Object valueToRender = isEnumValue ? ((EnumPresentation.Attributes) value).name : value; //call the super to arrange all the fonts, colors etc. renderer.getListCellRendererComponent(list, valueToRender, index, isSelected, cellHasFocus); if (isEnumValue) { //calling super.getListCellRendererComponent() nulls the icon since the value is a text //so we're setting the icon later ((JLabel) renderer).setIcon(((EnumPresentation.Attributes) value).smallIcon); } return (Component) renderer; }else { //the value is merely an indicator to the separator. so {@link JSeparator} is returned return separator; } } private boolean isSeparator(Object value) { return separaterValue.equals(value); } } /** * A {@link KeySelectionManager} that is the same as {@link JComboBox.DefaultKeySelectionManager} * except that it ignores HTML markup and SEPARATOR values. This allows the user to, i.e., select * "Empty" by typing the letter 'e'. */ private class HtmlAwareKeySelectionManager implements KeySelectionManager { @Override public int selectionForKey(char aKey,ComboBoxModel aModel) { int i,c; int currentSelection = -1; Object selectedItem = aModel.getSelectedItem(); String v; String pattern; if ( selectedItem != null ) { for ( i=0,c=aModel.getSize();i 0 && v.charAt(0) == aKey ) return i; } } for ( i = 0 ; i < currentSelection ; i ++ ) { Object elem = aModel.getElementAt(i); // Ignore separator value. if (SEPARATER_VALUE.equals(elem)) { continue; } if (elem != null && elem.toString() != null) { // Strip HTML from text before comparison. v = CommonStringUtils.getTextFromHtml(elem.toString()).toLowerCase(); if ( v.length() > 0 && v.charAt(0) == aKey ) return i; } } return -1; } } }