Commit | Line | Data |
---|---|---|
051e2913 KL |
1 | /* |
2 | * Jexer - Java Text User Interface | |
3 | * | |
4 | * The MIT License (MIT) | |
5 | * | |
a69ed767 | 6 | * Copyright (C) 2019 Kevin Lamonte |
051e2913 KL |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a | |
9 | * copy of this software and associated documentation files (the "Software"), | |
10 | * to deal in the Software without restriction, including without limitation | |
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
12 | * and/or sell copies of the Software, and to permit persons to whom the | |
13 | * Software is furnished to do so, subject to the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
24 | * DEALINGS IN THE SOFTWARE. | |
25 | * | |
26 | * @author Kevin Lamonte [kevin.lamonte@gmail.com] | |
27 | * @version 1 | |
28 | */ | |
29 | package jexer; | |
30 | ||
31 | import java.util.List; | |
32 | ||
33 | import jexer.bits.CellAttributes; | |
34 | import jexer.bits.GraphicsChars; | |
35 | import jexer.event.TKeypressEvent; | |
36 | import jexer.event.TMouseEvent; | |
37 | import static jexer.TKeypress.*; | |
38 | ||
39 | /** | |
40 | * TComboBox implements a combobox containing a drop-down list and edit | |
41 | * field. Alt-Down can be used to show the drop-down. | |
42 | */ | |
43 | public class TComboBox extends TWidget { | |
44 | ||
45 | // ------------------------------------------------------------------------ | |
46 | // Variables -------------------------------------------------------------- | |
47 | // ------------------------------------------------------------------------ | |
48 | ||
49 | /** | |
50 | * The list of items in the drop-down. | |
51 | */ | |
52 | private TList list; | |
53 | ||
54 | /** | |
55 | * The edit field containing the value to return. | |
56 | */ | |
57 | private TField field; | |
58 | ||
59 | /** | |
60 | * The action to perform when the user selects an item (clicks or enter). | |
61 | */ | |
62 | private TAction updateAction = null; | |
63 | ||
a69ed767 KL |
64 | /** |
65 | * If true, the field cannot be updated to a value not on the list. | |
66 | */ | |
67 | private boolean limitToListValue = true; | |
68 | ||
051e2913 KL |
69 | // ------------------------------------------------------------------------ |
70 | // Constructors ----------------------------------------------------------- | |
71 | // ------------------------------------------------------------------------ | |
72 | ||
73 | /** | |
74 | * Public constructor. | |
75 | * | |
76 | * @param parent parent widget | |
77 | * @param x column relative to parent | |
78 | * @param y row relative to parent | |
79 | * @param width visible combobox width, including the down-arrow | |
80 | * @param values the possible values for the box, shown in the drop-down | |
81 | * @param valuesIndex the initial index in values, or -1 for no default | |
82 | * value | |
83 | * @param valuesHeight the height of the values drop-down when it is | |
84 | * visible | |
85 | * @param updateAction action to call when a new value is selected from | |
86 | * the list or enter is pressed in the edit field | |
87 | */ | |
88 | public TComboBox(final TWidget parent, final int x, final int y, | |
89 | final int width, final List<String> values, final int valuesIndex, | |
90 | final int valuesHeight, final TAction updateAction) { | |
91 | ||
92 | // Set parent and window | |
93 | super(parent, x, y, width, 1); | |
94 | ||
a69ed767 KL |
95 | assert (values != null); |
96 | ||
051e2913 KL |
97 | this.updateAction = updateAction; |
98 | ||
a69ed767 | 99 | field = new TField(this, 0, 0, width - 3, false, "", |
051e2913 KL |
100 | updateAction, null); |
101 | if (valuesIndex >= 0) { | |
102 | field.setText(values.get(valuesIndex)); | |
103 | } | |
104 | ||
105 | list = new TList(this, values, 0, 1, width, valuesHeight, | |
106 | new TAction() { | |
107 | public void DO() { | |
108 | field.setText(list.getSelected()); | |
109 | list.setEnabled(false); | |
110 | list.setVisible(false); | |
111 | TComboBox.this.setHeight(1); | |
a69ed767 KL |
112 | if (TComboBox.this.limitToListValue == false) { |
113 | TComboBox.this.activate(field); | |
114 | } | |
051e2913 KL |
115 | if (updateAction != null) { |
116 | updateAction.DO(); | |
117 | } | |
118 | } | |
119 | } | |
120 | ); | |
a69ed767 KL |
121 | if (valuesIndex >= 0) { |
122 | list.setSelectedIndex(valuesIndex); | |
123 | } | |
051e2913 KL |
124 | |
125 | list.setEnabled(false); | |
126 | list.setVisible(false); | |
127 | setHeight(1); | |
a69ed767 KL |
128 | if (limitToListValue) { |
129 | field.setEnabled(false); | |
130 | } else { | |
131 | activate(field); | |
132 | } | |
051e2913 KL |
133 | } |
134 | ||
135 | // ------------------------------------------------------------------------ | |
136 | // Event handlers --------------------------------------------------------- | |
137 | // ------------------------------------------------------------------------ | |
138 | ||
139 | /** | |
140 | * Returns true if the mouse is currently on the down arrow. | |
141 | * | |
142 | * @param mouse mouse event | |
143 | * @return true if the mouse is currently on the down arrow | |
144 | */ | |
145 | private boolean mouseOnArrow(final TMouseEvent mouse) { | |
146 | if ((mouse.getY() == 0) | |
a69ed767 KL |
147 | && (mouse.getX() >= getWidth() - 3) |
148 | && (mouse.getX() <= getWidth() - 1) | |
051e2913 KL |
149 | ) { |
150 | return true; | |
151 | } | |
152 | return false; | |
153 | } | |
154 | ||
155 | /** | |
156 | * Handle mouse down clicks. | |
157 | * | |
158 | * @param mouse mouse button down event | |
159 | */ | |
160 | @Override | |
161 | public void onMouseDown(final TMouseEvent mouse) { | |
162 | if ((mouseOnArrow(mouse)) && (mouse.isMouse1())) { | |
163 | // Make the list visible or not. | |
164 | if (list.isActive()) { | |
165 | list.setEnabled(false); | |
166 | list.setVisible(false); | |
167 | setHeight(1); | |
a69ed767 KL |
168 | if (limitToListValue == false) { |
169 | activate(field); | |
170 | } | |
051e2913 KL |
171 | } else { |
172 | list.setEnabled(true); | |
173 | list.setVisible(true); | |
174 | setHeight(list.getHeight() + 1); | |
175 | activate(list); | |
176 | } | |
177 | } | |
a69ed767 KL |
178 | |
179 | // Pass to parent for the things we don't care about. | |
180 | super.onMouseDown(mouse); | |
051e2913 KL |
181 | } |
182 | ||
183 | /** | |
184 | * Handle keystrokes. | |
185 | * | |
186 | * @param keypress keystroke event | |
187 | */ | |
188 | @Override | |
189 | public void onKeypress(final TKeypressEvent keypress) { | |
a69ed767 KL |
190 | if (keypress.equals(kbEsc)) { |
191 | if (list.isActive()) { | |
192 | list.setEnabled(false); | |
193 | list.setVisible(false); | |
194 | setHeight(1); | |
195 | if (limitToListValue == false) { | |
196 | activate(field); | |
197 | } | |
198 | return; | |
199 | } | |
200 | } | |
201 | ||
051e2913 KL |
202 | if (keypress.equals(kbAltDown)) { |
203 | list.setEnabled(true); | |
204 | list.setVisible(true); | |
205 | setHeight(list.getHeight() + 1); | |
206 | activate(list); | |
207 | return; | |
208 | } | |
209 | ||
210 | if (keypress.equals(kbTab) | |
211 | || (keypress.equals(kbShiftTab)) | |
212 | || (keypress.equals(kbBackTab)) | |
213 | ) { | |
214 | if (list.isActive()) { | |
215 | list.setEnabled(false); | |
216 | list.setVisible(false); | |
217 | setHeight(1); | |
a69ed767 KL |
218 | if (limitToListValue == false) { |
219 | activate(field); | |
220 | } | |
051e2913 KL |
221 | return; |
222 | } | |
223 | } | |
224 | ||
225 | // Pass to parent for the things we don't care about. | |
226 | super.onKeypress(keypress); | |
227 | } | |
228 | ||
229 | // ------------------------------------------------------------------------ | |
230 | // TWidget ---------------------------------------------------------------- | |
231 | // ------------------------------------------------------------------------ | |
232 | ||
233 | /** | |
234 | * Draw the combobox down arrow. | |
235 | */ | |
236 | @Override | |
237 | public void draw() { | |
238 | CellAttributes comboBoxColor; | |
239 | ||
a69ed767 KL |
240 | if (!isAbsoluteActive()) { |
241 | // We lost focus, turn off the list. | |
242 | if (list.isActive()) { | |
243 | list.setEnabled(false); | |
244 | list.setVisible(false); | |
245 | setHeight(1); | |
246 | if (limitToListValue == false) { | |
247 | activate(field); | |
248 | } | |
249 | } | |
250 | } | |
251 | ||
e23ea538 | 252 | if (isAbsoluteActive()) { |
051e2913 KL |
253 | comboBoxColor = getTheme().getColor("tcombobox.active"); |
254 | } else { | |
255 | comboBoxColor = getTheme().getColor("tcombobox.inactive"); | |
256 | } | |
257 | ||
a69ed767 KL |
258 | putCharXY(getWidth() - 3, 0, GraphicsChars.DOWNARROWLEFT, |
259 | comboBoxColor); | |
260 | putCharXY(getWidth() - 2, 0, GraphicsChars.DOWNARROW, | |
261 | comboBoxColor); | |
262 | putCharXY(getWidth() - 1, 0, GraphicsChars.DOWNARROWRIGHT, | |
051e2913 KL |
263 | comboBoxColor); |
264 | } | |
265 | ||
266 | // ------------------------------------------------------------------------ | |
267 | // TComboBox -------------------------------------------------------------- | |
268 | // ------------------------------------------------------------------------ | |
269 | ||
270 | /** | |
271 | * Get combobox text value. | |
272 | * | |
273 | * @return text in the edit field | |
274 | */ | |
275 | public String getText() { | |
276 | return field.getText(); | |
277 | } | |
278 | ||
279 | /** | |
280 | * Set combobox text value. | |
281 | * | |
282 | * @param text the new text in the edit field | |
283 | */ | |
284 | public void setText(final String text) { | |
af56159c KL |
285 | setText(text, true); |
286 | } | |
287 | ||
288 | /** | |
289 | * Set combobox text value. | |
290 | * | |
291 | * @param text the new text in the edit field | |
292 | * @param caseSensitive if true, perform a case-sensitive search for the | |
293 | * list item | |
294 | */ | |
295 | public void setText(final String text, final boolean caseSensitive) { | |
051e2913 KL |
296 | field.setText(text); |
297 | for (int i = 0; i < list.getMaxSelectedIndex(); i++) { | |
af56159c KL |
298 | if (caseSensitive == true) { |
299 | if (list.getListItem(i).equals(text)) { | |
300 | list.setSelectedIndex(i); | |
301 | return; | |
302 | } | |
303 | } else { | |
304 | if (list.getListItem(i).toLowerCase().equals(text.toLowerCase())) { | |
305 | list.setSelectedIndex(i); | |
306 | return; | |
307 | } | |
051e2913 KL |
308 | } |
309 | } | |
310 | list.setSelectedIndex(-1); | |
311 | } | |
312 | ||
a69ed767 KL |
313 | /** |
314 | * Set combobox text to one of the list values. | |
315 | * | |
316 | * @param index the index in the list | |
317 | */ | |
318 | public void setIndex(final int index) { | |
319 | list.setSelectedIndex(index); | |
320 | field.setText(list.getSelected()); | |
321 | } | |
322 | ||
323 | /** | |
324 | * Get a copy of the list of strings to display. | |
325 | * | |
326 | * @return the list of strings | |
327 | */ | |
328 | public final List<String> getList() { | |
329 | return list.getList(); | |
330 | } | |
331 | ||
332 | /** | |
333 | * Set the new list of strings to display. | |
334 | * | |
335 | * @param list new list of strings | |
336 | */ | |
337 | public final void setList(final List<String> list) { | |
338 | this.list.setList(list); | |
339 | field.setText(""); | |
340 | } | |
341 | ||
051e2913 | 342 | } |