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]
32 import jexer
.THScroller
;
33 import jexer
.TKeypress
;
34 import jexer
.TScrollableWidget
;
35 import jexer
.TVScroller
;
37 import jexer
.bits
.StringUtils
;
38 import jexer
.event
.TKeypressEvent
;
39 import jexer
.event
.TMouseEvent
;
40 import jexer
.event
.TResizeEvent
;
41 import static jexer
.TKeypress
.*;
44 * TTreeViewWidget wraps a tree view with horizontal and vertical scrollbars.
46 public class TTreeViewWidget
extends TScrollableWidget
{
48 // ------------------------------------------------------------------------
49 // Variables --------------------------------------------------------------
50 // ------------------------------------------------------------------------
55 private TTreeView treeView
;
58 * If true, move the window to put the selected item in view. This
59 * normally only happens once after setting treeRoot.
61 private boolean centerWindow
= false;
64 * Maximum width of a single line.
66 private int maxLineWidth
;
68 // ------------------------------------------------------------------------
69 // Constructors -----------------------------------------------------------
70 // ------------------------------------------------------------------------
75 * @param parent parent widget
76 * @param x column relative to parent
77 * @param y row relative to parent
78 * @param width width of tree view
79 * @param height height of tree view
81 public TTreeViewWidget(final TWidget parent
, final int x
, final int y
,
82 final int width
, final int height
) {
84 this(parent
, x
, y
, width
, height
, null);
90 * @param parent parent widget
91 * @param x column relative to parent
92 * @param y row relative to parent
93 * @param width width of tree view
94 * @param height height of tree view
95 * @param action action to perform when an item is selected
97 public TTreeViewWidget(final TWidget parent
, final int x
, final int y
,
98 final int width
, final int height
, final TAction action
) {
100 super(parent
, x
, y
, width
, height
);
102 treeView
= new TTreeView(this, 0, 0, getWidth() - 1, getHeight() - 1,
105 vScroller
= new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
106 hScroller
= new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
110 // ------------------------------------------------------------------------
111 // Event handlers ---------------------------------------------------------
112 // ------------------------------------------------------------------------
115 * Handle window/screen resize events.
117 * @param event resize event
120 public void onResize(final TResizeEvent event
) {
121 super.onResize(event
);
123 if (event
.getType() == TResizeEvent
.Type
.WIDGET
) {
124 treeView
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
125 getWidth() - 1, getHeight() - 1));
128 super.onResize(event
);
133 * Handle mouse press events.
135 * @param mouse mouse button press event
138 public void onMouseDown(final TMouseEvent mouse
) {
139 if (mouse
.isMouseWheelUp()) {
141 } else if (mouse
.isMouseWheelDown()) {
144 // Pass to the TreeView or scrollbars
145 super.onMouseDown(mouse
);
148 // Update the view to reflect the new scrollbar positions
149 treeView
.setTopLine(getVerticalValue());
150 treeView
.setLeftColumn(getHorizontalValue());
155 * Handle mouse release events.
157 * @param mouse mouse button release event
160 public void onMouseUp(final TMouseEvent mouse
) {
161 // Pass to the TreeView or scrollbars
162 super.onMouseUp(mouse
);
164 // Update the view to reflect the new scrollbar positions
165 treeView
.setTopLine(getVerticalValue());
166 treeView
.setLeftColumn(getHorizontalValue());
171 * Handle mouse motion events.
173 * @param mouse mouse motion event
176 public void onMouseMotion(final TMouseEvent mouse
) {
177 // Pass to the TreeView or scrollbars
178 super.onMouseMotion(mouse
);
180 // Update the view to reflect the new scrollbar positions
181 treeView
.setTopLine(getVerticalValue());
182 treeView
.setLeftColumn(getHorizontalValue());
189 * @param keypress keystroke event
192 public void onKeypress(final TKeypressEvent keypress
) {
193 if (keypress
.equals(kbShiftLeft
)
194 || keypress
.equals(kbCtrlLeft
)
195 || keypress
.equals(kbAltLeft
)
197 horizontalDecrement();
198 } else if (keypress
.equals(kbShiftRight
)
199 || keypress
.equals(kbCtrlRight
)
200 || keypress
.equals(kbAltRight
)
202 horizontalIncrement();
203 } else if (keypress
.equals(kbShiftUp
)
204 || keypress
.equals(kbCtrlUp
)
205 || keypress
.equals(kbAltUp
)
208 } else if (keypress
.equals(kbShiftDown
)
209 || keypress
.equals(kbCtrlDown
)
210 || keypress
.equals(kbAltDown
)
213 } else if (keypress
.equals(kbShiftPgUp
)
214 || keypress
.equals(kbCtrlPgUp
)
215 || keypress
.equals(kbAltPgUp
)
217 bigVerticalDecrement();
218 } else if (keypress
.equals(kbShiftPgDn
)
219 || keypress
.equals(kbCtrlPgDn
)
220 || keypress
.equals(kbAltPgDn
)
222 bigVerticalIncrement();
223 } else if (keypress
.equals(kbPgDn
)) {
224 for (int i
= 0; i
< getHeight() - 2; i
++) {
225 treeView
.onKeypress(new TKeypressEvent(TKeypress
.kbDown
));
229 } else if (keypress
.equals(kbPgUp
)) {
230 for (int i
= 0; i
< getHeight() - 2; i
++) {
231 treeView
.onKeypress(new TKeypressEvent(TKeypress
.kbUp
));
235 } else if (keypress
.equals(kbHome
)) {
236 treeView
.setSelected((TTreeItem
) treeView
.getChildren().get(0),
238 treeView
.setTopLine(0);
241 } else if (keypress
.equals(kbEnd
)) {
242 treeView
.setSelected((TTreeItem
) treeView
.getChildren().get(
243 treeView
.getChildren().size() - 1), true);
246 } else if (keypress
.equals(kbTab
)) {
247 getParent().switchWidget(true);
249 } else if (keypress
.equals(kbShiftTab
)
250 || keypress
.equals(kbBackTab
)) {
251 getParent().switchWidget(false);
254 treeView
.onKeypress(keypress
);
256 // Update the scrollbars to reflect the new data position
261 // Update the view to reflect the new scrollbar position
262 treeView
.setTopLine(getVerticalValue());
263 treeView
.setLeftColumn(getHorizontalValue());
267 // ------------------------------------------------------------------------
268 // TScrollableWidget ------------------------------------------------------
269 // ------------------------------------------------------------------------
272 * Override TWidget's width: we need to set child widget widths.
274 * @param width new widget width
277 public void setWidth(final int width
) {
278 super.setWidth(width
);
279 if (hScroller
!= null) {
280 hScroller
.setWidth(getWidth() - 1);
282 if (vScroller
!= null) {
283 vScroller
.setX(getWidth() - 1);
285 if (treeView
!= null) {
286 treeView
.setWidth(getWidth() - 1);
292 * Override TWidget's height: we need to set child widget heights.
294 * @param height new widget height
297 public void setHeight(final int height
) {
298 super.setHeight(height
);
299 if (hScroller
!= null) {
300 hScroller
.setY(getHeight() - 1);
302 if (vScroller
!= null) {
303 vScroller
.setHeight(getHeight() - 1);
305 if (treeView
!= null) {
306 treeView
.setHeight(getHeight() - 1);
312 * Resize text and scrollbars for a new width/height.
315 public void reflowData() {
316 if (treeView
== null) {
321 boolean foundSelectedRow
= false;
323 // Reset the keyboard list, expandTree() will recreate it.
324 for (TWidget widget
: treeView
.getChildren()) {
325 TTreeItem item
= (TTreeItem
) widget
;
326 item
.keyboardPrevious
= null;
327 item
.keyboardNext
= null;
330 // Expand the tree into a linear list
331 treeView
.getChildren().clear();
332 treeView
.getChildren().addAll(treeView
.getTreeRoot().expandTree("",
335 // Locate the selected row and maximum line width
336 for (TWidget widget
: treeView
.getChildren()) {
337 TTreeItem item
= (TTreeItem
) widget
;
339 if (item
== treeView
.getSelected()) {
340 foundSelectedRow
= true;
342 if (!foundSelectedRow
) {
346 int lineWidth
= StringUtils
.width(item
.getText())
347 + item
.getPrefix().length() + 4;
348 if (lineWidth
> maxLineWidth
) {
349 maxLineWidth
= lineWidth
;
353 if ((centerWindow
) && (foundSelectedRow
)) {
354 if ((selectedRow
< getVerticalValue())
355 || (selectedRow
> getVerticalValue() + getHeight() - 2)
357 treeView
.setTopLine(selectedRow
);
358 centerWindow
= false;
361 treeView
.alignTree();
363 // Rescale the scroll bars
364 setVerticalValue(treeView
.getTopLine());
365 setBottomValue(treeView
.getTotalLineCount() - (getHeight() - 1));
366 if (getBottomValue() < getTopValue()) {
367 setBottomValue(getTopValue());
369 if (getVerticalValue() > getBottomValue()) {
370 setVerticalValue(getBottomValue());
372 setRightValue(maxLineWidth
- 2);
373 if (getHorizontalValue() > getRightValue()) {
374 setHorizontalValue(getRightValue());
379 // ------------------------------------------------------------------------
380 // TTreeView --------------------------------------------------------------
381 // ------------------------------------------------------------------------
384 * Get the underlying TTreeView.
386 * @return the TTreeView
388 public TTreeView
getTreeView() {
393 * Get the root of the tree.
395 * @return the root of the tree
397 public final TTreeItem
getTreeRoot() {
398 return treeView
.getTreeRoot();
402 * Set the root of the tree.
404 * @param treeRoot the new root of the tree
406 public final void setTreeRoot(final TTreeItem treeRoot
) {
407 treeView
.setTreeRoot(treeRoot
);
413 * @param treeRoot ultimate root of tree
414 * @param centerWindow if true, move the window to put the root in view
416 public void setTreeRoot(final TTreeItem treeRoot
,
417 final boolean centerWindow
) {
419 treeView
.setTreeRoot(treeRoot
);
420 this.centerWindow
= centerWindow
;
424 * Get the tree view item that was selected.
426 * @return the selected item, or null if no item is selected
428 public final TTreeItem
getSelected() {
429 return treeView
.getSelected();
433 * Set the new selected tree view item.
435 * @param item new item that became selected
436 * @param centerWindow if true, move the window to put the selected into
439 public void setSelected(final TTreeItem item
, final boolean centerWindow
) {
440 treeView
.setSelected(item
, centerWindow
);
444 * Perform user selection action.
446 public void dispatch() {