experimental 24-bit image protocol
[nikiroo-utils.git] / src / jexer / TComboBox.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
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
64 /**
65 * If true, the field cannot be updated to a value not on the list.
66 */
67 private boolean limitToListValue = true;
68
69 /**
70 * The maximum height of the values drop-down when it is visible.
71 */
72 private int maxValuesHeight = 3;
73
74 // ------------------------------------------------------------------------
75 // Constructors -----------------------------------------------------------
76 // ------------------------------------------------------------------------
77
78 /**
79 * Public constructor.
80 *
81 * @param parent parent widget
82 * @param x column relative to parent
83 * @param y row relative to parent
84 * @param width visible combobox width, including the down-arrow
85 * @param values the possible values for the box, shown in the drop-down
86 * @param valuesIndex the initial index in values, or -1 for no default
87 * value
88 * @param maxValuesHeight the maximum height of the values drop-down when
89 * it is visible
90 * @param updateAction action to call when a new value is selected from
91 * the list or enter is pressed in the edit field
92 */
93 public TComboBox(final TWidget parent, final int x, final int y,
94 final int width, final List<String> values, final int valuesIndex,
95 final int maxValuesHeight, final TAction updateAction) {
96
97 // Set parent and window
98 super(parent, x, y, width, 1);
99
100 assert (values != null);
101
102 this.updateAction = updateAction;
103 this.maxValuesHeight = maxValuesHeight;
104
105 field = addField(0, 0, width - 3, false, "", updateAction, null);
106 if ((valuesIndex >= 0) && (valuesIndex < values.size())) {
107 field.setText(values.get(valuesIndex));
108 }
109
110 list = addList(values, 0, 1, width,
111 Math.max(3, Math.min(values.size() + 1, maxValuesHeight)),
112 new TAction() {
113 public void DO() {
114 field.setText(list.getSelected());
115 list.setEnabled(false);
116 list.setVisible(false);
117 TComboBox.super.setHeight(1);
118 if (TComboBox.this.limitToListValue == false) {
119 TComboBox.this.activate(field);
120 }
121 if (updateAction != null) {
122 updateAction.DO(TComboBox.this);
123 }
124 }
125 }
126 );
127 if (valuesIndex >= 0) {
128 list.setSelectedIndex(valuesIndex);
129 }
130
131 list.setEnabled(false);
132 list.setVisible(false);
133 super.setHeight(1);
134 if (limitToListValue) {
135 field.setEnabled(false);
136 } else {
137 activate(field);
138 }
139 }
140
141 // ------------------------------------------------------------------------
142 // Event handlers ---------------------------------------------------------
143 // ------------------------------------------------------------------------
144
145 /**
146 * Returns true if the mouse is currently on the down arrow.
147 *
148 * @param mouse mouse event
149 * @return true if the mouse is currently on the down arrow
150 */
151 private boolean mouseOnArrow(final TMouseEvent mouse) {
152 if ((mouse.getY() == 0)
153 && (mouse.getX() >= getWidth() - 3)
154 && (mouse.getX() <= getWidth() - 1)
155 ) {
156 return true;
157 }
158 return false;
159 }
160
161 /**
162 * Handle mouse down clicks.
163 *
164 * @param mouse mouse button down event
165 */
166 @Override
167 public void onMouseDown(final TMouseEvent mouse) {
168 if ((mouseOnArrow(mouse)) && (mouse.isMouse1())) {
169 // Make the list visible or not.
170 if (list.isActive()) {
171 hideList();
172 } else {
173 showList();
174 }
175 }
176
177 // Pass to parent for the things we don't care about.
178 super.onMouseDown(mouse);
179 }
180
181 /**
182 * Handle keystrokes.
183 *
184 * @param keypress keystroke event
185 */
186 @Override
187 public void onKeypress(final TKeypressEvent keypress) {
188 if (keypress.equals(kbEsc)) {
189 if (list.isActive()) {
190 hideList();
191 return;
192 }
193 }
194
195 if (keypress.equals(kbAltDown)) {
196 showList();
197 return;
198 }
199
200 if (keypress.equals(kbTab)
201 || (keypress.equals(kbShiftTab))
202 || (keypress.equals(kbBackTab))
203 ) {
204 if (list.isActive()) {
205 hideList();
206 return;
207 }
208 }
209
210 // Pass to parent for the things we don't care about.
211 super.onKeypress(keypress);
212 }
213
214 // ------------------------------------------------------------------------
215 // TWidget ----------------------------------------------------------------
216 // ------------------------------------------------------------------------
217
218 /**
219 * Override TWidget's width: we need to set child widget widths.
220 *
221 * @param width new widget width
222 */
223 @Override
224 public void setWidth(final int width) {
225 if (field != null) {
226 field.setWidth(width - 3);
227 }
228 if (list != null) {
229 list.setWidth(width);
230 }
231 super.setWidth(width);
232 }
233
234 /**
235 * Override TWidget's height: we can only set height at construction
236 * time.
237 *
238 * @param height new widget height (ignored)
239 */
240 @Override
241 public void setHeight(final int height) {
242 // Do nothing
243 }
244
245 /**
246 * Draw the combobox down arrow.
247 */
248 @Override
249 public void draw() {
250 CellAttributes comboBoxColor;
251
252 if (!isAbsoluteActive()) {
253 // We lost focus, turn off the list.
254 if (list.isActive()) {
255 hideList();
256 }
257 }
258
259 if (isAbsoluteActive()) {
260 comboBoxColor = getTheme().getColor("tcombobox.active");
261 } else {
262 comboBoxColor = getTheme().getColor("tcombobox.inactive");
263 }
264
265 putCharXY(getWidth() - 3, 0, GraphicsChars.DOWNARROWLEFT,
266 comboBoxColor);
267 putCharXY(getWidth() - 2, 0, GraphicsChars.DOWNARROW,
268 comboBoxColor);
269 putCharXY(getWidth() - 1, 0, GraphicsChars.DOWNARROWRIGHT,
270 comboBoxColor);
271 }
272
273 // ------------------------------------------------------------------------
274 // TComboBox --------------------------------------------------------------
275 // ------------------------------------------------------------------------
276
277 /**
278 * Hide the drop-down list.
279 */
280 public void hideList() {
281 list.setEnabled(false);
282 list.setVisible(false);
283 super.setHeight(1);
284 if (limitToListValue == false) {
285 activate(field);
286 }
287 }
288
289 /**
290 * Show the drop-down list.
291 */
292 public void showList() {
293 list.setEnabled(true);
294 list.setVisible(true);
295 super.setHeight(list.getHeight() + 1);
296 activate(list);
297 }
298
299 /**
300 * Get combobox text value.
301 *
302 * @return text in the edit field
303 */
304 public String getText() {
305 return field.getText();
306 }
307
308 /**
309 * Set combobox text value.
310 *
311 * @param text the new text in the edit field
312 */
313 public void setText(final String text) {
314 setText(text, true);
315 }
316
317 /**
318 * Set combobox text value.
319 *
320 * @param text the new text in the edit field
321 * @param caseSensitive if true, perform a case-sensitive search for the
322 * list item
323 */
324 public void setText(final String text, final boolean caseSensitive) {
325 field.setText(text);
326 for (int i = 0; i < list.getMaxSelectedIndex(); i++) {
327 if (caseSensitive == true) {
328 if (list.getListItem(i).equals(text)) {
329 list.setSelectedIndex(i);
330 return;
331 }
332 } else {
333 if (list.getListItem(i).toLowerCase().equals(text.toLowerCase())) {
334 list.setSelectedIndex(i);
335 return;
336 }
337 }
338 }
339 list.setSelectedIndex(-1);
340 }
341
342 /**
343 * Set combobox text to one of the list values.
344 *
345 * @param index the index in the list
346 */
347 public void setIndex(final int index) {
348 list.setSelectedIndex(index);
349 field.setText(list.getSelected());
350 }
351
352 /**
353 * Get a copy of the list of strings to display.
354 *
355 * @return the list of strings
356 */
357 public final List<String> getList() {
358 return list.getList();
359 }
360
361 /**
362 * Set the new list of strings to display.
363 *
364 * @param list new list of strings
365 */
366 public final void setList(final List<String> list) {
367 this.list.setList(list);
368 this.list.setHeight(Math.max(3, Math.min(list.size() + 1,
369 maxValuesHeight)));
370 field.setText("");
371 }
372
373 }