#55 restore default attributes on exit
[nikiroo-utils.git] / src / jexer / TComboBox.java
CommitLineData
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 */
29package jexer;
30
31import java.util.List;
32
33import jexer.bits.CellAttributes;
34import jexer.bits.GraphicsChars;
35import jexer.event.TKeypressEvent;
36import jexer.event.TMouseEvent;
37import 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 */
43public 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
8ab60a33
KL
69 /**
70 * The maximum height of the values drop-down when it is visible.
71 */
72 private int maxValuesHeight = 3;
73
051e2913
KL
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
8ab60a33
KL
88 * @param maxValuesHeight the maximum height of the values drop-down when
89 * it is visible
051e2913
KL
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,
8ab60a33 95 final int maxValuesHeight, final TAction updateAction) {
051e2913
KL
96
97 // Set parent and window
98 super(parent, x, y, width, 1);
99
a69ed767
KL
100 assert (values != null);
101
051e2913 102 this.updateAction = updateAction;
8ab60a33 103 this.maxValuesHeight = maxValuesHeight;
051e2913 104
382bc294 105 field = addField(0, 0, width - 3, false, "", updateAction, null);
8ab60a33 106 if ((valuesIndex >= 0) && (valuesIndex < values.size())) {
051e2913
KL
107 field.setText(values.get(valuesIndex));
108 }
109
8ab60a33
KL
110 list = addList(values, 0, 1, width,
111 Math.max(3, Math.min(values.size() + 1, maxValuesHeight)),
051e2913
KL
112 new TAction() {
113 public void DO() {
114 field.setText(list.getSelected());
115 list.setEnabled(false);
116 list.setVisible(false);
d8dc8aea 117 TComboBox.super.setHeight(1);
a69ed767
KL
118 if (TComboBox.this.limitToListValue == false) {
119 TComboBox.this.activate(field);
120 }
051e2913 121 if (updateAction != null) {
a524aa2e 122 updateAction.DO(TComboBox.this);
051e2913
KL
123 }
124 }
125 }
126 );
a69ed767
KL
127 if (valuesIndex >= 0) {
128 list.setSelectedIndex(valuesIndex);
129 }
051e2913
KL
130
131 list.setEnabled(false);
132 list.setVisible(false);
d8dc8aea 133 super.setHeight(1);
a69ed767
KL
134 if (limitToListValue) {
135 field.setEnabled(false);
136 } else {
137 activate(field);
138 }
051e2913
KL
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)
a69ed767
KL
153 && (mouse.getX() >= getWidth() - 3)
154 && (mouse.getX() <= getWidth() - 1)
051e2913
KL
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()) {
8ab60a33 171 hideList();
051e2913 172 } else {
8ab60a33 173 showList();
051e2913
KL
174 }
175 }
a69ed767
KL
176
177 // Pass to parent for the things we don't care about.
178 super.onMouseDown(mouse);
051e2913
KL
179 }
180
181 /**
182 * Handle keystrokes.
183 *
184 * @param keypress keystroke event
185 */
186 @Override
187 public void onKeypress(final TKeypressEvent keypress) {
a69ed767
KL
188 if (keypress.equals(kbEsc)) {
189 if (list.isActive()) {
8ab60a33 190 hideList();
a69ed767
KL
191 return;
192 }
193 }
194
051e2913 195 if (keypress.equals(kbAltDown)) {
8ab60a33 196 showList();
051e2913
KL
197 return;
198 }
199
200 if (keypress.equals(kbTab)
201 || (keypress.equals(kbShiftTab))
202 || (keypress.equals(kbBackTab))
203 ) {
204 if (list.isActive()) {
8ab60a33 205 hideList();
051e2913
KL
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
d8dc8aea 218 /**
8f62f06e 219 * Override TWidget's width: we need to set child widget widths.
d8dc8aea 220 *
8f62f06e 221 * @param width new widget width
d8dc8aea
KL
222 */
223 @Override
224 public void setWidth(final int width) {
fc2af494
KL
225 if (field != null) {
226 field.setWidth(width - 3);
227 }
228 if (list != null) {
229 list.setWidth(width);
230 }
8f62f06e 231 super.setWidth(width);
d8dc8aea
KL
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
051e2913
KL
245 /**
246 * Draw the combobox down arrow.
247 */
248 @Override
249 public void draw() {
250 CellAttributes comboBoxColor;
251
a69ed767
KL
252 if (!isAbsoluteActive()) {
253 // We lost focus, turn off the list.
254 if (list.isActive()) {
8ab60a33 255 hideList();
a69ed767
KL
256 }
257 }
258
e23ea538 259 if (isAbsoluteActive()) {
051e2913
KL
260 comboBoxColor = getTheme().getColor("tcombobox.active");
261 } else {
262 comboBoxColor = getTheme().getColor("tcombobox.inactive");
263 }
264
a69ed767
KL
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,
051e2913
KL
270 comboBoxColor);
271 }
272
273 // ------------------------------------------------------------------------
274 // TComboBox --------------------------------------------------------------
275 // ------------------------------------------------------------------------
276
8ab60a33
KL
277 /**
278 * Hide the drop-down list.
279 */
280 public void hideList() {
281 list.setEnabled(false);
282 list.setVisible(false);
d8dc8aea 283 super.setHeight(1);
8ab60a33
KL
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);
d8dc8aea 295 super.setHeight(list.getHeight() + 1);
8ab60a33
KL
296 activate(list);
297 }
298
051e2913
KL
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) {
af56159c
KL
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) {
051e2913
KL
325 field.setText(text);
326 for (int i = 0; i < list.getMaxSelectedIndex(); i++) {
af56159c
KL
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 }
051e2913
KL
337 }
338 }
339 list.setSelectedIndex(-1);
340 }
341
a69ed767
KL
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);
8ab60a33
KL
368 this.list.setHeight(Math.max(3, Math.min(list.size() + 1,
369 maxValuesHeight)));
a69ed767
KL
370 field.setText("");
371 }
372
051e2913 373}