LICENSE CHANGED TO MIT
[nikiroo-utils.git] / src / jexer / TList.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2016 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.ArrayList;
32 import java.util.List;
33
34 import jexer.bits.CellAttributes;
35 import jexer.event.TKeypressEvent;
36 import jexer.event.TMouseEvent;
37 import static jexer.TKeypress.*;
38
39 /**
40 * TList shows a list of strings, and lets the user select one.
41 */
42 public class TList extends TWidget {
43
44 /**
45 * The list of strings to display.
46 */
47 private List<String> strings;
48
49 /**
50 * Selected string.
51 */
52 private int selectedString = -1;
53
54 /**
55 * Get the selection index.
56 *
57 * @return -1 if nothing is selected, otherwise the index into the list
58 */
59 public final int getSelectedIndex() {
60 return selectedString;
61 }
62
63 /**
64 * Set the selected string index.
65 *
66 * @param index -1 to unselect, otherwise the index into the list
67 */
68 public final void setSelectedIndex(final int index) {
69 selectedString = index;
70 }
71
72 /**
73 * Get the selected string.
74 *
75 * @return the selected string, or null of nothing is selected yet
76 */
77 public final String getSelected() {
78 if ((selectedString >= 0) && (selectedString <= strings.size() - 1)) {
79 return strings.get(selectedString);
80 }
81 return null;
82 }
83
84 /**
85 * Set the new list of strings to display.
86 *
87 * @param list new list of strings
88 */
89 public final void setList(final List<String> list) {
90 strings.clear();
91 strings.addAll(list);
92 reflow();
93 }
94
95 /**
96 * Vertical scrollbar.
97 */
98 private TVScroller vScroller;
99
100 /**
101 * Get the vertical scrollbar. This is used by subclasses.
102 *
103 * @return the vertical scrollbar
104 */
105 public final TVScroller getVScroller() {
106 return vScroller;
107 }
108
109 /**
110 * Horizontal scrollbar.
111 */
112 private THScroller hScroller;
113
114 /**
115 * Get the horizontal scrollbar. This is used by subclasses.
116 *
117 * @return the horizontal scrollbar
118 */
119 public final THScroller getHScroller() {
120 return hScroller;
121 }
122
123 /**
124 * Maximum width of a single line.
125 */
126 private int maxLineWidth;
127
128 /**
129 * The action to perform when the user selects an item (clicks or enter).
130 */
131 private TAction enterAction = null;
132
133 /**
134 * The action to perform when the user navigates with keyboard.
135 */
136 private TAction moveAction = null;
137
138 /**
139 * Perform user selection action.
140 */
141 public void dispatchEnter() {
142 assert (selectedString >= 0);
143 assert (selectedString < strings.size());
144 if (enterAction != null) {
145 enterAction.DO();
146 }
147 }
148
149 /**
150 * Perform list movement action.
151 */
152 public void dispatchMove() {
153 assert (selectedString >= 0);
154 assert (selectedString < strings.size());
155 if (moveAction != null) {
156 moveAction.DO();
157 }
158 }
159
160 /**
161 * Resize for a new width/height.
162 */
163 public void reflow() {
164
165 // Reset the lines
166 selectedString = -1;
167 maxLineWidth = 0;
168
169 for (int i = 0; i < strings.size(); i++) {
170 String line = strings.get(i);
171 if (line.length() > maxLineWidth) {
172 maxLineWidth = line.length();
173 }
174 }
175
176 // Start at the top
177 if (vScroller == null) {
178 vScroller = new TVScroller(this, getWidth() - 1, 0,
179 getHeight() - 1);
180 } else {
181 vScroller.setX(getWidth() - 1);
182 vScroller.setHeight(getHeight() - 1);
183 }
184 vScroller.setBottomValue(strings.size() - getHeight() + 1);
185 vScroller.setTopValue(0);
186 vScroller.setValue(0);
187 if (vScroller.getBottomValue() < 0) {
188 vScroller.setBottomValue(0);
189 }
190 vScroller.setBigChange(getHeight() - 1);
191
192 // Start at the left
193 if (hScroller == null) {
194 hScroller = new THScroller(this, 0, getHeight() - 1,
195 getWidth() - 1);
196 } else {
197 hScroller.setY(getHeight() - 1);
198 hScroller.setWidth(getWidth() - 1);
199 }
200 hScroller.setRightValue(maxLineWidth - getWidth() + 1);
201 hScroller.setLeftValue(0);
202 hScroller.setValue(0);
203 if (hScroller.getRightValue() < 0) {
204 hScroller.setRightValue(0);
205 }
206 hScroller.setBigChange(getWidth() - 1);
207 }
208
209 /**
210 * Public constructor.
211 *
212 * @param parent parent widget
213 * @param strings list of strings to show
214 * @param x column relative to parent
215 * @param y row relative to parent
216 * @param width width of text area
217 * @param height height of text area
218 */
219 public TList(final TWidget parent, final List<String> strings, final int x,
220 final int y, final int width, final int height) {
221
222 this(parent, strings, x, y, width, height, null);
223 }
224
225 /**
226 * Public constructor.
227 *
228 * @param parent parent widget
229 * @param strings list of strings to show. This is allowed to be null
230 * and set later with setList() or by subclasses.
231 * @param x column relative to parent
232 * @param y row relative to parent
233 * @param width width of text area
234 * @param height height of text area
235 * @param enterAction action to perform when an item is selected
236 */
237 public TList(final TWidget parent, final List<String> strings, final int x,
238 final int y, final int width, final int height,
239 final TAction enterAction) {
240
241 super(parent, x, y, width, height);
242 this.enterAction = enterAction;
243 this.strings = new ArrayList<String>();
244 if (strings != null) {
245 this.strings.addAll(strings);
246 }
247 reflow();
248 }
249
250 /**
251 * Public constructor.
252 *
253 * @param parent parent widget
254 * @param strings list of strings to show. This is allowed to be null
255 * and set later with setList() or by subclasses.
256 * @param x column relative to parent
257 * @param y row relative to parent
258 * @param width width of text area
259 * @param height height of text area
260 * @param enterAction action to perform when an item is selected
261 * @param moveAction action to perform when the user navigates to a new
262 * item with arrow/page keys
263 */
264 public TList(final TWidget parent, final List<String> strings, final int x,
265 final int y, final int width, final int height,
266 final TAction enterAction, final TAction moveAction) {
267
268 super(parent, x, y, width, height);
269 this.enterAction = enterAction;
270 this.moveAction = moveAction;
271 this.strings = new ArrayList<String>();
272 if (strings != null) {
273 this.strings.addAll(strings);
274 }
275 reflow();
276 }
277
278 /**
279 * Draw the files list.
280 */
281 @Override
282 public void draw() {
283 CellAttributes color = null;
284 int begin = vScroller.getValue();
285 int topY = 0;
286 for (int i = begin; i < strings.size(); i++) {
287 String line = strings.get(i);
288 if (hScroller.getValue() < line.length()) {
289 line = line.substring(hScroller.getValue());
290 } else {
291 line = "";
292 }
293 if (i == selectedString) {
294 color = getTheme().getColor("tlist.selected");
295 } else if (isAbsoluteActive()) {
296 color = getTheme().getColor("tlist");
297 } else {
298 color = getTheme().getColor("tlist.inactive");
299 }
300 String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
301 getScreen().putStringXY(0, topY, String.format(formatString, line),
302 color);
303 topY++;
304 if (topY >= getHeight() - 1) {
305 break;
306 }
307 }
308
309 if (isAbsoluteActive()) {
310 color = getTheme().getColor("tlist");
311 } else {
312 color = getTheme().getColor("tlist.inactive");
313 }
314
315 // Pad the rest with blank lines
316 for (int i = topY; i < getHeight() - 1; i++) {
317 getScreen().hLineXY(0, i, getWidth() - 1, ' ', color);
318 }
319 }
320
321 /**
322 * Handle mouse press events.
323 *
324 * @param mouse mouse button press event
325 */
326 @Override
327 public void onMouseDown(final TMouseEvent mouse) {
328 if (mouse.isMouseWheelUp()) {
329 vScroller.decrement();
330 return;
331 }
332 if (mouse.isMouseWheelDown()) {
333 vScroller.increment();
334 return;
335 }
336
337 if ((mouse.getX() < getWidth() - 1)
338 && (mouse.getY() < getHeight() - 1)) {
339 if (vScroller.getValue() + mouse.getY() < strings.size()) {
340 selectedString = vScroller.getValue() + mouse.getY();
341 }
342 dispatchEnter();
343 return;
344 }
345
346 // Pass to children
347 super.onMouseDown(mouse);
348 }
349
350 /**
351 * Handle keystrokes.
352 *
353 * @param keypress keystroke event
354 */
355 @Override
356 public void onKeypress(final TKeypressEvent keypress) {
357 if (keypress.equals(kbLeft)) {
358 hScroller.decrement();
359 } else if (keypress.equals(kbRight)) {
360 hScroller.increment();
361 } else if (keypress.equals(kbUp)) {
362 if (strings.size() > 0) {
363 if (selectedString >= 0) {
364 if (selectedString > 0) {
365 if (selectedString - vScroller.getValue() == 0) {
366 vScroller.decrement();
367 }
368 selectedString--;
369 }
370 } else {
371 selectedString = strings.size() - 1;
372 }
373 }
374 if (selectedString >= 0) {
375 dispatchMove();
376 }
377 } else if (keypress.equals(kbDown)) {
378 if (strings.size() > 0) {
379 if (selectedString >= 0) {
380 if (selectedString < strings.size() - 1) {
381 selectedString++;
382 if (selectedString - vScroller.getValue() == getHeight() - 1) {
383 vScroller.increment();
384 }
385 }
386 } else {
387 selectedString = 0;
388 }
389 }
390 if (selectedString >= 0) {
391 dispatchMove();
392 }
393 } else if (keypress.equals(kbPgUp)) {
394 vScroller.bigDecrement();
395 if (selectedString >= 0) {
396 selectedString -= getHeight() - 1;
397 if (selectedString < 0) {
398 selectedString = 0;
399 }
400 }
401 if (selectedString >= 0) {
402 dispatchMove();
403 }
404 } else if (keypress.equals(kbPgDn)) {
405 vScroller.bigIncrement();
406 if (selectedString >= 0) {
407 selectedString += getHeight() - 1;
408 if (selectedString > strings.size() - 1) {
409 selectedString = strings.size() - 1;
410 }
411 }
412 if (selectedString >= 0) {
413 dispatchMove();
414 }
415 } else if (keypress.equals(kbHome)) {
416 vScroller.toTop();
417 if (strings.size() > 0) {
418 selectedString = 0;
419 }
420 if (selectedString >= 0) {
421 dispatchMove();
422 }
423 } else if (keypress.equals(kbEnd)) {
424 vScroller.toBottom();
425 if (strings.size() > 0) {
426 selectedString = strings.size() - 1;
427 }
428 if (selectedString >= 0) {
429 dispatchMove();
430 }
431 } else if (keypress.equals(kbTab)) {
432 getParent().switchWidget(true);
433 } else if (keypress.equals(kbShiftTab) || keypress.equals(kbBackTab)) {
434 getParent().switchWidget(false);
435 } else if (keypress.equals(kbEnter)) {
436 if (selectedString >= 0) {
437 dispatchEnter();
438 }
439 } else {
440 // Pass other keys (tab etc.) on
441 super.onKeypress(keypress);
442 }
443 }
444
445 }