1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.xnap.gui.component;
20
21 import java.awt.Dimension;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.FocusAdapter;
24 import java.awt.event.FocusEvent;
25 import java.util.Vector;
26 import javax.help.CSH;
27 import javax.swing.AbstractAction;
28 import javax.swing.Action;
29 import javax.swing.ComboBoxModel;
30 import javax.swing.JComboBox;
31 import javax.swing.JTextField;
32 import javax.swing.event.ListDataEvent;
33 import javax.swing.text.JTextComponent;
34 import org.apache.log4j.Logger;
35 import org.xnap.XNap;
36 import org.xnap.gui.action.ClearHistoriesAction;
37 import org.xnap.gui.action.Clearable;
38 import org.xnap.gui.event.PopupListener;
39 import org.xnap.gui.menu.TextFieldMenu;
40 import org.xnap.gui.util.IconHelper;
41 import org.xnap.util.Preferences;
42 import org.xnap.util.PreferencesProvider;
43
44
45 /***
46 * Provides a <code>JComboBox</code> with auto completion and a history.
47 */
48 public class HistoryComboBox extends JComboBox implements Clearable, Completable
49 {
50
51
52
53
54 private static Logger logger = Logger.getLogger(HistoryComboBox.class);
55
56 private boolean itemsOnly;
57
58 private JTextField jtf;
59
60 private TextFieldMenu menu;
61
62 /***
63 * Holds the current completion mode.
64 */
65 private CompletionMode mode = null;
66
67
68 /***
69 * Constructs a new HistoryComboBox with a history and specified width.
70 */
71 public HistoryComboBox(Vector history, int columns)
72 {
73 super(history);
74 initialize(columns);
75 }
76
77 /***
78 * Constructs a new HistoryComboBox that takes it's items from an existing
79 * ComboBoxModel.
80 */
81 public HistoryComboBox(ComboBoxModel model, int columns)
82 {
83 super(model);
84 initialize(columns);
85 }
86
87
88 /***
89 * Constructs a new HistoryComboBox with specified width.
90 */
91 public HistoryComboBox(int columns)
92 {
93 initialize(columns);
94 }
95
96 public Object[] getHistoryItems()
97 {
98 int count = getItemCount();
99 Object[] array = new Object[count];
100
101 for (int i = 0; (i < array.length) && (i < getItemCount()); i++) {
102 array[i] = getItemAt(i);
103 }
104 return array;
105 }
106
107 public boolean isItemSelected()
108 {
109 return isItem(jtf.getText());
110 }
111
112 public void setItemsOnly(boolean newValue)
113 {
114 this.itemsOnly = newValue;
115 }
116
117 public boolean getItemsOnly()
118 {
119 return itemsOnly;
120 }
121
122 public Dimension getMaximumSize()
123 {
124 return jtf.getMaximumSize();
125 }
126
127 public Dimension getMinimumSize()
128 {
129 return jtf.getMinimumSize();
130 }
131
132 public Dimension getPreferredSize()
133 {
134 return jtf.getPreferredSize();
135 }
136
137 /***
138 * Returns the selected object if autoComplete is true or editable is
139 * false.
140 * Otherwise you only get a string.
141 * @return the selected object (if autoComplete is true or editable is
142 * false).
143 */
144 public Object getSelectedItem()
145 {
146 ComboBoxModel boxModel = getModel();
147 Object item = boxModel.getSelectedItem();
148
149 if (item != null) {
150 if (item instanceof String) {
151 Object o = getItem((String)item);
152
153 if (o != null) {
154 return o;
155 }
156 else {
157 return jtf.getText();
158 }
159 }
160 else {
161 return item;
162 }
163 }
164 else {
165 return jtf.getText();
166 }
167 }
168
169 public void setText(String newValue)
170 {
171 jtf.setText(newValue);
172 }
173
174 public String getText()
175 {
176 return jtf.getText();
177 }
178
179 public JTextComponent getTextComponent()
180 {
181 return jtf;
182 }
183
184 public JTextField getTextField()
185 {
186 return jtf;
187 }
188
189 public void setCompletionModel(CompletionModel model)
190 {
191 mode.setModel(model);
192 }
193
194 public CompletionModel getCompletionModel()
195 {
196 return mode.getModel();
197 }
198
199 public CompletionMode getCompletionMode()
200 {
201 return mode;
202 }
203
204 public void setPreferences(String prefsKey, PreferencesProvider prefs)
205 {
206 menu.setPreferences(prefsKey, prefs);
207 }
208
209 public void setPreferences(String prefsKey)
210 {
211 setPreferences(prefsKey, Preferences.getInstance());
212 }
213
214 /***
215 * Overwritten to insert the new elements in the history into the
216 * completion model.
217 */
218 public void intervalAdded(ListDataEvent event)
219 {
220 super.intervalAdded(event);
221 for (int i = event.getIndex0(); i <= event.getIndex1(); i++) {
222 mode.getModel().insert(getModel().getElementAt(i));
223 }
224 }
225
226 private void setCompletionMode(CompletionMode newMode)
227 {
228 if (mode != null) {
229 if (mode.isEnabled()) {
230 mode.setEnabled(false);
231 newMode.setModel(mode.getModel());
232 mode = newMode;
233 mode.setEnabled(true);
234 }
235 else {
236 newMode.setModel(mode.getModel());
237 mode = newMode;
238 }
239 }
240 else {
241 mode = newMode;
242 mode.setEnabled(true);
243 }
244 }
245
246 public void setCompletionMode(String mode)
247 {
248 CompletionMode m = CompletionModeFactory.createCompletionMode(mode,
249 jtf, true);
250 setCompletionMode(m);
251 }
252
253 /***
254 * Adds item if not already in list.
255 */
256 public void addDistinctItemAtTop(Object item)
257 {
258 for (int i = getItemCount() - 1; i >= 0; i--) {
259 if (item.toString().equals(getItemAt(i).toString())) {
260 removeItemAt(i);
261 }
262 }
263 insertItemAt(item, 0);
264 setSelectedIndex(0);
265 }
266
267 /***
268 * Implements {@link Clearable} interface.
269 */
270 public void clear()
271 {
272 removeAllItems();
273 }
274
275 /***
276 * Returns the first object which corresponds to name.
277 * @param name The name of the object to return.
278 * @return the object whose name is name.
279 */
280 private Object getItem(String name)
281 {
282 ComboBoxModel boxModel = getModel();
283
284 for (int i = 0; i < boxModel.getSize(); i++) {
285
286 if (boxModel.getElementAt(i).equals(name)) {
287 return boxModel.getElementAt(i);
288 }
289 }
290
291 return null;
292 }
293
294 private boolean isItem(String name)
295 {
296 return getItem(name) != null;
297 }
298
299
300
301 private void initialize(int columns)
302 {
303 jtf = (JTextField)getEditor().getEditorComponent();
304
305 jtf.setColumns(columns);
306 jtf.addFocusListener(new FocusHandler());
307 setEditable(true);
308
309
310 CSH.setHelpIDString(this, "historycombobox");
311
312
313 menu = new TextFieldMenu(this, null);
314 menu.addSeparator();
315 menu.add(new XNapMenuItem(new ClearHistoryAction()));
316 jtf.addMouseListener(new PopupListener(menu));
317
318
319 ClearHistoriesAction.add(this);
320
321 for (int i = 0; i < getModel().getSize(); i++) {
322 mode.getModel().insert(getModel().getElementAt(i));
323 }
324 }
325
326 private boolean selectFirstMatchingItem(String string)
327 {
328 ComboBoxModel boxModel = getModel();
329 int matchingItemIndex = -1;
330 string = string.toLowerCase();
331
332 for (int i = 0; i < boxModel.getSize(); i++) {
333 if (boxModel.getElementAt(i).toString().toLowerCase().
334 startsWith(string)) {
335 matchingItemIndex = i;
336 break;
337 }
338 }
339
340
341 if (matchingItemIndex != -1) {
342
343
344 setSelectedItem(null);
345 setSelectedIndex(matchingItemIndex);
346
347 return true;
348 }
349 else {
350 return false;
351 }
352 }
353
354 /***
355 * Make sure a valid item is selected when focus is lost.
356 */
357 public class FocusHandler extends FocusAdapter
358 {
359 public void focusGained(FocusEvent e)
360 {
361
362
363 }
364
365 public void focusLost(FocusEvent e)
366 {
367 int caretPos = jtf.getCaretPosition();
368
369 if (getItemsOnly() && !isItemSelected()) {
370 HistoryComboBox.this.requestFocus();
371 selectFirstMatchingItem(jtf.getText());
372 jtf.setCaretPosition(jtf.getText().length());
373 jtf.moveCaretPosition(caretPos);
374 }
375 }
376 }
377
378 private class ClearHistoryAction extends AbstractAction
379 {
380 public ClearHistoryAction()
381 {
382 putValue(Action.NAME, XNap.tr("Clear History"));
383 putValue(IconHelper.XNAP_ICON, "eraser.png");
384 }
385
386 public void actionPerformed(ActionEvent event)
387 {
388 clear();
389 }
390 }
391 }