custom command line terminal window, blinking in color theme, scrolling region resize
[fanfix.git] / src / jexer / TList.java
CommitLineData
3649b921
KL
1/*
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
3649b921 5 *
a2018e99 6 * Copyright (C) 2017 Kevin Lamonte
3649b921 7 *
e16dda65
KL
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:
3649b921 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
3649b921 17 *
e16dda65
KL
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.
3649b921
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer;
30
31import java.util.ArrayList;
32import java.util.List;
33
34import jexer.bits.CellAttributes;
35import jexer.event.TKeypressEvent;
36import jexer.event.TMouseEvent;
37import static jexer.TKeypress.*;
38
39/**
40 * TList shows a list of strings, and lets the user select one.
41 */
56661844 42public class TList extends TScrollableWidget {
3649b921
KL
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
fe0770f9
KL
84 /**
85 * Get the maximum selection index value.
86 *
87 * @return -1 if the list is empty
88 */
89 public final int getMaxSelectedIndex() {
90 return strings.size() - 1;
91 }
92
3649b921
KL
93 /**
94 * Set the new list of strings to display.
95 *
96 * @param list new list of strings
97 */
98 public final void setList(final List<String> list) {
99 strings.clear();
100 strings.addAll(list);
56661844 101 reflowData();
3649b921
KL
102 }
103
104 /**
105 * Maximum width of a single line.
106 */
107 private int maxLineWidth;
108
109 /**
110 * The action to perform when the user selects an item (clicks or enter).
111 */
112 private TAction enterAction = null;
113
114 /**
115 * The action to perform when the user navigates with keyboard.
116 */
117 private TAction moveAction = null;
118
119 /**
120 * Perform user selection action.
121 */
122 public void dispatchEnter() {
123 assert (selectedString >= 0);
124 assert (selectedString < strings.size());
125 if (enterAction != null) {
126 enterAction.DO();
127 }
128 }
129
130 /**
131 * Perform list movement action.
132 */
133 public void dispatchMove() {
134 assert (selectedString >= 0);
135 assert (selectedString < strings.size());
136 if (moveAction != null) {
137 moveAction.DO();
138 }
139 }
140
141 /**
142 * Resize for a new width/height.
143 */
56661844
KL
144 @Override
145 public void reflowData() {
3649b921
KL
146
147 // Reset the lines
148 selectedString = -1;
149 maxLineWidth = 0;
150
151 for (int i = 0; i < strings.size(); i++) {
152 String line = strings.get(i);
153 if (line.length() > maxLineWidth) {
154 maxLineWidth = line.length();
155 }
156 }
157
56661844
KL
158 setBottomValue(strings.size() - getHeight() + 1);
159 if (getBottomValue() < 0) {
160 setBottomValue(0);
3649b921 161 }
3649b921 162
56661844
KL
163 setRightValue(maxLineWidth - getWidth() + 1);
164 if (getRightValue() < 0) {
165 setRightValue(0);
3649b921 166 }
3649b921
KL
167 }
168
169 /**
170 * Public constructor.
171 *
172 * @param parent parent widget
173 * @param strings list of strings to show
174 * @param x column relative to parent
175 * @param y row relative to parent
176 * @param width width of text area
177 * @param height height of text area
178 */
179 public TList(final TWidget parent, final List<String> strings, final int x,
180 final int y, final int width, final int height) {
181
182 this(parent, strings, x, y, width, height, null);
183 }
184
185 /**
186 * Public constructor.
187 *
188 * @param parent parent widget
189 * @param strings list of strings to show. This is allowed to be null
190 * and set later with setList() or by subclasses.
191 * @param x column relative to parent
192 * @param y row relative to parent
193 * @param width width of text area
194 * @param height height of text area
195 * @param enterAction action to perform when an item is selected
196 */
197 public TList(final TWidget parent, final List<String> strings, final int x,
198 final int y, final int width, final int height,
199 final TAction enterAction) {
200
201 super(parent, x, y, width, height);
202 this.enterAction = enterAction;
203 this.strings = new ArrayList<String>();
204 if (strings != null) {
205 this.strings.addAll(strings);
206 }
56661844
KL
207
208 hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
209 vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
210 reflowData();
3649b921
KL
211 }
212
213 /**
214 * Public constructor.
215 *
216 * @param parent parent widget
217 * @param strings list of strings to show. This is allowed to be null
218 * and set later with setList() or by subclasses.
219 * @param x column relative to parent
220 * @param y row relative to parent
221 * @param width width of text area
222 * @param height height of text area
223 * @param enterAction action to perform when an item is selected
224 * @param moveAction action to perform when the user navigates to a new
225 * item with arrow/page keys
226 */
227 public TList(final TWidget parent, final List<String> strings, final int x,
228 final int y, final int width, final int height,
229 final TAction enterAction, final TAction moveAction) {
230
231 super(parent, x, y, width, height);
232 this.enterAction = enterAction;
233 this.moveAction = moveAction;
234 this.strings = new ArrayList<String>();
235 if (strings != null) {
236 this.strings.addAll(strings);
237 }
56661844
KL
238
239 hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
240 vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
241 reflowData();
3649b921
KL
242 }
243
244 /**
245 * Draw the files list.
246 */
247 @Override
248 public void draw() {
249 CellAttributes color = null;
56661844 250 int begin = getVerticalValue();
3649b921
KL
251 int topY = 0;
252 for (int i = begin; i < strings.size(); i++) {
253 String line = strings.get(i);
56661844
KL
254 if (getHorizontalValue() < line.length()) {
255 line = line.substring(getHorizontalValue());
3649b921
KL
256 } else {
257 line = "";
258 }
259 if (i == selectedString) {
260 color = getTheme().getColor("tlist.selected");
261 } else if (isAbsoluteActive()) {
262 color = getTheme().getColor("tlist");
263 } else {
264 color = getTheme().getColor("tlist.inactive");
265 }
266 String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
267 getScreen().putStringXY(0, topY, String.format(formatString, line),
268 color);
269 topY++;
270 if (topY >= getHeight() - 1) {
271 break;
272 }
273 }
274
275 if (isAbsoluteActive()) {
276 color = getTheme().getColor("tlist");
277 } else {
278 color = getTheme().getColor("tlist.inactive");
279 }
280
281 // Pad the rest with blank lines
282 for (int i = topY; i < getHeight() - 1; i++) {
283 getScreen().hLineXY(0, i, getWidth() - 1, ' ', color);
284 }
285 }
286
287 /**
288 * Handle mouse press events.
289 *
290 * @param mouse mouse button press event
291 */
292 @Override
293 public void onMouseDown(final TMouseEvent mouse) {
294 if (mouse.isMouseWheelUp()) {
56661844 295 verticalDecrement();
3649b921
KL
296 return;
297 }
298 if (mouse.isMouseWheelDown()) {
56661844 299 verticalIncrement();
3649b921
KL
300 return;
301 }
302
303 if ((mouse.getX() < getWidth() - 1)
304 && (mouse.getY() < getHeight() - 1)) {
56661844
KL
305 if (getVerticalValue() + mouse.getY() < strings.size()) {
306 selectedString = getVerticalValue() + mouse.getY();
307 dispatchEnter();
3649b921 308 }
3649b921
KL
309 return;
310 }
311
312 // Pass to children
313 super.onMouseDown(mouse);
314 }
315
316 /**
317 * Handle keystrokes.
318 *
319 * @param keypress keystroke event
320 */
321 @Override
322 public void onKeypress(final TKeypressEvent keypress) {
323 if (keypress.equals(kbLeft)) {
56661844 324 horizontalDecrement();
3649b921 325 } else if (keypress.equals(kbRight)) {
56661844 326 horizontalIncrement();
3649b921
KL
327 } else if (keypress.equals(kbUp)) {
328 if (strings.size() > 0) {
329 if (selectedString >= 0) {
330 if (selectedString > 0) {
56661844
KL
331 if (selectedString - getVerticalValue() == 0) {
332 verticalDecrement();
3649b921
KL
333 }
334 selectedString--;
335 }
336 } else {
337 selectedString = strings.size() - 1;
338 }
339 }
340 if (selectedString >= 0) {
341 dispatchMove();
342 }
343 } else if (keypress.equals(kbDown)) {
344 if (strings.size() > 0) {
345 if (selectedString >= 0) {
346 if (selectedString < strings.size() - 1) {
347 selectedString++;
56661844
KL
348 if (selectedString - getVerticalValue() == getHeight() - 1) {
349 verticalIncrement();
3649b921
KL
350 }
351 }
352 } else {
353 selectedString = 0;
354 }
355 }
356 if (selectedString >= 0) {
357 dispatchMove();
358 }
359 } else if (keypress.equals(kbPgUp)) {
56661844 360 bigVerticalDecrement();
3649b921
KL
361 if (selectedString >= 0) {
362 selectedString -= getHeight() - 1;
363 if (selectedString < 0) {
364 selectedString = 0;
365 }
366 }
367 if (selectedString >= 0) {
368 dispatchMove();
369 }
370 } else if (keypress.equals(kbPgDn)) {
56661844 371 bigVerticalIncrement();
3649b921
KL
372 if (selectedString >= 0) {
373 selectedString += getHeight() - 1;
374 if (selectedString > strings.size() - 1) {
375 selectedString = strings.size() - 1;
376 }
377 }
378 if (selectedString >= 0) {
379 dispatchMove();
380 }
381 } else if (keypress.equals(kbHome)) {
56661844 382 toTop();
3649b921
KL
383 if (strings.size() > 0) {
384 selectedString = 0;
385 }
386 if (selectedString >= 0) {
387 dispatchMove();
388 }
389 } else if (keypress.equals(kbEnd)) {
56661844 390 toBottom();
3649b921
KL
391 if (strings.size() > 0) {
392 selectedString = strings.size() - 1;
393 }
394 if (selectedString >= 0) {
395 dispatchMove();
396 }
397 } else if (keypress.equals(kbTab)) {
398 getParent().switchWidget(true);
399 } else if (keypress.equals(kbShiftTab) || keypress.equals(kbBackTab)) {
400 getParent().switchWidget(false);
401 } else if (keypress.equals(kbEnter)) {
402 if (selectedString >= 0) {
403 dispatchEnter();
404 }
405 } else {
406 // Pass other keys (tab etc.) on
407 super.onKeypress(keypress);
408 }
409 }
410
411}