View Javadoc

1   /***
2    * JDualComboBox.java
3    *
4    * This file is part of the creme library.
5    * The creme library intends to ease the development effort of large
6    * applications by providing easy to use support classes.
7    *
8    * Copyright (C) 2003 Denis Bregeon
9    *
10   * This library is free software; you can redistribute it and/or
11   * modify it under the terms of the GNU Lesser General Public
12   * License as published by the Free Software Foundation; either
13   * version 2.1 of the License, or (at your option) any later version.
14   *
15   * This library is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   * Lesser General Public License for more details.
19   *
20   * You should have received a copy of the GNU Lesser General Public
21   * License along with this library; if not, write to the Free Software
22   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   *
24   * contact information: dbregeon@sourceforge.net
25   */
26  package org.jcreme.swing;
27  
28  import java.awt.Component;
29  import java.util.Arrays;
30  import java.util.HashSet;
31  import java.util.Iterator;
32  import java.util.Vector;
33  
34  import javax.swing.DefaultListCellRenderer;
35  import javax.swing.JComboBox;
36  import javax.swing.JList;
37  import javax.swing.JSeparator;
38  import javax.swing.ListCellRenderer;
39  import javax.swing.MutableComboBoxModel;
40  import javax.swing.event.ListDataEvent;
41  import javax.swing.event.ListDataListener;
42  
43  /***
44   * This class enables to display a JComboBox which model is made of two
45   * different lists. This is intended to present a shorter list at the top of the
46   * choice list and a more comprehensive list at the bottom.
47   * 
48   * @author $Author: dbregeon $
49   * @version $Revision: 1.1 $
50   */
51  public class JDualComboBox extends JComboBox {
52      /***
53       * This is a place holder in the ComboBoxModel to mark the separation
54       * between the constituent lists.
55       */
56      public static final String SEPARATOR = "-";
57  
58      /***
59       * This class joins two separate lists of values into a MutableComboBoxModel
60       * implementation.
61       * 
62       * @author $Author: dbregeon $
63       * @version $Revision: 1.1 $
64       */
65      public class DualComboBoxModel implements MutableComboBoxModel {
66          /***
67           * The list of values in the top part of the list.
68           */
69          protected Vector preferredValues = new Vector();
70  
71          /***
72           * The list of values in the bottom part of the list.
73           */
74          protected Vector additionalValues = new Vector();
75  
76          /***
77           * A reminder of the current selection.
78           */
79          protected Object selectedItem = null;
80  
81          /***
82           * The listeners to this MutableComboBoxModel.
83           */
84          protected HashSet dataChangeListeners = new HashSet();
85  
86          /***
87           * Builds the DualComboBoxModel from Object arrays.
88           * 
89           * @param preferred
90           *            the values to be displayed at the top of the list.
91           * @param additional
92           *            the values to be displayed at the bottom of the list.
93           */
94          public DualComboBoxModel(Object[] preferred, Object[] additional) {
95              if (preferred != null) {
96                  this.preferredValues.addAll(Arrays.asList(preferred));
97              }
98              if (additional != null) {
99                  this.additionalValues.addAll(Arrays.asList(additional));
100             }
101         }
102 
103         /***
104          * Builds the DualComboBoxModel from Vectors.
105          * 
106          * @param preferred
107          *            the values to be displayed at the top of the list.
108          * @param additional
109          *            the values to be displayed at the bottom of the list.
110          */
111         public DualComboBoxModel(Vector preferred, Vector additional) {
112             if (preferred != null) {
113                 this.preferredValues.addAll(preferred);
114             }
115             if (additional != null) {
116                 this.additionalValues.addAll(additional);
117             }
118         }
119 
120         /***
121          * @see javax.swing.ComboBoxModel#getSelectedItem()
122          */
123         public Object getSelectedItem() {
124             return this.selectedItem;
125         }
126 
127         /***
128          * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
129          */
130         public void setSelectedItem(Object anItem) {
131             if ((this.preferredValues.contains(anItem))
132                     || (this.additionalValues.contains(anItem))) {
133                 this.selectedItem = anItem;
134             }
135         }
136 
137         /***
138          * @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener)
139          */
140         public void addListDataListener(ListDataListener l) {
141             if (l != null) {
142                 this.dataChangeListeners.add(l);
143             }
144         }
145 
146         /***
147          * @see javax.swing.ListModel#getElementAt(int)
148          */
149         public Object getElementAt(int index) {
150             Object result = null;
151             if ((index >= 0) && (index < getSize())) {
152                 if (index < this.preferredValues.size()) {
153                     result = this.preferredValues.elementAt(index);
154                 } else if (index > this.preferredValues.size()) {
155                     result = this.additionalValues.elementAt(index
156                             - this.preferredValues.size() - 1);
157                 } else {
158                     result = SEPARATOR;
159                 }
160             }
161             return result;
162         }
163 
164         /***
165          * @see javax.swing.ListModel#getSize()
166          */
167         public int getSize() {
168             return this.preferredValues.size() + this.additionalValues.size()
169                     + 1;
170         }
171 
172         /***
173          * @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener)
174          */
175         public void removeListDataListener(ListDataListener l) {
176             if (l != null) {
177                 this.dataChangeListeners.remove(l);
178             }
179         }
180 
181         /***
182          * @see javax.swing.MutableComboBoxModel#addElement(java.lang.Object)
183          */
184         public void addElement(Object obj) {
185             this.preferredValues.add(obj);
186             fireDataChangeEvent(ListDataEvent.INTERVAL_ADDED,
187                     this.preferredValues.size() - 1, this.preferredValues
188                             .size() - 1);
189         }
190 
191         /***
192          * @see javax.swing.MutableComboBoxModel#insertElementAt(java.lang.Object,
193          *      int)
194          */
195         public void insertElementAt(Object obj, int index) {
196             if (index < this.preferredValues.size()) {
197                 this.preferredValues.insertElementAt(obj, index);
198             } else if (index == this.preferredValues.size()) {
199                 this.preferredValues.add(obj);
200             } else if (index >= this.preferredValues.size() + 1) {
201                 this.additionalValues.insertElementAt(obj, index
202                         - this.preferredValues.size() - 1);
203             }
204             fireDataChangeEvent(ListDataEvent.INTERVAL_ADDED, index, index);
205         }
206 
207         /***
208          * @see javax.swing.MutableComboBoxModel#removeElement(java.lang.Object)
209          */
210         public void removeElement(Object obj) {
211             this.preferredValues.remove(obj);
212             this.additionalValues.remove(obj);
213             fireDataChangeEvent(ListDataEvent.CONTENTS_CHANGED, 0, getSize());
214         }
215 
216         /***
217          * @see javax.swing.MutableComboBoxModel#removeElementAt(int)
218          */
219         public void removeElementAt(int index) {
220             if (index < this.preferredValues.size()) {
221                 this.preferredValues.removeElementAt(index);
222             } else if (index >= this.preferredValues.size() + 1) {
223                 this.additionalValues.removeElementAt(index
224                         - this.preferredValues.size() - 1);
225             }
226             fireDataChangeEvent(ListDataEvent.INTERVAL_REMOVED, index, index);
227         }
228 
229         protected void fireDataChangeEvent(int type, int index0, int index1) {
230             ListDataEvent event = new ListDataEvent(this, type, index0, index1);
231             Iterator iter = this.dataChangeListeners.iterator();
232             while (iter.hasNext()) {
233                 ((ListDataListener) iter.next()).contentsChanged(event);
234             }
235         }
236 
237     }
238 
239     /***
240      * This ListCellRenderer enables to handle the separator in a
241      * DualComboBoxModel. It otherwise relies on another renderer (linked to the
242      * UI) to display the actual list values.
243      * 
244      * @author $Author: dbregeon $
245      * @version $Revision: 1.1 $
246      */
247     protected class CompositeRenderer extends DefaultListCellRenderer {
248         /***
249          * The default Renderer provided by the UI.
250          */
251         protected ListCellRenderer baseRenderer = null;
252 
253         /***
254          * The component that displays the separation between top and bottom
255          * parts of the list.
256          */
257         protected JSeparator separator = new JSeparator();
258 
259         /***
260          * Builds a CompositeRenderer from a pre-existing renderer.
261          * 
262          * @param renderer
263          *            a UI linked renderer.
264          */
265         public CompositeRenderer(ListCellRenderer renderer) {
266             this.baseRenderer = renderer;
267         }
268 
269         /***
270          * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList,
271          *      java.lang.Object, int, boolean, boolean)
272          */
273         public Component getListCellRendererComponent(JList list, Object value,
274                 int index, boolean isSelected, boolean cellHasFocus) {
275             Component result = null;
276             if ((value == SEPARATOR) || (this.baseRenderer == null)) {
277                 result = this.separator;
278             } else {
279                 result = this.baseRenderer.getListCellRendererComponent(list,
280                         value, index, isSelected, cellHasFocus);
281             }
282             return result;
283         }
284 
285         /***
286          * Provides access to the renderer that is used to display list values.
287          * 
288          * @return the renderer that is used to display list values.
289          */
290         public ListCellRenderer getBaseRenderer() {
291             return this.baseRenderer;
292         }
293 
294     }
295 
296     /***
297      * Creates a JDualComboBox wih no data.
298      *  
299      */
300     public JDualComboBox() {
301         setRenderer(super.getRenderer());
302     }
303 
304     /***
305      * Creates a JDualComboBox wih Object arrays data.
306      * 
307      * @param preferredValues
308      *            the values to be displayed in the top part of the list.
309      * @param additionalValues
310      *            the values to be displayed in the bottom part of the list.
311      */
312     public JDualComboBox(Object[] preferredValues, Object[] additionalValues) {
313         setRenderer(super.getRenderer());
314         setModel(new DualComboBoxModel(preferredValues, additionalValues));
315     }
316 
317     /***
318      * Creates a JDualComboBox wih Vector arrays data.
319      * 
320      * @param preferredValues
321      *            the values to be displayed in the top part of the list.
322      * @param additionalValues
323      *            the values to be displayed in the bottom part of the list.
324      */
325     public JDualComboBox(Vector preferredValues, Vector additionalValues) {
326         setRenderer(super.getRenderer());
327         setModel(new DualComboBoxModel(preferredValues, additionalValues));
328     }
329 
330     /***
331      * @see javax.swing.JComboBox#setRenderer(javax.swing.ListCellRenderer)
332      */
333     public void setRenderer(ListCellRenderer aRenderer) {
334         super.setRenderer(new CompositeRenderer(aRenderer));
335     }
336 
337     /***
338      * @see javax.swing.JComboBox#getRenderer()
339      */
340     public ListCellRenderer getRenderer() {
341         ListCellRenderer result = super.getRenderer();
342         if (result instanceof CompositeRenderer) {
343             result = ((CompositeRenderer) super.getRenderer())
344                     .getBaseRenderer();
345         }
346         return result;
347     }
348 }