Source code: jpicedt/widgets/HistoryTextField.java
1 /*
2 * HistoryTextField.java - Text field with a history
3 * Copyright (C) 1999, 2000, 2001 Slava Pestov
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package jpicedt.widgets;
21
22 import javax.swing.*;
23 import java.awt.*;
24 import java.awt.event.*;
25
26 /**
27 * Text field with an arrow-key accessable history.
28 * @author Slava Pestov
29 * @version $Id: HistoryTextField.java,v 1.1.1.1 2002/04/18 16:23:02 reynal Exp $
30 */
31 public class HistoryTextField extends JTextField
32 {
33 /**
34 * Creates a new history text field.
35 * @param name The history model name
36 */
37 public HistoryTextField(String name)
38 {
39 this(name,false,true);
40 }
41
42 /**
43 * Creates a new history text field.
44 * @param name The history model name
45 * @param instantPopup If true, selecting a value from the history
46 * popup will immediately fire an ActionEvent. If false, the user
47 * will have to press 'Enter' first
48 *
49 * @since jEdit 2.2pre5
50 */
51 public HistoryTextField(String name, boolean instantPopups)
52 {
53 this(name,instantPopups,true);
54 }
55
56 /**
57 * Creates a new history text field.
58 * @param name The history model name
59 * @param instantPopup If true, selecting a value from the history
60 * popup will immediately fire an ActionEvent. If false, the user
61 * will have to press 'Enter' first
62 * @param enterAddsToHistory If true, pressing the Enter key will
63 * automatically add the currently entered text to the history.
64 *
65 * @since jEdit 2.6pre5
66 */
67 public HistoryTextField(String name, boolean instantPopups,
68 boolean enterAddsToHistory)
69 {
70 if(name != null)
71 historyModel = HistoryModel.getModel(name);
72 addMouseListener(new MouseHandler());
73
74 this.instantPopups = instantPopups;
75 this.enterAddsToHistory = enterAddsToHistory;
76
77 index = -1;
78 }
79
80 /**
81 * Sets the history list model.
82 * @param name The model name
83 * @since jEdit 2.3pre3
84 */
85 public void setModel(String name)
86 {
87 if(name == null)
88 historyModel = null;
89 else
90 historyModel = HistoryModel.getModel(name);
91 index = -1;
92 }
93
94 /**
95 * Adds the currently entered item to the history.
96 */
97 public void addCurrentToHistory()
98 {
99 if(historyModel != null)
100 historyModel.addItem(getText());
101 index = 0;
102 }
103
104 /**
105 * Sets the displayed text.
106 */
107 public void setText(String text)
108 {
109 super.setText(text);
110 index = -1;
111 }
112
113 /**
114 * Returns the underlying history model.
115 */
116 public HistoryModel getModel()
117 {
118 return historyModel;
119 }
120
121 /**
122 * Fires an action event to all listeners. This is public so
123 * that inner classes can access it.
124 */
125 public void fireActionPerformed()
126 {
127 super.fireActionPerformed();
128 }
129
130 // protected members
131 protected void processKeyEvent(KeyEvent evt)
132 {
133 evt = KeyEventWorkaround.processKeyEvent(evt);
134 if(evt == null)
135 return;
136
137 if(evt.getID() == KeyEvent.KEY_PRESSED)
138 {
139 if(evt.getKeyCode() == KeyEvent.VK_ENTER)
140 {
141 if(enterAddsToHistory)
142 addCurrentToHistory();
143
144 if(evt.getModifiers() == 0)
145 {
146 fireActionPerformed();
147 evt.consume();
148 }
149 }
150 else if(evt.getKeyCode() == KeyEvent.VK_UP)
151 {
152 if((evt.getModifiers() & InputEvent.CTRL_MASK) != 0)
153 doBackwardSearch();
154 else
155 historyPrevious();
156 evt.consume();
157 }
158 else if(evt.getKeyCode() == KeyEvent.VK_DOWN)
159 {
160 if((evt.getModifiers() & InputEvent.CTRL_MASK) != 0)
161 doForwardSearch();
162 else
163 historyNext();
164 evt.consume();
165 }
166 else if(evt.getKeyCode() == KeyEvent.VK_TAB
167 && (evt.getModifiers() & InputEvent.CTRL_MASK) != 0)
168 {
169 doBackwardSearch();
170 evt.consume();
171 }
172 }
173
174 if(!evt.isConsumed())
175 super.processKeyEvent(evt);
176 }
177
178 // private members
179 private HistoryModel historyModel;
180 private JPopupMenu popup;
181 private boolean instantPopups;
182 private boolean enterAddsToHistory;
183 private String current;
184 private int index;
185
186 private void doBackwardSearch()
187 {
188 if(historyModel == null)
189 return;
190
191 if(getSelectionEnd() != getDocument().getLength())
192 {
193 setCaretPosition(getDocument().getLength());
194 }
195
196 String text = getText().substring(0,getSelectionStart());
197 if(text == null)
198 {
199 historyPrevious();
200 return;
201 }
202
203 for(int i = index + 1; i < historyModel.getSize(); i++)
204 {
205 String item = historyModel.getItem(i);
206 if(item.startsWith(text))
207 {
208 replaceSelection(item.substring(text.length()));
209 select(text.length(),getDocument().getLength());
210 index = i;
211 return;
212 }
213 }
214
215 getToolkit().beep();
216 }
217
218 private void doForwardSearch()
219 {
220 if(historyModel == null)
221 return;
222
223 if(getSelectionEnd() != getDocument().getLength())
224 {
225 setCaretPosition(getDocument().getLength());
226 }
227
228 String text = getText().substring(0,getSelectionStart());
229 if(text == null)
230 {
231 historyNext();
232 return;
233 }
234
235 for(int i = index - 1; i >= 0; i--)
236 {
237 String item = historyModel.getItem(i);
238 if(item.startsWith(text))
239 {
240 replaceSelection(item.substring(text.length()));
241 select(text.length(),getDocument().getLength());
242 index = i;
243 return;
244 }
245 }
246
247 getToolkit().beep();
248 }
249
250 private void historyPrevious()
251 {
252 if(historyModel == null)
253 return;
254
255 if(index == historyModel.getSize() - 1)
256 getToolkit().beep();
257 else if(index == -1)
258 {
259 current = getText();
260 setText(historyModel.getItem(0));
261 index = 0;
262 }
263 else
264 {
265 // have to do this because setText() sets index to -1
266 int newIndex = index + 1;
267 setText(historyModel.getItem(newIndex));
268 index = newIndex;
269 }
270 }
271
272 private void historyNext()
273 {
274 if(historyModel == null)
275 return;
276
277 if(index == -1)
278 getToolkit().beep();
279 else if(index == 0)
280 setText(current);
281 else
282 {
283 // have to do this because setText() sets index to -1
284 int newIndex = index - 1;
285 setText(historyModel.getItem(newIndex));
286 index = newIndex;
287 }
288 }
289
290 private void showPopupMenu(String text, int x, int y)
291 {
292 if(historyModel == null)
293 return;
294
295 requestFocus();
296
297 if(popup != null && popup.isVisible())
298 {
299 popup.setVisible(false);
300 return;
301 }
302
303 ActionHandler actionListener = new ActionHandler();
304
305 popup = new JPopupMenu();
306 JMenuItem caption = new JMenuItem(historyModel.getName()
307 + (text.length() == 0 ? "" : "/" + text));
308 caption.getModel().setEnabled(false);
309 popup.add(caption);
310 popup.addSeparator();
311
312 for(int i = 0; i < historyModel.getSize(); i++)
313 {
314 String item = historyModel.getItem(i);
315 if(item.startsWith(text))
316 {
317 JMenuItem menuItem = new JMenuItem(item);
318 menuItem.setActionCommand(String.valueOf(i));
319 menuItem.addActionListener(actionListener);
320 popup.add(menuItem);
321 }
322 }
323
324 popup.show(this,x,y);
325 }
326
327 class ActionHandler implements ActionListener
328 {
329 public void actionPerformed(ActionEvent evt)
330 {
331 int ind = Integer.parseInt(evt.getActionCommand());
332 if(ind == -1)
333 {
334 if(index != -1)
335 setText(current);
336 }
337 else
338 {
339 setText(historyModel.getItem(ind));
340 index = ind;
341 }
342 if(instantPopups)
343 {
344 addCurrentToHistory();
345 fireActionPerformed();
346 }
347 }
348 }
349
350 class MouseHandler extends MouseAdapter
351 {
352 public void mousePressed(MouseEvent evt)
353 {
354 if((evt.getModifiers() & InputEvent.CTRL_MASK) != 0)
355 {
356 showPopupMenu(getText().substring(0,getSelectionStart()),
357 0,getHeight());
358 }
359 else if((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
360 showPopupMenu("",0,getHeight());
361 }
362 }
363 }