001/* DefaultComboBoxModel.java --
002   Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.swing;
039
040import java.io.Serializable;
041import java.util.Arrays;
042import java.util.Vector;
043
044import javax.swing.event.ListDataEvent;
045
046
047/**
048 * A model that stores a list of elements and a selected item (which may be
049 * <code>null</code>).  Changes to the model are signalled to listeners using
050 * {@link ListDataEvent}.  This model is designed for use by the
051 * {@link JComboBox} component.
052 *
053 * @author Andrew Selkirk
054 * @author Olga Rodimina
055 * @author Robert Schuster
056 */
057public class DefaultComboBoxModel extends AbstractListModel
058  implements MutableComboBoxModel, Serializable
059{
060  private static final long serialVersionUID = 6698657703676921904L;
061
062  /**
063   * Storage for the elements in the model's list.
064   */
065  private Vector list;
066
067  /**
068   * The selected item (<code>null</code> indicates no selection).
069   */
070  private Object selectedItem = null;
071
072  /**
073   * Creates a new model, initially empty.
074   */
075  public DefaultComboBoxModel()
076  {
077    list = new Vector();
078  }
079
080  /**
081   * Creates a new model and initializes its item list to the values in the
082   * given array.  The selected item is set to the first item in the array, or
083   * <code>null</code> if the array length is zero.
084   *
085   * @param items  an array containing items for the model (<code>null</code>
086   *               not permitted).
087   *
088   * @throws NullPointerException if <code>items</code> is <code>null</code>.
089   */
090  public DefaultComboBoxModel(Object[] items)
091  {
092    list = new Vector(Arrays.asList(items));
093    if (list.size() > 0)
094      selectedItem = list.get(0);
095  }
096
097  /**
098   * Creates a new model and initializes its item list to the values in the
099   * given vector.  The selected item is set to the first item in the vector,
100   * or <code>null</code> if the vector length is zero.
101   *
102   * @param vector  a vector containing items for the model (<code>null</code>
103   *                not permitted).
104   *
105   * @throws NullPointerException if <code>vector</code> is <code>null</code>.
106   */
107  public DefaultComboBoxModel(Vector<?> vector)
108  {
109    this.list = vector;
110    if (getSize() > 0)
111      selectedItem = vector.get(0);
112  }
113
114  /**
115   * Adds an element to the model's item list and sends a {@link ListDataEvent}
116   * to all registered listeners.  If the new element is the first item added
117   * to the list, and the selected item is <code>null</code>, the new element
118   * is set as the selected item.
119   *
120   * @param object item to add to the model's item list.
121   */
122  public void addElement(Object object)
123  {
124    list.addElement(object);
125    int index = list.size() - 1;
126    fireIntervalAdded(this, index, index);
127    if (list.size() == 1 && selectedItem == null)
128      setSelectedItem(object);
129  }
130
131  /**
132   * Removes the element at the specified index from the model's item list
133   * and sends a {@link ListDataEvent} to all registered listeners.  If the
134   * element removed was the selected item, then the preceding element becomes
135   * the new selected item (or the next element, if there is no preceding
136   * element).
137   *
138   * @param index  the index of the item to remove.
139   *
140   * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of
141   *         bounds.
142   */
143  public void removeElementAt(int index)
144  {
145    int selected = getIndexOf(selectedItem);
146    if (selected == index) // choose a new selected item
147      {
148        if (selected > 0)
149          setSelectedItem(getElementAt(selected - 1));
150        else
151          setSelectedItem(getElementAt(selected + 1));
152      }
153    list.removeElementAt(index);
154    fireIntervalRemoved(this, index, index);
155  }
156
157  /**
158   * Adds an element at the specified index in the model's item list
159   * and sends a {@link ListDataEvent} to all registered listeners.
160   *
161   * @param object element to insert
162   * @param index index specifing position in the list where given element
163   *        should be inserted.
164   *
165   * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of
166   *         bounds.
167   *
168   * @see #addElement(Object)
169   */
170  public void insertElementAt(Object object, int index)
171  {
172    list.insertElementAt(object, index);
173    fireIntervalAdded(this, index, index);
174  }
175
176  /**
177   * Removes an element from the model's item list and sends a
178   * {@link ListDataEvent} to all registered listeners.  If the item to be
179   * removed is the current selected item, a new selected item will be set.
180   * If the element is not found in the model's item list, this method does
181   * nothing.
182   *
183   * @param object  the element to remove.
184   */
185  public void removeElement(Object object)
186  {
187    int index = getIndexOf(object);
188    if (index != -1)
189      removeElementAt(index);
190  }
191
192  /**
193   * Removes all the items from the model's item list, resets and selected item
194   * to <code>null</code>, and sends a {@link ListDataEvent} to all registered
195   * listeners.
196   */
197  public void removeAllElements()
198  {
199    selectedItem = null;
200    int size = getSize();
201    if (size > 0)
202      {
203        list.clear();
204        fireIntervalRemoved(this, 0, size - 1);
205      }
206  }
207
208  /**
209   * Returns the number of items in the model's item list.
210   *
211   * @return The number of items in the model's item list.
212   */
213  public int getSize()
214  {
215    return list.size();
216  }
217
218  /**
219   * Sets the selected item for the model and sends a {@link ListDataEvent} to
220   * all registered listeners.  The start and end index of the event is set to
221   * -1 to indicate the model's selection has changed, and not its contents.
222   *
223   * @param object  the new selected item (<code>null</code> permitted).
224   */
225  public void setSelectedItem(Object object)
226  {
227    // No item is selected and object is null, so no change required.
228    if (selectedItem == null && object == null)
229      return;
230
231    // object is already selected so no change required.
232    if (selectedItem != null && selectedItem.equals(object))
233      return;
234
235    // Simply return if object is not in the list.
236    if (object != null && getIndexOf(object) == -1)
237      return;
238
239    // Here we know that object is either an item in the list or null.
240
241    // Handle the three change cases: selectedItem is null, object is
242    // non-null; selectedItem is non-null, object is null;
243    // selectedItem is non-null, object is non-null and they're not
244    // equal.
245    selectedItem = object;
246    fireContentsChanged(this, -1, -1);
247  }
248
249  /**
250   * Returns the selected item.
251   *
252   * @return The selected item (possibly <code>null</code>).
253   */
254  public Object getSelectedItem()
255  {
256    return selectedItem;
257  }
258
259  /**
260   * Returns the element at the specified index in the model's item list.
261   *
262   * @param index  the element index.
263   *
264   * @return The element at the specified index in the model's item list, or
265   *         <code>null</code> if the <code>index</code> is outside the bounds
266   *         of the list.
267   */
268  public Object getElementAt(int index)
269  {
270    if (index < 0 || index >= list.size())
271      return null;
272    return list.elementAt(index);
273  }
274
275  /**
276   * Returns the index of the specified element in the model's item list.
277   *
278   * @param object  the element.
279   *
280   * @return The index of the specified element in the model's item list.
281   */
282  public int getIndexOf(Object object)
283  {
284    return list.indexOf(object);
285  }
286}