Mega Code Archive

 
Categories / Java / Swing Components
 

The KeyedComboBox model allows to define an internal key (the data element) for every entry in the model

/*   * JCommon : a free general purpose class library for the Java(tm) platform  *   *  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.  *  * Project Info:  http://www.jfree.org/jcommon/index.html  *  * This library is free software; you can redistribute it and/or modify it  * under the terms of the GNU Lesser General Public License as published by  * the Free Software Foundation; either version 2.1 of the License, or  * (at your option) any later version.  *  * This library 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 Lesser General Public  * License for more details.  *  * You should have received a copy of the GNU Lesser General Public  * License along with this library; if not, write to the Free Software  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,  * USA.  *  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.  * in the United States and other countries.]  *  * ------------------  * KeyedComboBoxModel.java  * ------------------  * (C) Copyright 2004, by Thomas Morgner and Contributors.  *  * Original Author:  Thomas Morgner;  * Contributor(s):   David Gilbert (for Object Refinery Limited);  *  * $Id: KeyedComboBoxModel.java,v 1.8 2008/09/10 09:26:11 mungady Exp $  *  * Changes  * -------  * 07-Jun-2004 : Added JCommon header (DG);  *  */ import java.util.ArrayList; import javax.swing.ComboBoxModel; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; /**  * The KeyedComboBox model allows to define an internal key (the data element)  * for every entry in the model. <p/> This class is usefull in all cases, where  * the public text differs from the internal view on the data. A separation  * between presentation data and processing data is a prequesite for localizing  * combobox entries. This model does not allow selected elements, which are not  * in the list of valid elements.  *   * @author Thomas Morgner  */ public class KeyedComboBoxModel implements ComboBoxModel {   /**    * The internal data carrier to map keys to values and vice versa.    */   private static class ComboBoxItemPair {     /**      * The key.      */     private Object key;     /**      * The value for the key.      */     private Object value;     /**      * Creates a new item pair for the given key and value. The value can be      * changed later, if needed.      *       * @param key      *          the key      * @param value      *          the value      */     public ComboBoxItemPair(final Object key, final Object value) {       this.key = key;       this.value = value;     }     /**      * Returns the key.      *       * @return the key.      */     public Object getKey() {       return this.key;     }     /**      * Returns the value.      *       * @return the value for this key.      */     public Object getValue() {       return this.value;     }     /**      * Redefines the value stored for that key.      *       * @param value      *          the new value.      */     public void setValue(final Object value) {       this.value = value;     }   }   /**    * The index of the selected item.    */   private int selectedItemIndex;   private Object selectedItemValue;   /**    * The data (contains ComboBoxItemPairs).    */   private ArrayList data;   /**    * The listeners.    */   private ArrayList listdatalistener;   /**    * The cached listeners as array.    */   private transient ListDataListener[] tempListeners;   private boolean allowOtherValue;   /**    * Creates a new keyed combobox model.    */   public KeyedComboBoxModel() {     this.data = new ArrayList();     this.listdatalistener = new ArrayList();   }   /**    * Creates a new keyed combobox model for the given keys and values. Keys and    * values must have the same number of items.    *     * @param keys    *          the keys    * @param values    *          the values    */   public KeyedComboBoxModel(final Object[] keys, final Object[] values) {     this();     setData(keys, values);   }   /**    * Replaces the data in this combobox model. The number of keys must be equals    * to the number of values.    *     * @param keys    *          the keys    * @param values    *          the values    */   public void setData(final Object[] keys, final Object[] values) {     if (values.length != keys.length) {       throw new IllegalArgumentException("Values and text must have the same length.");     }     this.data.clear();     this.data.ensureCapacity(keys.length);     for (int i = 0; i < values.length; i++) {       add(keys[i], values[i]);     }     this.selectedItemIndex = -1;     final ListDataEvent evt = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, this.data         .size() - 1);     fireListDataEvent(evt);   }   /**    * Notifies all registered list data listener of the given event.    *     * @param evt    *          the event.    */   protected synchronized void fireListDataEvent(final ListDataEvent evt) {     if (this.tempListeners == null) {       this.tempListeners = (ListDataListener[]) this.listdatalistener           .toArray(new ListDataListener[this.listdatalistener.size()]);     }     final ListDataListener[] listeners = this.tempListeners;     for (int i = 0; i < listeners.length; i++) {       final ListDataListener l = listeners[i];       l.contentsChanged(evt);     }   }   /**    * Returns the selected item.    *     * @return The selected item or <code>null</code> if there is no selection    */   public Object getSelectedItem() {     return this.selectedItemValue;   }   /**    * Defines the selected key. If the object is not in the list of values, no    * item gets selected.    *     * @param anItem    *          the new selected item.    */   public void setSelectedKey(final Object anItem) {     if (anItem == null) {       this.selectedItemIndex = -1;       this.selectedItemValue = null;     } else {       final int newSelectedItem = findDataElementIndex(anItem);       if (newSelectedItem == -1) {         this.selectedItemIndex = -1;         this.selectedItemValue = null;       } else {         this.selectedItemIndex = newSelectedItem;         this.selectedItemValue = getElementAt(this.selectedItemIndex);       }     }     fireListDataEvent(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1));   }   /**    * Set the selected item. The implementation of this method should notify all    * registered <code>ListDataListener</code>s that the contents have    * changed.    *     * @param anItem    *          the list object to select or <code>null</code> to clear the    *          selection    */   public void setSelectedItem(final Object anItem) {     if (anItem == null) {       this.selectedItemIndex = -1;       this.selectedItemValue = null;     } else {       final int newSelectedItem = findElementIndex(anItem);       if (newSelectedItem == -1) {         if (isAllowOtherValue()) {           this.selectedItemIndex = -1;           this.selectedItemValue = anItem;         } else {           this.selectedItemIndex = -1;           this.selectedItemValue = null;         }       } else {         this.selectedItemIndex = newSelectedItem;         this.selectedItemValue = getElementAt(this.selectedItemIndex);       }     }     fireListDataEvent(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1));   }   private boolean isAllowOtherValue() {     return this.allowOtherValue;   }   /**    * @param allowOtherValue    */   public void setAllowOtherValue(final boolean allowOtherValue) {     this.allowOtherValue = allowOtherValue;   }   /**    * Adds a listener to the list that's notified each time a change to the data    * model occurs.    *     * @param l    *          the <code>ListDataListener</code> to be added    */   public synchronized void addListDataListener(final ListDataListener l) {     if (l == null) {       throw new NullPointerException();     }     this.listdatalistener.add(l);     this.tempListeners = null;   }   /**    * Returns the value at the specified index.    *     * @param index    *          the requested index    * @return the value at <code>index</code>    */   public Object getElementAt(final int index) {     if (index >= this.data.size()) {       return null;     }     final ComboBoxItemPair datacon = (ComboBoxItemPair) this.data.get(index);     if (datacon == null) {       return null;     }     return datacon.getValue();   }   /**    * Returns the key from the given index.    *     * @param index    *          the index of the key.    * @return the the key at the specified index.    */   public Object getKeyAt(final int index) {     if (index >= this.data.size()) {       return null;     }     if (index < 0) {       return null;     }     final ComboBoxItemPair datacon = (ComboBoxItemPair) this.data.get(index);     if (datacon == null) {       return null;     }     return datacon.getKey();   }   /**    * Returns the selected data element or null if none is set.    *     * @return the selected data element.    */   public Object getSelectedKey() {     return getKeyAt(this.selectedItemIndex);   }   /**    * Returns the length of the list.    *     * @return the length of the list    */   public int getSize() {     return this.data.size();   }   /**    * Removes a listener from the list that's notified each time a change to the    * data model occurs.    *     * @param l    *          the <code>ListDataListener</code> to be removed    */   public void removeListDataListener(final ListDataListener l) {     this.listdatalistener.remove(l);     this.tempListeners = null;   }   /**    * Searches an element by its data value. This method is called by the    * setSelectedItem method and returns the first occurence of the element.    *     * @param anItem    *          the item    * @return the index of the item or -1 if not found.    */   private int findDataElementIndex(final Object anItem) {     if (anItem == null) {       throw new NullPointerException("Item to find must not be null");     }     for (int i = 0; i < this.data.size(); i++) {       final ComboBoxItemPair datacon = (ComboBoxItemPair) this.data.get(i);       if (anItem.equals(datacon.getKey())) {         return i;       }     }     return -1;   }   /**    * Tries to find the index of element with the given key. The key must not be    * null.    *     * @param key    *          the key for the element to be searched.    * @return the index of the key, or -1 if not found.    */   public int findElementIndex(final Object key) {     if (key == null) {       throw new NullPointerException("Item to find must not be null");     }     for (int i = 0; i < this.data.size(); i++) {       final ComboBoxItemPair datacon = (ComboBoxItemPair) this.data.get(i);       if (key.equals(datacon.getValue())) {         return i;       }     }     return -1;   }   /**    * Removes an entry from the model.    *     * @param key    *          the key    */   public void removeDataElement(final Object key) {     final int idx = findDataElementIndex(key);     if (idx == -1) {       return;     }     this.data.remove(idx);     final ListDataEvent evt = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, idx, idx);     fireListDataEvent(evt);   }   /**    * Adds a new entry to the model.    *     * @param key    *          the key    * @param cbitem    *          the display value.    */   public void add(final Object key, final Object cbitem) {     final ComboBoxItemPair con = new ComboBoxItemPair(key, cbitem);     this.data.add(con);     final ListDataEvent evt = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, this.data         .size() - 2, this.data.size() - 2);     fireListDataEvent(evt);   }   /**    * Removes all entries from the model.    */   public void clear() {     final int size = getSize();     this.data.clear();     final ListDataEvent evt = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, size - 1);     fireListDataEvent(evt);   } }