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