2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.ArrayList
;
32 import java
.util
.List
;
34 import jexer
.bits
.CellAttributes
;
35 import jexer
.event
.TKeypressEvent
;
36 import jexer
.event
.TMouseEvent
;
37 import static jexer
.TKeypress
.*;
40 * TList shows a list of strings, and lets the user select one.
42 public class TList
extends TScrollableWidget
{
44 // ------------------------------------------------------------------------
45 // Variables --------------------------------------------------------------
46 // ------------------------------------------------------------------------
49 * The list of strings to display.
51 private List
<String
> strings
;
56 private int selectedString
= -1;
59 * Maximum width of a single line.
61 private int maxLineWidth
;
64 * The action to perform when the user selects an item (double-clicks or
67 protected TAction enterAction
= null;
70 * The action to perform when the user selects an item (single-click).
72 protected TAction singleClickAction
= null;
75 * The action to perform when the user navigates with keyboard.
77 protected TAction moveAction
= null;
79 // ------------------------------------------------------------------------
80 // Constructors -----------------------------------------------------------
81 // ------------------------------------------------------------------------
86 * @param parent parent widget
87 * @param strings list of strings to show
88 * @param x column relative to parent
89 * @param y row relative to parent
90 * @param width width of text area
91 * @param height height of text area
93 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
94 final int y
, final int width
, final int height
) {
96 this(parent
, strings
, x
, y
, width
, height
, null);
100 * Public constructor.
102 * @param parent parent widget
103 * @param strings list of strings to show. This is allowed to be null
104 * and set later with setList() or by subclasses.
105 * @param x column relative to parent
106 * @param y row relative to parent
107 * @param width width of text area
108 * @param height height of text area
109 * @param enterAction action to perform when an item is selected
111 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
112 final int y
, final int width
, final int height
,
113 final TAction enterAction
) {
115 super(parent
, x
, y
, width
, height
);
116 this.enterAction
= enterAction
;
117 this.strings
= new ArrayList
<String
>();
118 if (strings
!= null) {
119 this.strings
.addAll(strings
);
122 hScroller
= new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
123 vScroller
= new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
128 * Public constructor.
130 * @param parent parent widget
131 * @param strings list of strings to show. This is allowed to be null
132 * and set later with setList() or by subclasses.
133 * @param x column relative to parent
134 * @param y row relative to parent
135 * @param width width of text area
136 * @param height height of text area
137 * @param enterAction action to perform when an item is selected
138 * @param moveAction action to perform when the user navigates to a new
139 * item with arrow/page keys
141 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
142 final int y
, final int width
, final int height
,
143 final TAction enterAction
, final TAction moveAction
) {
145 super(parent
, x
, y
, width
, height
);
146 this.enterAction
= enterAction
;
147 this.moveAction
= moveAction
;
148 this.strings
= new ArrayList
<String
>();
149 if (strings
!= null) {
150 this.strings
.addAll(strings
);
153 hScroller
= new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
154 vScroller
= new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
158 // ------------------------------------------------------------------------
159 // Event handlers ---------------------------------------------------------
160 // ------------------------------------------------------------------------
163 * Handle mouse press events.
165 * @param mouse mouse button press event
168 public void onMouseDown(final TMouseEvent mouse
) {
169 if (mouse
.isMouseWheelUp()) {
173 if (mouse
.isMouseWheelDown()) {
178 if ((mouse
.getX() < getWidth() - 1)
179 && (mouse
.getY() < getHeight() - 1)
181 if (getVerticalValue() + mouse
.getY() < strings
.size()) {
182 selectedString
= getVerticalValue() + mouse
.getY();
183 dispatchSingleClick();
189 super.onMouseDown(mouse
);
193 * Handle mouse double click.
195 * @param mouse mouse double click event
198 public void onMouseDoubleClick(final TMouseEvent mouse
) {
199 if ((mouse
.getX() < getWidth() - 1)
200 && (mouse
.getY() < getHeight() - 1)
202 if (getVerticalValue() + mouse
.getY() < strings
.size()) {
203 selectedString
= getVerticalValue() + mouse
.getY();
210 super.onMouseDoubleClick(mouse
);
216 * @param keypress keystroke event
219 public void onKeypress(final TKeypressEvent keypress
) {
220 if (keypress
.equals(kbLeft
)) {
221 horizontalDecrement();
222 } else if (keypress
.equals(kbRight
)) {
223 horizontalIncrement();
224 } else if (keypress
.equals(kbUp
)) {
225 if (strings
.size() > 0) {
226 if (selectedString
>= 0) {
227 if (selectedString
> 0) {
228 if (selectedString
- getVerticalValue() == 0) {
234 selectedString
= strings
.size() - 1;
237 if (selectedString
>= 0) {
240 } else if (keypress
.equals(kbDown
)) {
241 if (strings
.size() > 0) {
242 if (selectedString
>= 0) {
243 if (selectedString
< strings
.size() - 1) {
245 if (selectedString
- getVerticalValue() == getHeight() - 1) {
253 if (selectedString
>= 0) {
256 } else if (keypress
.equals(kbPgUp
)) {
257 bigVerticalDecrement();
258 if (selectedString
>= 0) {
259 selectedString
-= getHeight() - 1;
260 if (selectedString
< 0) {
264 if (selectedString
>= 0) {
267 } else if (keypress
.equals(kbPgDn
)) {
268 bigVerticalIncrement();
269 if (selectedString
>= 0) {
270 selectedString
+= getHeight() - 1;
271 if (selectedString
> strings
.size() - 1) {
272 selectedString
= strings
.size() - 1;
275 if (selectedString
>= 0) {
278 } else if (keypress
.equals(kbHome
)) {
280 if (strings
.size() > 0) {
283 if (selectedString
>= 0) {
286 } else if (keypress
.equals(kbEnd
)) {
288 if (strings
.size() > 0) {
289 selectedString
= strings
.size() - 1;
291 if (selectedString
>= 0) {
294 } else if (keypress
.equals(kbTab
)) {
295 getParent().switchWidget(true);
296 } else if (keypress
.equals(kbShiftTab
) || keypress
.equals(kbBackTab
)) {
297 getParent().switchWidget(false);
298 } else if (keypress
.equals(kbEnter
)) {
299 if (selectedString
>= 0) {
303 // Pass other keys (tab etc.) on
304 super.onKeypress(keypress
);
308 // ------------------------------------------------------------------------
309 // TScrollableWidget ------------------------------------------------------
310 // ------------------------------------------------------------------------
313 * Resize for a new width/height.
316 public void reflowData() {
322 for (int i
= 0; i
< strings
.size(); i
++) {
323 String line
= strings
.get(i
);
324 if (line
.length() > maxLineWidth
) {
325 maxLineWidth
= line
.length();
329 setBottomValue(strings
.size() - getHeight() + 1);
330 if (getBottomValue() < 0) {
334 setRightValue(maxLineWidth
- getWidth() + 1);
335 if (getRightValue() < 0) {
345 CellAttributes color
= null;
346 int begin
= getVerticalValue();
348 for (int i
= begin
; i
< strings
.size(); i
++) {
349 String line
= strings
.get(i
);
350 if (getHorizontalValue() < line
.length()) {
351 line
= line
.substring(getHorizontalValue());
355 if (i
== selectedString
) {
356 if (isAbsoluteActive()) {
357 color
= getTheme().getColor("tlist.selected");
359 color
= getTheme().getColor("tlist.selected.inactive");
361 } else if (isAbsoluteActive()) {
362 color
= getTheme().getColor("tlist");
364 color
= getTheme().getColor("tlist.inactive");
366 String formatString
= "%-" + Integer
.toString(getWidth() - 1) + "s";
367 putStringXY(0, topY
, String
.format(formatString
, line
), color
);
369 if (topY
>= getHeight() - 1) {
374 if (isAbsoluteActive()) {
375 color
= getTheme().getColor("tlist");
377 color
= getTheme().getColor("tlist.inactive");
380 // Pad the rest with blank lines
381 for (int i
= topY
; i
< getHeight() - 1; i
++) {
382 hLineXY(0, i
, getWidth() - 1, ' ', color
);
386 // ------------------------------------------------------------------------
387 // TList ------------------------------------------------------------------
388 // ------------------------------------------------------------------------
391 * Get the selection index.
393 * @return -1 if nothing is selected, otherwise the index into the list
395 public final int getSelectedIndex() {
396 return selectedString
;
400 * Set the selected string index.
402 * @param index -1 to unselect, otherwise the index into the list
404 public final void setSelectedIndex(final int index
) {
405 selectedString
= index
;
409 * Get a selectable string by index.
411 * @param idx index into list
412 * @return the string at idx in the list
414 public final String
getListItem(final int idx
) {
415 return strings
.get(idx
);
419 * Get the selected string.
421 * @return the selected string, or null of nothing is selected yet
423 public final String
getSelected() {
424 if ((selectedString
>= 0) && (selectedString
<= strings
.size() - 1)) {
425 return strings
.get(selectedString
);
431 * Get the maximum selection index value.
433 * @return -1 if the list is empty
435 public final int getMaxSelectedIndex() {
436 return strings
.size() - 1;
440 * Get a copy of the list of strings to display.
442 * @return the list of strings
444 public final List
<String
> getList() {
445 return new ArrayList
<String
>(strings
);
449 * Set the new list of strings to display.
451 * @param list new list of strings
453 public final void setList(final List
<String
> list
) {
455 strings
.addAll(list
);
460 * Perform user selection action.
462 public void dispatchEnter() {
463 assert (selectedString
>= 0);
464 assert (selectedString
< strings
.size());
465 if (enterAction
!= null) {
471 * Perform list movement action.
473 public void dispatchMove() {
474 assert (selectedString
>= 0);
475 assert (selectedString
< strings
.size());
476 if (moveAction
!= null) {
482 * Perform single-click action.
484 public void dispatchSingleClick() {
485 assert (selectedString
>= 0);
486 assert (selectedString
< strings
.size());
487 if (singleClickAction
!= null) {
488 singleClickAction
.DO();