2 * Jexer - Java Text User Interface
4 * License: LGPLv3 or later
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.
10 * Copyright (C) 2015 Kevin Lamonte
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.
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.
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
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
33 import jexer
.bits
.CellAttributes
;
34 import jexer
.bits
.GraphicsChars
;
35 import jexer
.event
.TKeypressEvent
;
36 import jexer
.event
.TMouseEvent
;
37 import static jexer
.TKeypress
.*;
40 * TTreeView implements a simple tree view.
42 public class TTreeView
extends TWidget
{
47 private TVScroller vScroller
;
50 * Horizontal scrollbar. Note package private access.
57 private TTreeItem treeRoot
;
60 * Get the root of the tree.
62 * @return the root of the tree
64 public final TTreeItem
getTreeRoot() {
69 * Set the root of the tree.
71 * @param treeRoot the new root of the tree
73 public final void setTreeRoot(final TTreeItem treeRoot
) {
74 this.treeRoot
= treeRoot
;
78 * Maximum width of a single line.
80 private int maxLineWidth
;
83 * Only one of my children can be selected.
85 private TTreeItem selectedItem
= null;
88 * If true, move the window to put the selected item in view. This
89 * normally only happens once after setting treeRoot.
91 public boolean centerWindow
= false;
94 * The action to perform when the user selects an item.
96 private TAction action
= null;
101 * @param treeRoot ultimate root of tree
102 * @param centerWindow if true, move the window to put the root in view
104 public void setTreeRoot(final TTreeItem treeRoot
, final boolean centerWindow
) {
105 this.treeRoot
= treeRoot
;
106 this.centerWindow
= centerWindow
;
110 * Public constructor.
112 * @param parent parent widget
113 * @param x column relative to parent
114 * @param y row relative to parent
115 * @param width width of tree view
116 * @param height height of tree view
118 public TTreeView(final TWidget parent
, final int x
, final int y
,
119 final int width
, final int height
) {
121 this(parent
, x
, y
, width
, height
, null);
125 * Public constructor.
127 * @param parent parent widget
128 * @param x column relative to parent
129 * @param y row relative to parent
130 * @param width width of tree view
131 * @param height height of tree view
132 * @param action action to perform when an item is selected
134 public TTreeView(final TWidget parent
, final int x
, final int y
,
135 final int width
, final int height
, final TAction action
) {
137 super(parent
, x
, y
, width
, height
);
138 this.action
= action
;
142 * Get the tree view item that was selected.
144 * @return the selected item, or null if no item is selected
146 public final TTreeItem
getSelected() {
151 * Set the new selected tree view item. Note package private access.
153 * @param item new item that became selected
155 void setSelected(final TTreeItem item
) {
157 item
.setSelected(true);
159 if ((selectedItem
!= null) && (selectedItem
!= item
)) {
160 selectedItem
.setSelected(false);
166 * Perform user selection action. Note package private access.
169 if (action
!= null) {
175 * Update (or instantiate) vScroller and hScroller.
177 private void updateScrollers() {
178 // Setup vertical scroller
179 if (vScroller
== null) {
180 vScroller
= new TVScroller(this, getWidth() - 1, 0,
182 vScroller
.setValue(0);
183 vScroller
.setTopValue(0);
185 vScroller
.setX(getWidth() - 1);
186 vScroller
.setHeight(getHeight() - 1);
187 vScroller
.setBigChange(getHeight() - 1);
189 // Setup horizontal scroller
190 if (hScroller
== null) {
191 hScroller
= new THScroller(this, 0, getHeight() - 1,
193 hScroller
.setValue(0);
194 hScroller
.setLeftValue(0);
196 hScroller
.setY(getHeight() - 1);
197 hScroller
.setWidth(getWidth() - 1);
198 hScroller
.setBigChange(getWidth() - 1);
202 * Resize text and scrollbars for a new width/height.
204 public void reflow() {
206 boolean foundSelectedRow
= false;
209 if (treeRoot
== null) {
213 // Make each child invisible/inactive to start, expandTree() will
214 // reactivate the visible ones.
215 for (TWidget widget
: getChildren()) {
216 if (widget
instanceof TTreeItem
) {
217 TTreeItem item
= (TTreeItem
) widget
;
218 item
.setInvisible(true);
219 item
.setEnabled(false);
223 // Expand the tree into a linear list
224 getChildren().clear();
225 getChildren().addAll(treeRoot
.expandTree("", true));
226 for (TWidget widget
: getChildren()) {
227 TTreeItem item
= (TTreeItem
) widget
;
229 if (item
== selectedItem
) {
230 foundSelectedRow
= true;
232 if (foundSelectedRow
== false) {
236 int lineWidth
= item
.getText().length()
237 + item
.getPrefix().length() + 4;
238 if (lineWidth
> maxLineWidth
) {
239 maxLineWidth
= lineWidth
;
242 if ((centerWindow
) && (foundSelectedRow
)) {
243 if ((selectedRow
< vScroller
.getValue())
244 || (selectedRow
> vScroller
.getValue() + getHeight() - 2)
246 vScroller
.setValue(selectedRow
);
247 centerWindow
= false;
252 // Rescale the scroll bars
253 vScroller
.setBottomValue(getChildren().size() - getHeight() + 1);
254 if (vScroller
.getBottomValue() < 0) {
255 vScroller
.setBottomValue(0);
258 if (vScroller.getValue() > vScroller.getBottomValue()) {
259 vScroller.setValue(vScroller.getBottomValue());
262 hScroller
.setRightValue(maxLineWidth
- getWidth() + 3);
263 if (hScroller
.getRightValue() < 0) {
264 hScroller
.setRightValue(0);
267 if (hScroller.getValue() > hScroller.getRightValue()) {
268 hScroller.setValue(hScroller.getRightValue());
271 getChildren().add(hScroller
);
272 getChildren().add(vScroller
);
276 * Update the Y positions of all the children items.
278 private void updatePositions() {
279 if (treeRoot
== null) {
283 int begin
= vScroller
.getValue();
285 for (int i
= 0; i
< getChildren().size(); i
++) {
286 if (!(getChildren().get(i
) instanceof TTreeItem
)) {
290 TTreeItem item
= (TTreeItem
) getChildren().get(i
);
294 item
.setEnabled(false);
295 item
.setInvisible(true);
299 if (topY
>= getHeight() - 1) {
301 item
.setEnabled(false);
302 item
.setInvisible(true);
307 item
.setEnabled(true);
308 item
.setInvisible(false);
309 item
.setWidth(getWidth() - 1);
315 * Handle mouse press events.
317 * @param mouse mouse button press event
320 public void onMouseDown(final TMouseEvent mouse
) {
321 if (mouse
.isMouseWheelUp()) {
322 vScroller
.decrement();
323 } else if (mouse
.isMouseWheelDown()) {
324 vScroller
.increment();
327 super.onMouseDown(mouse
);
330 // Update the screen after the scrollbars have moved
335 * Handle mouse release events.
337 * @param mouse mouse button release event
340 public void onMouseUp(TMouseEvent mouse
) {
342 super.onMouseDown(mouse
);
344 // Update the screen after any thing has expanded/contracted
351 * @param keypress keystroke event
354 public void onKeypress(final TKeypressEvent keypress
) {
355 if (keypress
.equals(kbLeft
)) {
356 hScroller
.decrement();
357 } else if (keypress
.equals(kbRight
)) {
358 hScroller
.increment();
359 } else if (keypress
.equals(kbUp
)) {
360 vScroller
.decrement();
361 } else if (keypress
.equals(kbDown
)) {
362 vScroller
.increment();
363 } else if (keypress
.equals(kbPgUp
)) {
364 vScroller
.bigDecrement();
365 } else if (keypress
.equals(kbPgDn
)) {
366 vScroller
.bigIncrement();
367 } else if (keypress
.equals(kbHome
)) {
369 } else if (keypress
.equals(kbEnd
)) {
370 vScroller
.toBottom();
371 } else if (keypress
.equals(kbEnter
)) {
372 if (selectedItem
!= null) {
376 // Pass other keys (tab etc.) on
377 super.onKeypress(keypress
);
380 // Update the screen after any thing has expanded/contracted