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
.bits
.StringUtils
;
36 import jexer
.event
.TKeypressEvent
;
37 import jexer
.event
.TMouseEvent
;
38 import static jexer
.TKeypress
.*;
41 * TList shows a list of strings, and lets the user select one.
43 public class TList
extends TScrollableWidget
{
45 // ------------------------------------------------------------------------
46 // Variables --------------------------------------------------------------
47 // ------------------------------------------------------------------------
50 * The list of strings to display.
52 private List
<String
> strings
;
57 private int selectedString
= -1;
60 * Maximum width of a single line.
62 private int maxLineWidth
;
65 * The action to perform when the user selects an item (double-clicks or
68 protected TAction enterAction
= null;
71 * The action to perform when the user selects an item (single-click).
73 protected TAction singleClickAction
= null;
76 * The action to perform when the user navigates with keyboard.
78 protected TAction moveAction
= null;
80 // ------------------------------------------------------------------------
81 // Constructors -----------------------------------------------------------
82 // ------------------------------------------------------------------------
87 * @param parent parent widget
88 * @param strings list of strings to show
89 * @param x column relative to parent
90 * @param y row relative to parent
91 * @param width width of text area
92 * @param height height of text area
94 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
95 final int y
, final int width
, final int height
) {
97 this(parent
, strings
, x
, y
, width
, height
, null, null, null);
101 * Public constructor.
103 * @param parent parent widget
104 * @param strings list of strings to show. This is allowed to be null
105 * and set later with setList() or by subclasses.
106 * @param x column relative to parent
107 * @param y row relative to parent
108 * @param width width of text area
109 * @param height height of text area
110 * @param enterAction action to perform when an item is selected
112 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
113 final int y
, final int width
, final int height
,
114 final TAction enterAction
) {
116 this(parent
, strings
, x
, y
, width
, height
, enterAction
, null, null);
120 * Public constructor.
122 * @param parent parent widget
123 * @param strings list of strings to show. This is allowed to be null
124 * and set later with setList() or by subclasses.
125 * @param x column relative to parent
126 * @param y row relative to parent
127 * @param width width of text area
128 * @param height height of text area
129 * @param enterAction action to perform when an item is selected
130 * @param moveAction action to perform when the user navigates to a new
131 * item with arrow/page keys
133 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
134 final int y
, final int width
, final int height
,
135 final TAction enterAction
, final TAction moveAction
) {
137 this(parent
, strings
, x
, y
, width
, height
, enterAction
, moveAction
,
142 * Public constructor.
144 * @param parent parent widget
145 * @param strings list of strings to show. This is allowed to be null
146 * and set later with setList() or by subclasses.
147 * @param x column relative to parent
148 * @param y row relative to parent
149 * @param width width of text area
150 * @param height height of text area
151 * @param enterAction action to perform when an item is selected
152 * @param moveAction action to perform when the user navigates to a new
153 * item with arrow/page keys
154 * @param singleClickAction action to perform when the user clicks on an
157 public TList(final TWidget parent
, final List
<String
> strings
, final int x
,
158 final int y
, final int width
, final int height
,
159 final TAction enterAction
, final TAction moveAction
,
160 final TAction singleClickAction
) {
162 super(parent
, x
, y
, width
, height
);
163 this.enterAction
= enterAction
;
164 this.moveAction
= moveAction
;
165 this.singleClickAction
= singleClickAction
;
166 this.strings
= new ArrayList
<String
>();
167 if (strings
!= null) {
168 this.strings
.addAll(strings
);
171 hScroller
= new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
172 vScroller
= new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
176 // ------------------------------------------------------------------------
177 // Event handlers ---------------------------------------------------------
178 // ------------------------------------------------------------------------
181 * Handle mouse press events.
183 * @param mouse mouse button press event
186 public void onMouseDown(final TMouseEvent mouse
) {
187 if (mouse
.isMouseWheelUp()) {
191 if (mouse
.isMouseWheelDown()) {
196 if ((mouse
.getX() < getWidth() - 1)
197 && (mouse
.getY() < getHeight() - 1)
199 if (getVerticalValue() + mouse
.getY() < strings
.size()) {
200 selectedString
= getVerticalValue() + mouse
.getY();
201 dispatchSingleClick();
207 super.onMouseDown(mouse
);
211 * Handle mouse double click.
213 * @param mouse mouse double click event
216 public void onMouseDoubleClick(final TMouseEvent mouse
) {
217 if ((mouse
.getX() < getWidth() - 1)
218 && (mouse
.getY() < getHeight() - 1)
220 if (getVerticalValue() + mouse
.getY() < strings
.size()) {
221 selectedString
= getVerticalValue() + mouse
.getY();
228 super.onMouseDoubleClick(mouse
);
234 * @param keypress keystroke event
237 public void onKeypress(final TKeypressEvent keypress
) {
238 if (keypress
.equals(kbLeft
)) {
239 horizontalDecrement();
240 } else if (keypress
.equals(kbRight
)) {
241 horizontalIncrement();
242 } else if (keypress
.equals(kbUp
)) {
243 if (strings
.size() > 0) {
244 if (selectedString
>= 0) {
245 if (selectedString
> 0) {
246 if (selectedString
- getVerticalValue() == 0) {
252 selectedString
= strings
.size() - 1;
255 if (selectedString
>= 0) {
258 } else if (keypress
.equals(kbDown
)) {
259 if (strings
.size() > 0) {
260 if (selectedString
>= 0) {
261 if (selectedString
< strings
.size() - 1) {
263 if (selectedString
- getVerticalValue() == getHeight() - 1) {
271 if (selectedString
>= 0) {
274 } else if (keypress
.equals(kbPgUp
)) {
275 bigVerticalDecrement();
276 if (selectedString
>= 0) {
277 selectedString
-= getHeight() - 1;
278 if (selectedString
< 0) {
282 if (selectedString
>= 0) {
285 } else if (keypress
.equals(kbPgDn
)) {
286 bigVerticalIncrement();
287 if (selectedString
>= 0) {
288 selectedString
+= getHeight() - 1;
289 if (selectedString
> strings
.size() - 1) {
290 selectedString
= strings
.size() - 1;
293 if (selectedString
>= 0) {
296 } else if (keypress
.equals(kbHome
)) {
298 if (strings
.size() > 0) {
301 if (selectedString
>= 0) {
304 } else if (keypress
.equals(kbEnd
)) {
306 if (strings
.size() > 0) {
307 selectedString
= strings
.size() - 1;
309 if (selectedString
>= 0) {
312 } else if (keypress
.equals(kbTab
)) {
313 getParent().switchWidget(true);
314 } else if (keypress
.equals(kbShiftTab
) || keypress
.equals(kbBackTab
)) {
315 getParent().switchWidget(false);
316 } else if (keypress
.equals(kbEnter
)) {
317 if (selectedString
>= 0) {
321 // Pass other keys (tab etc.) on
322 super.onKeypress(keypress
);
326 // ------------------------------------------------------------------------
327 // TScrollableWidget ------------------------------------------------------
328 // ------------------------------------------------------------------------
331 * Override TWidget's width: we need to set child widget widths.
333 * @param width new widget width
336 public void setWidth(final int width
) {
337 super.setWidth(width
);
338 if (hScroller
!= null) {
339 hScroller
.setWidth(getWidth() - 1);
341 if (vScroller
!= null) {
342 vScroller
.setX(getWidth() - 1);
347 * Override TWidget's height: we need to set child widget heights.
349 * @param height new widget height
352 public void setHeight(final int height
) {
353 super.setHeight(height
);
354 if (hScroller
!= null) {
355 hScroller
.setY(getHeight() - 1);
357 if (vScroller
!= null) {
358 vScroller
.setHeight(getHeight() - 1);
363 * Resize for a new width/height.
366 public void reflowData() {
372 for (int i
= 0; i
< strings
.size(); i
++) {
373 String line
= strings
.get(i
);
374 int lineLength
= StringUtils
.width(line
);
375 if (lineLength
> maxLineWidth
) {
376 maxLineWidth
= lineLength
;
380 setBottomValue(strings
.size() - getHeight() + 1);
381 if (getBottomValue() < 0) {
385 setRightValue(maxLineWidth
- getWidth() + 1);
386 if (getRightValue() < 0) {
396 CellAttributes color
= null;
397 int begin
= getVerticalValue();
399 for (int i
= begin
; i
< strings
.size(); i
++) {
400 String line
= strings
.get(i
);
404 if (getHorizontalValue() < line
.length()) {
405 line
= line
.substring(getHorizontalValue());
409 if (i
== selectedString
) {
410 if (isAbsoluteActive()) {
411 color
= getTheme().getColor("tlist.selected");
413 color
= getTheme().getColor("tlist.selected.inactive");
415 } else if (isAbsoluteActive()) {
416 color
= getTheme().getColor("tlist");
418 color
= getTheme().getColor("tlist.inactive");
420 String formatString
= "%-" + Integer
.toString(getWidth() - 1) + "s";
421 putStringXY(0, topY
, String
.format(formatString
, line
), color
);
423 if (topY
>= getHeight() - 1) {
428 if (isAbsoluteActive()) {
429 color
= getTheme().getColor("tlist");
431 color
= getTheme().getColor("tlist.inactive");
434 // Pad the rest with blank lines
435 for (int i
= topY
; i
< getHeight() - 1; i
++) {
436 hLineXY(0, i
, getWidth() - 1, ' ', color
);
440 // ------------------------------------------------------------------------
441 // TList ------------------------------------------------------------------
442 // ------------------------------------------------------------------------
445 * Get the selection index.
447 * @return -1 if nothing is selected, otherwise the index into the list
449 public final int getSelectedIndex() {
450 return selectedString
;
454 * Set the selected string index.
456 * @param index -1 to unselect, otherwise the index into the list
458 public final void setSelectedIndex(final int index
) {
459 selectedString
= index
;
463 * Get a selectable string by index.
465 * @param idx index into list
466 * @return the string at idx in the list
468 public final String
getListItem(final int idx
) {
469 return strings
.get(idx
);
473 * Get the selected string.
475 * @return the selected string, or null of nothing is selected yet
477 public final String
getSelected() {
478 if ((selectedString
>= 0) && (selectedString
<= strings
.size() - 1)) {
479 return strings
.get(selectedString
);
485 * Get the maximum selection index value.
487 * @return -1 if the list is empty
489 public final int getMaxSelectedIndex() {
490 return strings
.size() - 1;
494 * Get a copy of the list of strings to display.
496 * @return the list of strings
498 public final List
<String
> getList() {
499 return new ArrayList
<String
>(strings
);
503 * Set the new list of strings to display.
505 * @param list new list of strings
507 public final void setList(final List
<String
> list
) {
509 strings
.addAll(list
);
514 * Perform user selection action.
516 public void dispatchEnter() {
517 assert (selectedString
>= 0);
518 assert (selectedString
< strings
.size());
519 if (enterAction
!= null) {
520 enterAction
.DO(this);
525 * Perform list movement action.
527 public void dispatchMove() {
528 assert (selectedString
>= 0);
529 assert (selectedString
< strings
.size());
530 if (moveAction
!= null) {
536 * Perform single-click action.
538 public void dispatchSingleClick() {
539 assert (selectedString
>= 0);
540 assert (selectedString
< strings
.size());
541 if (singleClickAction
!= null) {
542 singleClickAction
.DO(this);