Frames | No Frames |
1: /* ======================================================================== 2: * JCommon : a free general purpose class library for the Java(tm) platform 3: * ======================================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jcommon/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------------ 28: * KeyedComboBoxModel.java 29: * ------------------ 30: * (C) Copyright 2004, by Thomas Morgner and Contributors. 31: * 32: * Original Author: Thomas Morgner; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * 35: * $Id: KeyedComboBoxModel.java,v 1.5 2005/10/18 13:18:34 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 07-Jun-2004 : Added JCommon header (DG); 40: * 41: */ 42: package org.jfree.ui; 43: 44: import java.util.ArrayList; 45: import javax.swing.ComboBoxModel; 46: import javax.swing.event.ListDataEvent; 47: import javax.swing.event.ListDataListener; 48: 49: /** 50: * The KeyedComboBox model allows to define an internal key (the data element) for every 51: * entry in the model. 52: * <p/> 53: * This class is usefull in all cases, where the public text differs from the internal 54: * view on the data. A separation between presentation data and processing data is a 55: * prequesite for localizing combobox entries. This model does not allow selected 56: * elements, which are not in the list of valid elements. 57: * 58: * @author Thomas Morgner 59: */ 60: public class KeyedComboBoxModel implements ComboBoxModel { 61: 62: /** 63: * The internal data carrier to map keys to values and vice versa. 64: */ 65: private static class ComboBoxItemPair { 66: /** The key. */ 67: private Object key; 68: /** The value for the key. */ 69: private Object value; 70: 71: /** 72: * Creates a new item pair for the given key and value. The value 73: * can be changed later, if needed. 74: * 75: * @param key the key 76: * @param value the value 77: */ 78: public ComboBoxItemPair(final Object key, final Object value) { 79: this.key = key; 80: this.value = value; 81: } 82: 83: /** 84: * Returns the key. 85: * @return the key. 86: */ 87: public Object getKey() { 88: return key; 89: } 90: 91: /** 92: * Returns the value. 93: * @return the value for this key. 94: */ 95: public Object getValue() { 96: return value; 97: } 98: 99: /** 100: * Redefines the value stored for that key. 101: * @param value the new value. 102: */ 103: public void setValue(final Object value) { 104: this.value = value; 105: } 106: } 107: 108: /** The index of the selected item. */ 109: private int selectedItem; 110: /** The data (contains ComboBoxItemPairs). */ 111: private ArrayList data; 112: /** The listeners. */ 113: private ArrayList listdatalistener; 114: /** The cached listeners as array. */ 115: private transient ListDataListener[] tempListeners; 116: 117: /** 118: * Creates a new keyed combobox model. 119: */ 120: public KeyedComboBoxModel() { 121: data = new ArrayList(); 122: listdatalistener = new ArrayList(); 123: } 124: 125: /** 126: * Creates a new keyed combobox model for the given keys and values. 127: * Keys and values must have the same number of items. 128: * 129: * @param keys the keys 130: * @param values the values 131: */ 132: public KeyedComboBoxModel(final Object[] keys, final Object[] values) { 133: this(); 134: setData(keys, values); 135: } 136: 137: /** 138: * Replaces the data in this combobox model. The number of keys must be equals to the 139: * number of values. 140: * 141: * @param keys the keys 142: * @param values the values 143: */ 144: public void setData(final Object[] keys, final Object[] values) { 145: if (values.length != keys.length) { 146: throw new IllegalArgumentException("Values and text must have the same length."); 147: } 148: 149: data.clear(); 150: data.ensureCapacity(keys.length); 151: 152: for (int i = 0; i < values.length; i++) { 153: add(keys[i], values[i]); 154: } 155: 156: selectedItem = -1; 157: final ListDataEvent evt = new ListDataEvent 158: (this, ListDataEvent.CONTENTS_CHANGED, 0, data.size() - 1); 159: fireListDataEvent(evt); 160: } 161: 162: /** 163: * Notifies all registered list data listener of the given event. 164: * 165: * @param evt the event. 166: */ 167: protected synchronized void fireListDataEvent(final ListDataEvent evt) { 168: if (tempListeners == null) { 169: tempListeners = (ListDataListener[]) listdatalistener.toArray 170: (new ListDataListener[listdatalistener.size()]); 171: } 172: for (int i = 0; i < tempListeners.length; i++) { 173: final ListDataListener l = tempListeners[i]; 174: l.contentsChanged(evt); 175: } 176: } 177: 178: /** 179: * Returns the selected item. 180: * 181: * @return The selected item or <code>null</code> if there is no selection 182: */ 183: public Object getSelectedItem() { 184: if (selectedItem >= data.size()) { 185: return null; 186: } 187: 188: if (selectedItem < 0) { 189: return null; 190: } 191: 192: final ComboBoxItemPair item = (ComboBoxItemPair) data.get(selectedItem); 193: return item.getValue(); 194: } 195: 196: /** 197: * Defines the selected key. If the object is not in the list of values, no item 198: * gets selected. 199: * 200: * @param anItem the new selected item. 201: */ 202: public void setSelectedKey(final Object anItem) { 203: if (anItem == null) { 204: selectedItem = -1; 205: } 206: else { 207: final int newSelectedItem = findDataElementIndex(anItem); 208: selectedItem = newSelectedItem; 209: } 210: fireListDataEvent(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1)); 211: } 212: 213: /** 214: * Set the selected item. The implementation of this method should notify all 215: * registered <code>ListDataListener</code>s that the contents have changed. 216: * 217: * @param anItem the list object to select or <code>null</code> to clear the selection 218: */ 219: public void setSelectedItem(final Object anItem) { 220: if (anItem == null) { 221: selectedItem = -1; 222: } 223: else { 224: final int newSelectedItem = findElementIndex(anItem); 225: selectedItem = newSelectedItem; 226: } 227: fireListDataEvent(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1)); 228: } 229: 230: /** 231: * Adds a listener to the list that's notified each time a change to the data model 232: * occurs. 233: * 234: * @param l the <code>ListDataListener</code> to be added 235: */ 236: public synchronized void addListDataListener(final ListDataListener l) { 237: listdatalistener.add(l); 238: tempListeners = null; 239: } 240: 241: /** 242: * Returns the value at the specified index. 243: * 244: * @param index the requested index 245: * @return the value at <code>index</code> 246: */ 247: public Object getElementAt(final int index) { 248: if (index >= data.size()) { 249: return null; 250: } 251: 252: final ComboBoxItemPair datacon = (ComboBoxItemPair) data.get(index); 253: if (datacon == null) { 254: return null; 255: } 256: return datacon.getValue(); 257: } 258: 259: /** 260: * Returns the key from the given index. 261: * 262: * @param index the index of the key. 263: * @return the the key at the specified index. 264: */ 265: public Object getKeyAt(final int index) { 266: if (index >= data.size()) { 267: return null; 268: } 269: 270: if (index < 0) { 271: return null; 272: } 273: 274: final ComboBoxItemPair datacon = (ComboBoxItemPair) data.get(index); 275: if (datacon == null) { 276: return null; 277: } 278: return datacon.getKey(); 279: } 280: 281: /** 282: * Returns the selected data element or null if none is set. 283: * 284: * @return the selected data element. 285: */ 286: public Object getSelectedKey() { 287: return getKeyAt(selectedItem); 288: } 289: 290: /** 291: * Returns the length of the list. 292: * 293: * @return the length of the list 294: */ 295: public int getSize() { 296: return data.size(); 297: } 298: 299: /** 300: * Removes a listener from the list that's notified each time a change to the data model 301: * occurs. 302: * 303: * @param l the <code>ListDataListener</code> to be removed 304: */ 305: public void removeListDataListener(final ListDataListener l) { 306: listdatalistener.remove(l); 307: tempListeners = null; 308: } 309: 310: /** 311: * Searches an element by its data value. This method is called by the setSelectedItem 312: * method and returns the first occurence of the element. 313: * 314: * @param anItem the item 315: * @return the index of the item or -1 if not found. 316: */ 317: private int findDataElementIndex(final Object anItem) { 318: if (anItem == null) { 319: throw new NullPointerException("Item to find must not be null"); 320: } 321: 322: for (int i = 0; i < data.size(); i++) { 323: final ComboBoxItemPair datacon = (ComboBoxItemPair) data.get(i); 324: if (anItem.equals(datacon.getKey())) { 325: return i; 326: } 327: } 328: return -1; 329: } 330: 331: /** 332: * Tries to find the index of element with the given key. The key must not be null. 333: * 334: * @param key the key for the element to be searched. 335: * @return the index of the key, or -1 if not found. 336: */ 337: public int findElementIndex(final Object key) { 338: if (key == null) { 339: throw new NullPointerException("Item to find must not be null"); 340: } 341: 342: for (int i = 0; i < data.size(); i++) { 343: final ComboBoxItemPair datacon = (ComboBoxItemPair) data.get(i); 344: if (key.equals(datacon.getValue())) { 345: return i; 346: } 347: } 348: return -1; 349: } 350: 351: /** 352: * Removes an entry from the model. 353: * 354: * @param key the key 355: */ 356: public void removeDataElement(final Object key) { 357: final int idx = findDataElementIndex(key); 358: if (idx == -1) { 359: return; 360: } 361: 362: data.remove(idx); 363: final ListDataEvent evt = new ListDataEvent 364: (this, ListDataEvent.INTERVAL_REMOVED, idx, idx); 365: fireListDataEvent(evt); 366: } 367: 368: /** 369: * Adds a new entry to the model. 370: * 371: * @param key the key 372: * @param cbitem the display value. 373: */ 374: public void add(final Object key, final Object cbitem) { 375: final ComboBoxItemPair con = new ComboBoxItemPair(key, cbitem); 376: data.add(con); 377: final ListDataEvent evt = new ListDataEvent 378: (this, ListDataEvent.INTERVAL_ADDED, data.size() - 2, data.size() - 2); 379: fireListDataEvent(evt); 380: } 381: 382: /** 383: * Removes all entries from the model. 384: */ 385: public void clear() { 386: final int size = getSize(); 387: data.clear(); 388: final ListDataEvent evt = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, size - 1); 389: fireListDataEvent(evt); 390: } 391: 392: }