001    /* DefaultComboBoxModel.java --
002       Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.swing;
039    
040    import java.io.Serializable;
041    import java.util.Arrays;
042    import java.util.Vector;
043    
044    import 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     */
057    public 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    }