a6855ba83c44088bda82fc68737c5742d4d8bbfc
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
.event
.TKeypressEvent
;
34 import jexer
.event
.TMouseEvent
;
35 import static jexer
.TKeypress
.*;
38 * TTreeView implements a simple tree view.
40 public class TTreeView
extends TWidget
{
45 private TVScroller vScroller
;
48 * Horizontal scrollbar.
50 private THScroller hScroller
;
53 * Get the horizontal scrollbar. This is used by TTreeItem.draw(), and
54 * potentially subclasses.
56 public final THScroller
getHScroller() {
63 private TTreeItem treeRoot
;
66 * Get the root of the tree.
68 * @return the root of the tree
70 public final TTreeItem
getTreeRoot() {
75 * Set the root of the tree.
77 * @param treeRoot the new root of the tree
79 public final void setTreeRoot(final TTreeItem treeRoot
) {
80 this.treeRoot
= treeRoot
;
84 * Maximum width of a single line.
86 private int maxLineWidth
;
89 * Only one of my children can be selected.
91 private TTreeItem selectedItem
= null;
94 * If true, move the window to put the selected item in view. This
95 * normally only happens once after setting treeRoot.
97 public boolean centerWindow
= false;
100 * The action to perform when the user selects an item.
102 private TAction action
= null;
107 * @param treeRoot ultimate root of tree
108 * @param centerWindow if true, move the window to put the root in view
110 public void setTreeRoot(final TTreeItem treeRoot
, final boolean centerWindow
) {
111 this.treeRoot
= treeRoot
;
112 this.centerWindow
= centerWindow
;
116 * Public constructor.
118 * @param parent parent widget
119 * @param x column relative to parent
120 * @param y row relative to parent
121 * @param width width of tree view
122 * @param height height of tree view
124 public TTreeView(final TWidget parent
, final int x
, final int y
,
125 final int width
, final int height
) {
127 this(parent
, x
, y
, width
, height
, null);
131 * Public constructor.
133 * @param parent parent widget
134 * @param x column relative to parent
135 * @param y row relative to parent
136 * @param width width of tree view
137 * @param height height of tree view
138 * @param action action to perform when an item is selected
140 public TTreeView(final TWidget parent
, final int x
, final int y
,
141 final int width
, final int height
, final TAction action
) {
143 super(parent
, x
, y
, width
, height
);
144 this.action
= action
;
148 * Get the tree view item that was selected.
150 * @return the selected item, or null if no item is selected
152 public final TTreeItem
getSelected() {
157 * Set the new selected tree view item.
159 * @param item new item that became selected
161 public void setSelected(final TTreeItem item
) {
163 item
.setSelected(true);
165 if ((selectedItem
!= null) && (selectedItem
!= item
)) {
166 selectedItem
.setSelected(false);
172 * Perform user selection action.
174 public void dispatch() {
175 if (action
!= null) {
181 * Update (or instantiate) vScroller and hScroller.
183 private void updateScrollers() {
184 // Setup vertical scroller
185 if (vScroller
== null) {
186 vScroller
= new TVScroller(this, getWidth() - 1, 0,
188 vScroller
.setValue(0);
189 vScroller
.setTopValue(0);
191 vScroller
.setX(getWidth() - 1);
192 vScroller
.setHeight(getHeight() - 1);
193 vScroller
.setBigChange(getHeight() - 1);
195 // Setup horizontal scroller
196 if (hScroller
== null) {
197 hScroller
= new THScroller(this, 0, getHeight() - 1,
199 hScroller
.setValue(0);
200 hScroller
.setLeftValue(0);
202 hScroller
.setY(getHeight() - 1);
203 hScroller
.setWidth(getWidth() - 1);
204 hScroller
.setBigChange(getWidth() - 1);
208 * Resize text and scrollbars for a new width/height.
210 public void reflow() {
212 boolean foundSelectedRow
= false;
215 if (treeRoot
== null) {
219 // Make each child invisible/inactive to start, expandTree() will
220 // reactivate the visible ones.
221 for (TWidget widget
: getChildren()) {
222 if (widget
instanceof TTreeItem
) {
223 TTreeItem item
= (TTreeItem
) widget
;
224 item
.setInvisible(true);
225 item
.setEnabled(false);
226 item
.keyboardPrevious
= null;
227 item
.keyboardNext
= null;
231 // Expand the tree into a linear list
232 getChildren().clear();
233 getChildren().addAll(treeRoot
.expandTree("", true));
235 // Locate the selected row and maximum line width
236 for (TWidget widget
: getChildren()) {
237 TTreeItem item
= (TTreeItem
) widget
;
239 if (item
== selectedItem
) {
240 foundSelectedRow
= true;
242 if (foundSelectedRow
== false) {
246 int lineWidth
= item
.getText().length()
247 + item
.getPrefix().length() + 4;
248 if (lineWidth
> maxLineWidth
) {
249 maxLineWidth
= lineWidth
;
253 if ((centerWindow
) && (foundSelectedRow
)) {
254 if ((selectedRow
< vScroller
.getValue())
255 || (selectedRow
> vScroller
.getValue() + getHeight() - 2)
257 vScroller
.setValue(selectedRow
);
258 centerWindow
= false;
263 // Rescale the scroll bars
264 vScroller
.setBottomValue(getChildren().size() - getHeight() + 1);
265 if (vScroller
.getBottomValue() < 0) {
266 vScroller
.setBottomValue(0);
269 if (vScroller.getValue() > vScroller.getBottomValue()) {
270 vScroller.setValue(vScroller.getBottomValue());
273 hScroller
.setRightValue(maxLineWidth
- getWidth() + 3);
274 if (hScroller
.getRightValue() < 0) {
275 hScroller
.setRightValue(0);
278 if (hScroller.getValue() > hScroller.getRightValue()) {
279 hScroller.setValue(hScroller.getRightValue());
282 getChildren().add(hScroller
);
283 getChildren().add(vScroller
);
287 * Update the Y positions of all the children items.
289 private void updatePositions() {
290 if (treeRoot
== null) {
294 int begin
= vScroller
.getValue();
297 // As we walk the list we also adjust next/previous pointers,
298 // resulting in a doubly-linked list but only of the expanded items.
301 for (int i
= 0; i
< getChildren().size(); i
++) {
302 if (!(getChildren().get(i
) instanceof TTreeItem
)) {
303 // Skip the scrollbars
306 TTreeItem item
= (TTreeItem
) getChildren().get(i
);
309 item
.keyboardPrevious
= p
;
310 p
.keyboardNext
= item
;
316 item
.setEnabled(false);
317 item
.setInvisible(true);
321 if (topY
>= getHeight() - 1) {
323 item
.setEnabled(false);
324 item
.setInvisible(true);
329 item
.setEnabled(true);
330 item
.setInvisible(false);
331 item
.setWidth(getWidth() - 1);
338 * Handle mouse press events.
340 * @param mouse mouse button press event
343 public void onMouseDown(final TMouseEvent mouse
) {
344 if (mouse
.isMouseWheelUp()) {
345 vScroller
.decrement();
346 } else if (mouse
.isMouseWheelDown()) {
347 vScroller
.increment();
350 super.onMouseDown(mouse
);
353 // Update the screen after the scrollbars have moved
358 * Handle mouse release events.
360 * @param mouse mouse button release event
363 public void onMouseUp(TMouseEvent mouse
) {
365 super.onMouseDown(mouse
);
367 // Update the screen after any thing has expanded/contracted
374 * @param keypress keystroke event
377 public void onKeypress(final TKeypressEvent keypress
) {
378 if (keypress
.equals(kbShiftLeft
)
379 || keypress
.equals(kbCtrlLeft
)
380 || keypress
.equals(kbAltLeft
)
382 hScroller
.decrement();
383 } else if (keypress
.equals(kbShiftRight
)
384 || keypress
.equals(kbCtrlRight
)
385 || keypress
.equals(kbAltRight
)
387 hScroller
.increment();
388 } else if (keypress
.equals(kbShiftUp
)
389 || keypress
.equals(kbCtrlUp
)
390 || keypress
.equals(kbAltUp
)
392 vScroller
.decrement();
393 } else if (keypress
.equals(kbShiftDown
)
394 || keypress
.equals(kbCtrlDown
)
395 || keypress
.equals(kbAltDown
)
397 vScroller
.increment();
398 } else if (keypress
.equals(kbShiftPgUp
)
399 || keypress
.equals(kbCtrlPgUp
)
400 || keypress
.equals(kbAltPgUp
)
402 vScroller
.bigDecrement();
403 } else if (keypress
.equals(kbShiftPgDn
)
404 || keypress
.equals(kbCtrlPgDn
)
405 || keypress
.equals(kbAltPgDn
)
407 vScroller
.bigIncrement();
408 } else if (keypress
.equals(kbHome
)) {
410 } else if (keypress
.equals(kbEnd
)) {
411 vScroller
.toBottom();
412 } else if (keypress
.equals(kbEnter
)) {
413 if (selectedItem
!= null) {
416 } else if (keypress
.equals(kbUp
)) {
417 // Select the previous item
418 if (selectedItem
!= null) {
419 TTreeItem oldItem
= selectedItem
;
420 if (selectedItem
.keyboardPrevious
!= null) {
421 setSelected(selectedItem
.keyboardPrevious
);
422 if (oldItem
.getY() == 0) {
423 vScroller
.decrement();
427 } else if (keypress
.equals(kbDown
)) {
428 // Select the next item
429 if (selectedItem
!= null) {
430 TTreeItem oldItem
= selectedItem
;
431 if (selectedItem
.keyboardNext
!= null) {
432 setSelected(selectedItem
.keyboardNext
);
433 if (oldItem
.getY() == getHeight() - 2) {
434 vScroller
.increment();
438 } else if (selectedItem
!= null) {
439 // Give the TTreeItem a chance to handle arrow keys
440 selectedItem
.onKeypress(keypress
);
442 // Pass other keys (tab etc.) on to TWidget's handler.
443 super.onKeypress(keypress
);
446 // Update the screen after any thing has expanded/contracted