001package arcommon.ui.component;
002
003import java.awt.Dimension;
004import java.util.ArrayList;
005import java.util.Iterator;
006import java.util.List;
007
008import javax.swing.AbstractListModel;
009import javax.swing.JComponent;
010import javax.swing.JList;
011import javax.swing.ListModel;
012import javax.swing.ListSelectionModel;
013import javax.swing.event.ListDataEvent;
014import javax.swing.event.ListDataListener;
015import javax.swing.event.ListSelectionEvent;
016import javax.swing.event.ListSelectionListener;
017
018import arcommon.ArrayUtil;
019
020/**
021 * A {@link javax.swing.JList<String>} that is wrapped in a {@link javax.swing.JScrollPane}.
022 * <br>
023 * NOTE: The {@link #removeAll()} method has been overwritten to remove all items, which is NOT what the API says!!
024 */
025public class MyJList extends AbstractDelegatingJScrollPane {
026    
027    private ArrayList<ListSelectionListener> listSelectionListeners;
028    private ListSelectionListener myListSelectionListener;
029    private boolean updateInProgress;
030    private JList<String> jlist;
031    private ArrayList<String> items;
032    private AbstractListModel<String> listModel;
033    
034    public MyJList() {
035        jlist = new JList<String>();
036
037        initActionMap();
038
039        setViewportView(jlist);
040        
041        items = new ArrayList<String>();
042        
043        listModel = new AbstractListModel<String>() {
044            private static final long serialVersionUID = -8769269893116663354L;
045
046            public String getElementAt(int index) {
047                return items.get(index);
048            }
049
050            public int getSize() {
051                return items.size();
052            }
053        };
054        
055        jlist.setModel(listModel);
056        jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
057        
058        setPreferredSize(new Dimension(25, 25));
059    }
060    
061    public void addListSelectionListener(ListSelectionListener plistSelectionListener) {
062        if (myListSelectionListener == null) {
063            myListSelectionListener = new ListSelectionListener() {
064                public void valueChanged(ListSelectionEvent e) {
065                    eventListValueChanged(e);
066                }
067            };
068            jlist.addListSelectionListener(myListSelectionListener);
069        }
070        if (listSelectionListeners == null) {
071            listSelectionListeners = new ArrayList<ListSelectionListener>();
072        }
073        listSelectionListeners.add(plistSelectionListener);
074    }
075    
076    public void removeListSelectionListener(ListSelectionListener plistSelectionListener) {
077        if (listSelectionListeners == null) {
078            return ;
079        }
080        listSelectionListeners.remove(plistSelectionListener);
081        if (listSelectionListeners.size() == 0) {
082            listSelectionListeners = null;
083            jlist.removeListSelectionListener(myListSelectionListener);
084            listSelectionListeners = null;
085        }
086    }
087        
088    private void eventListValueChanged(ListSelectionEvent e) {
089        if (listSelectionListeners == null || updateInProgress || e.getValueIsAdjusting()) {
090            return ;
091        }
092        
093        Iterator<ListSelectionListener> i = listSelectionListeners.iterator();
094        while (i.hasNext()) {
095            ListSelectionListener tempListSelectionListener = i.next();
096            tempListSelectionListener.valueChanged(e);
097        }
098    }
099    
100    public void addAll(List<String> newItems) {
101        items.addAll(newItems);
102        update();
103    }
104    
105    public void add(String item) {
106        items.add(item);
107        update();
108    }
109    
110    public void add(int index, String item) {
111        items.add(index, item);
112        update();
113    }
114    
115    public void remove(String item) {
116        items.remove(item);
117        update();
118    }
119
120    public String removeItem(int index) {
121        String removed = items.remove(index);
122        update();
123        return removed;
124    }
125    
126    public ListModel<String> getModel() {
127        return listModel;
128    }
129    
130    private void update() {
131        ListDataEvent e = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, 0);
132        ListDataListener[] listListDataListeners = listModel.getListDataListeners();
133        for (int i = 0; i < listListDataListeners.length; i++) {
134            listListDataListeners[i].contentsChanged(e);
135        }
136    }
137    
138    public int getItemCount() {
139        return items.size();
140    }
141    
142    public String getItem(int index) {
143        return items.get(index);
144    }
145    
146    public int getSelectedIndex() {
147        int index = jlist.getSelectedIndex();
148        if (index >= getItemCount()) {
149            return -1;
150        }
151        return index;
152    }
153    
154    public int[] getSelectedIndices() {
155        return jlist.getSelectedIndices();
156    }
157    
158    public String getSelectedValue() {
159        if (getSelectedIndex() == -1) {
160            return null;
161        }
162        return jlist.getSelectedValue();
163    }
164
165    public List<String> getSelectedValuesList() {
166        return jlist.getSelectedValuesList();
167    }
168
169    public void setSelectedIndex(int index) {
170        updateInProgress = true;
171        if (index == -1) {
172            jlist.removeSelectionInterval(0, items.size() - 1);
173        } else {
174            jlist.setSelectedIndex(index);
175        }
176        updateInProgress = false;
177    }
178    
179    public void addSelectedIndex(int index) {
180        updateInProgress = true;
181        if (index == -1) {
182            throw new RuntimeException("index == -1");
183        }
184        jlist.addSelectionInterval(index, index);
185        updateInProgress = false;
186    }
187    
188    public void setSelectedIndices(int[] indices) {
189        updateInProgress = true;
190        jlist.clearSelection();
191        for (int index : indices) {
192            jlist.addSelectionInterval(index, index);
193        }
194        updateInProgress = false;
195    }
196    
197    public void ensureSelectedIsVisible() {
198        int[] selectedIndices = getSelectedIndices();
199        if (selectedIndices.length == 0) {
200            return ;
201        }
202        
203        int firstVisibleIndex = jlist.getFirstVisibleIndex();
204        int lastVisibleIndex = jlist.getLastVisibleIndex();
205        if (firstVisibleIndex == -1 || lastVisibleIndex == -1) {
206            return ;
207        }
208        
209        int minimum = ArrayUtil.min(selectedIndices);
210        int maximum = ArrayUtil.max(selectedIndices);
211        int selectedDiff = maximum - minimum;
212        int visibleDiff = lastVisibleIndex - firstVisibleIndex;
213        if (selectedDiff < visibleDiff) {
214            ensureIndexIsVisible(minimum - 1);
215            ensureIndexIsVisible(maximum + 1);
216            return ;
217        }
218        
219        for (int i = 0; i < selectedIndices.length; i++) {
220            if (selectedIndices[i] >= firstVisibleIndex && selectedIndices[i] <= lastVisibleIndex) {
221                return ;
222            }
223        }
224        
225        if (firstVisibleIndex > maximum) {
226            ensureIndexIsVisible(maximum - 1);
227            return ;
228        }
229        
230        if (lastVisibleIndex < minimum) {
231            ensureIndexIsVisible(minimum + 1);
232            return ;
233        }
234        
235        ensureIndexIsVisible(minimum - 1);
236    }
237    
238    public void ensureIndexIsVisible(int index) {
239        if (index < 0) {
240            index = 0;
241        }
242        if (index > items.size() - 1) {
243            index = items.size() - 1;
244        }
245        jlist.ensureIndexIsVisible(index);
246    }
247    
248    public void removeAll() {
249        items.clear();
250        update();
251    }
252    
253    public void setSelectMulti(boolean selectMulti) {
254        if (selectMulti) {
255            jlist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
256        } else {
257            jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
258        }
259    }
260    
261    @Override
262    protected JComponent getDelegatingJComponent() {
263        return jlist;
264    }
265    
266    private static final long serialVersionUID = -6971893970210035428L;
267}