//
// @(#)AssocJComboBox.java 9/2004
//
// Copyright 2004 Zachary DelProposto. All rights reserved.
// Use is subject to license terms.
//
//
// This program 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 of the License, or
// (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// Or from http://www.gnu.org/
//
package dip.gui.swing;
import dip.misc.Utils;
import java.util.Arrays;
import java.util.Comparator;
import java.text.Collator;
import javax.swing.JComboBox;
import javax.swing.ComboBoxModel;
/**
* This is an Associated JCombobox, which associates a set of objects with
* a set of labels. The labels can be localized. The get/set methods work with
* the object set (typically, constant values or the like).
* <p>
* See GeneralPreferencePanel for examples usage.
*
*
*/
public class AssocJComboBox extends JComboBox
{
/** Create an AssocJComboBox */
public AssocJComboBox()
{
super();
}// AssocJComboBox()
/** Create an AssocJComboBox with the given AssociatedObj(s) */
public AssocJComboBox(final AssociatedObj[] objs)
{
super(objs);
// longest is the prototype
int len = 0;
AssociatedObj longestAO = null;
for(int i=0; i<objs.length; i++)
{
if(objs[i].getDisplay().length() > len)
{
len = objs[i].getDisplay().length();
longestAO = objs[i];
}
}
setPrototypeDisplayValue(longestAO+"M");
}// AssocJComboBox()
/**
* Remove an item, by comparing its AssociatedObj <b>value</b>.
*/
public void removeItem(final Object obj)
{
super.removeItem( getAOForValue(obj) );
}// removeItem()
/** Remove an AssociatedObj */
public void removeItem(final AssociatedObj ao)
{
super.removeItem(ao);
}// removeItem()
/**
* Add an AssociatedObj. Other objects are not allowed to be
* added, and will throw an exception.
*/
public void addItem(final Object obj)
{
if(obj instanceof AssociatedObj)
{
super.addItem(obj);
}
else
{
throw new IllegalArgumentException("Not an AssociatedObj!");
}
}// addItem()
/** Set to the Default AssociatedObj, or index(0) if none. */
public void reset()
{
AssociatedObj ao = getDefaultAO();
if(ao == null)
{
setSelectedIndex(0);
}
else
{
setSelectedItem(ao);
}
}// reset()
/** Get the selected AssociatedObj; null if none selected. */
public Object getSelectedItem()
{
return (AssociatedObj) super.getSelectedItem();
}// getSelectedItem
/**
* Get the selected AssociatedObj value; if null, it uses the
* default. If there is no default, it uses the
* first value.
*/
public Object getSelectedValue()
{
AssociatedObj ao = (AssociatedObj) getSelectedItem();
if(ao == null)
{
ao = getDefaultAO();
if(ao == null)
{
assert (getItemAt(0) != null);
ao = (AssociatedObj) getItemAt(0);
}
}
return ao.getValue();
}// getSelectedValue()
/**
* Sets the selected item by its AssociatedObj <b>Value</b>;
*/
public void setSelectedItem(Object obj)
{
super.setSelectedItem( getAOForValue(obj) );
}// setSelectedItem()
/**
* Returns the Default AssociatedObj, if any; otherwise,
* returns null.
*/
private AssociatedObj getDefaultAO()
{
ComboBoxModel model = getModel();
final int len = model.getSize();
for(int i=0; i<len; i++)
{
AssociatedObj ao = (AssociatedObj) model.getElementAt(i);
if(ao.isDefault())
{
return ao;
}
}
return null;
}// getDefaultAO()
/**
* Returns the AssociatedObj for a given value/display
* If none, returns null. If an AssociatedObj is passed in,
* that same AssociatedObj is returned.
*/
private AssociatedObj getAOForValue(Object value)
{
// short circuit
if(value instanceof AssociatedObj)
{
return (AssociatedObj) value;
}
else if(value != null)
{
ComboBoxModel model = getModel();
final int len = model.getSize();
for(int i=0; i<len; i++)
{
AssociatedObj ao = (AssociatedObj) model.getElementAt(i);
if(value.equals(ao.getValue()) ||
value.equals(ao.getDisplay()))
{
return ao;
}
}
}
return null;
}// getAOForValue()
/**
* Associates a given Value with a given String. This is essentially
* a name-value pair, and is useful for localizing internal program
* constants (Strings, Enums, ints, etc.) to a value suitable for UI display.
* <p>
* The Value object needs to have a constant toString() representation
* for persitence to work properly.
* <p>
* Note: this class has a natural ordering that is inconsistent with equals
*/
public static class AssociatedObj
{
private final String display;
private final Object obj;
private final boolean isDefault;
/** Create an AssociatedObj */
public AssociatedObj(Object obj, String display)
{
this(obj, display, false);
}// AssociatedObj()
/** Create an AssociatedObj */
public AssociatedObj(Object obj, String display, boolean isDefault)
{
if(obj == null || display == null)
{
throw new IllegalArgumentException("null obj or display value");
}
this.obj = obj;
this.display = display;
this.isDefault = isDefault;
}// AssociatedObj()
/** Returns if this is the Default value */
public boolean isDefault() { return isDefault; }
/** Returns the Display String associated with the Value */
public String getDisplay() { return display; }
/** Returns the Value */
public Object getValue() { return obj; }
/**
* Collates (sorts) but only if display values are Strings.
* Otherwise, nothing is done.
*/
public static void collate(AssociatedObj[] array, Collator c)
{
Arrays.sort(array, new AssocObjComparator(c));
}// collates()
/**
* Collate internal display strings
*/
private static class AssocObjComparator implements Comparator
{
private final Collator c;
public AssocObjComparator(Collator c)
{
this.c = c;
}// AssocObjComparator()
public int compare(Object o1, Object o2)
{
String display1 = ((AssociatedObj) o1).getDisplay();
String display2 = ((AssociatedObj) o2).getDisplay();
return c.compare(display1, display2);
}// compare()
public boolean equals(Object obj)
{
return c.equals(obj);
}// equals()
}// inner class AssocObjComparator
/** Returns getDisplay(), so no JComboBox renderer chane is required. */
public String toString()
{
return getDisplay();
}// toString()
// NO array val can be null
// default can be null
// assumes TEXT is defined for key. (could be HTML?)
// MUST include trailing '.' on prefix if req'd
// objs must be toString()-able
public static AssociatedObj[] createAssociatedObjects(final Object[] objs,
final String prefix, Object defaultValue, boolean sort)
{
if(objs == null || prefix == null)
{
throw new IllegalArgumentException();
}
AssociatedObj[] assocObjs = new AssociatedObj[objs.length];
for(int i=0; i<assocObjs.length; i++)
{
boolean isDefault = objs[i].equals(defaultValue);
String text = Utils.getLocalString(prefix + objs[i].toString());
assocObjs[i] = new AssociatedObj(objs[i], text, isDefault);
}
if(sort)
{
Collator collator = Collator.getInstance();
AssociatedObj.collate(assocObjs, collator);
}
return assocObjs;
}// createAssociatedObjects()
// NO array val can be null
// default can be null
public static AssociatedObj[] createAssociatedObjects(final Object[] objs,
final String[] display, Object defaultValue, boolean sort)
{
if(objs == null)
{
throw new IllegalArgumentException();
}
if(objs.length != display.length)
{
throw new IllegalArgumentException();
}
AssociatedObj[] assocObjs = new AssociatedObj[objs.length];
for(int i=0; i<assocObjs.length; i++)
{
boolean isDefault = objs[i].equals(defaultValue);
assocObjs[i] = new AssociatedObj(objs[i], display[i], isDefault);
}
if(sort)
{
Collator collator = Collator.getInstance();
AssociatedObj.collate(assocObjs, collator);
}
return assocObjs;
}// createAssociatedObjects()
}// nested class AssociatedObj
}// class AssocJComboBox