|
1 /* |
|
2 * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 package com.sun.swingset3.demos.textfield; |
|
24 |
|
25 import java.awt.*; |
|
26 import java.awt.event.*; |
|
27 import java.util.*; |
|
28 import java.util.List; |
|
29 import javax.swing.*; |
|
30 import javax.swing.border.LineBorder; |
|
31 import javax.swing.event.DocumentEvent; |
|
32 import javax.swing.event.DocumentListener; |
|
33 |
|
34 /** |
|
35 * JHistoryTextField |
|
36 * |
|
37 * @author Pavel Porvatov |
|
38 */ |
|
39 public class JHistoryTextField extends JTextField { |
|
40 |
|
41 private static final int MAX_VISIBLE_ROWS = 8; |
|
42 |
|
43 private final List<String> history = new ArrayList<String>(); |
|
44 |
|
45 private final JPopupMenu popup = new JPopupMenu() { |
|
46 @Override |
|
47 public Dimension getPreferredSize() { |
|
48 Dimension dimension = super.getPreferredSize(); |
|
49 |
|
50 dimension.width = JHistoryTextField.this.getWidth(); |
|
51 |
|
52 return dimension; |
|
53 } |
|
54 }; |
|
55 |
|
56 private final JList<String> list = new JList<>(new DefaultListModel<>()); |
|
57 |
|
58 private String userText; |
|
59 |
|
60 private boolean notificationDenied; |
|
61 |
|
62 public JHistoryTextField() { |
|
63 JScrollPane scrollPane = new JScrollPane(list, |
|
64 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, |
|
65 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
|
66 scrollPane.setHorizontalScrollBar(null); |
|
67 scrollPane.setBorder(null); |
|
68 |
|
69 list.setFocusable(false); |
|
70 |
|
71 popup.add(scrollPane); |
|
72 popup.setFocusable(false); |
|
73 popup.setBorder(new LineBorder(Color.BLACK, 1)); |
|
74 |
|
75 getDocument().addDocumentListener(new DocumentListener() { |
|
76 @Override |
|
77 public void insertUpdate(DocumentEvent e) { |
|
78 onTextChanged(); |
|
79 } |
|
80 |
|
81 @Override |
|
82 public void removeUpdate(DocumentEvent e) { |
|
83 onTextChanged(); |
|
84 } |
|
85 |
|
86 @Override |
|
87 public void changedUpdate(DocumentEvent e) { |
|
88 onTextChanged(); |
|
89 } |
|
90 }); |
|
91 |
|
92 list.addMouseMotionListener(new MouseAdapter() { |
|
93 @Override |
|
94 public void mouseMoved(MouseEvent e) { |
|
95 int index = list.locationToIndex(e.getPoint()); |
|
96 |
|
97 if (index >= 0 && list.getSelectedIndex() != index) { |
|
98 list.setSelectedIndex(index); |
|
99 } |
|
100 } |
|
101 }); |
|
102 |
|
103 list.addMouseListener(new MouseAdapter() { |
|
104 @Override |
|
105 public void mouseReleased(MouseEvent e) { |
|
106 if (SwingUtilities.isLeftMouseButton(e)) { |
|
107 setTextWithoutNotification(list.getSelectedValue()); |
|
108 |
|
109 popup.setVisible(false); |
|
110 } |
|
111 } |
|
112 }); |
|
113 |
|
114 addFocusListener(new FocusAdapter() { |
|
115 @Override |
|
116 public void focusLost(FocusEvent e) { |
|
117 popup.setVisible(false); |
|
118 } |
|
119 }); |
|
120 |
|
121 addKeyListener(new KeyAdapter() { |
|
122 @Override |
|
123 public void keyPressed(KeyEvent e) { |
|
124 if (popup.isShowing()) { |
|
125 switch (e.getKeyCode()) { |
|
126 case KeyEvent.VK_UP: { |
|
127 changeListSelectedIndex(-1); |
|
128 |
|
129 break; |
|
130 } |
|
131 |
|
132 case KeyEvent.VK_PAGE_UP: { |
|
133 changeListSelectedIndex(-list.getVisibleRowCount()); |
|
134 |
|
135 break; |
|
136 } |
|
137 |
|
138 case KeyEvent.VK_DOWN: { |
|
139 changeListSelectedIndex(1); |
|
140 |
|
141 break; |
|
142 } |
|
143 |
|
144 case KeyEvent.VK_PAGE_DOWN: { |
|
145 changeListSelectedIndex(list.getVisibleRowCount()); |
|
146 |
|
147 break; |
|
148 } |
|
149 |
|
150 case KeyEvent.VK_ESCAPE: { |
|
151 popup.setVisible(false); |
|
152 |
|
153 setTextWithoutNotification(userText); |
|
154 |
|
155 break; |
|
156 } |
|
157 |
|
158 case KeyEvent.VK_ENTER: |
|
159 case KeyEvent.VK_LEFT: |
|
160 case KeyEvent.VK_RIGHT: { |
|
161 popup.setVisible(false); |
|
162 |
|
163 break; |
|
164 } |
|
165 } |
|
166 } else if (e.getKeyCode() == KeyEvent.VK_DOWN |
|
167 || e.getKeyCode() == KeyEvent.VK_UP |
|
168 || e.getKeyCode() == KeyEvent.VK_PAGE_UP |
|
169 || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) { |
|
170 userText = getText(); |
|
171 |
|
172 showFilteredHistory(); |
|
173 } |
|
174 } |
|
175 }); |
|
176 } |
|
177 |
|
178 private void changeListSelectedIndex(int delta) { |
|
179 int size = list.getModel().getSize(); |
|
180 int index = list.getSelectedIndex(); |
|
181 |
|
182 int newIndex; |
|
183 |
|
184 if (index < 0) { |
|
185 newIndex = delta > 0 ? 0 : size - 1; |
|
186 } else { |
|
187 newIndex = index + delta; |
|
188 } |
|
189 |
|
190 if (newIndex >= size || newIndex < 0) { |
|
191 newIndex = newIndex < 0 ? 0 : size - 1; |
|
192 |
|
193 if (index == newIndex) { |
|
194 newIndex = -1; |
|
195 } |
|
196 } |
|
197 |
|
198 if (newIndex < 0) { |
|
199 list.getSelectionModel().clearSelection(); |
|
200 list.ensureIndexIsVisible(0); |
|
201 |
|
202 setTextWithoutNotification(userText); |
|
203 } else { |
|
204 list.setSelectedIndex(newIndex); |
|
205 list.ensureIndexIsVisible(newIndex); |
|
206 |
|
207 setTextWithoutNotification(list.getSelectedValue()); |
|
208 } |
|
209 } |
|
210 |
|
211 private void setTextWithoutNotification(String text) { |
|
212 notificationDenied = true; |
|
213 |
|
214 try { |
|
215 setText(text); |
|
216 } finally { |
|
217 notificationDenied = false; |
|
218 } |
|
219 } |
|
220 |
|
221 private void onTextChanged() { |
|
222 if (!notificationDenied) { |
|
223 userText = getText(); |
|
224 |
|
225 showFilteredHistory(); |
|
226 } |
|
227 } |
|
228 |
|
229 private void showFilteredHistory() { |
|
230 list.getSelectionModel().clearSelection(); |
|
231 |
|
232 DefaultListModel<String> model = (DefaultListModel<String>) list.getModel(); |
|
233 |
|
234 model.clear(); |
|
235 |
|
236 for (String s : history) { |
|
237 if (s.contains(userText)) { |
|
238 model.addElement(s); |
|
239 } |
|
240 } |
|
241 |
|
242 int size = model.size(); |
|
243 |
|
244 if (size == 0) { |
|
245 popup.setVisible(false); |
|
246 } else { |
|
247 list.setVisibleRowCount(size < MAX_VISIBLE_ROWS ? size : MAX_VISIBLE_ROWS); |
|
248 |
|
249 popup.pack(); |
|
250 |
|
251 if (!popup.isShowing()) { |
|
252 popup.show(JHistoryTextField.this, 0, getHeight()); |
|
253 } |
|
254 } |
|
255 } |
|
256 |
|
257 public List<String> getHistory() { |
|
258 return Collections.unmodifiableList(history); |
|
259 } |
|
260 |
|
261 public void setHistory(List<? extends String> history) { |
|
262 this.history.clear(); |
|
263 this.history.addAll(history); |
|
264 } |
|
265 } |