2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 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
.GraphicsChars
;
36 import jexer
.event
.TKeypressEvent
;
37 import jexer
.event
.TMouseEvent
;
38 import static jexer
.TKeypress
.*;
41 * TTreeItem is a single item in a tree view.
43 public class TTreeItem
extends TWidget
{
46 * Hang onto reference to my parent TTreeView so I can call its reflow()
47 * when I add a child node.
49 private TTreeView view
;
52 * Get the parent TTreeView.
54 * @return the parent TTreeView
56 public final TTreeView
getTreeView() {
61 * Displayable text for this item.
66 * Get the displayable text for this item.
68 * @return the displayable text for this item
70 public final String
getText() {
75 * Set the displayable text for this item.
77 * @param text the displayable text for this item
79 public final void setText(final String text
) {
84 * If true, this item is expanded in the tree view.
86 private boolean expanded
= true;
91 * @return if true, this item is expanded
93 public final boolean isExpanded() {
100 * @param expanded new value
102 public final void setExpanded(final boolean expanded
) {
103 this.expanded
= expanded
;
107 * If true, this item can be expanded in the tree view.
109 private boolean expandable
= false;
112 * Get expandable value.
114 * @return if true, this item is expandable
116 public final boolean isExpandable() {
121 * Set expandable value.
123 * @param expandable new value
125 public final void setExpandable(final boolean expandable
) {
126 this.expandable
= expandable
;
130 * The vertical bars and such along the left side.
132 private String prefix
= "";
135 * Get the vertical bars and such along the left side.
137 * @return the vertical bars and such along the left side
139 public final String
getPrefix() {
144 * Whether or not this item is last in its parent's list of children.
146 private boolean last
= false;
149 * Tree level. Note package private access.
154 * If true, this item will not be drawn.
156 private boolean invisible
= false;
159 * Set invisible value.
161 * @param invisible new value
163 public final void setInvisible(final boolean invisible
) {
164 this.invisible
= invisible
;
168 * True means selected.
170 private boolean selected
= false;
173 * Get selected value.
175 * @return if true, this item is selected
177 public final boolean isSelected() {
182 * Set selected value.
184 * @param selected new value
186 public final void setSelected(final boolean selected
) {
187 this.selected
= selected
;
191 * True means select-able.
193 private boolean selectable
= true;
196 * Set selectable value.
198 * @param selectable new value
200 public final void setSelectable(final boolean selectable
) {
201 this.selectable
= selectable
;
205 * Pointer to the previous keyboard-navigable item (kbUp). Note package
208 TTreeItem keyboardPrevious
= null;
211 * Pointer to the next keyboard-navigable item (kbDown). Note package
214 TTreeItem keyboardNext
= null;
217 * Public constructor.
219 * @param view root TTreeView
220 * @param text text for this item
221 * @param expanded if true, have it expanded immediately
223 public TTreeItem(final TTreeView view
, final String text
,
224 final boolean expanded
) {
226 super(view
, 0, 0, view
.getWidth() - 3, 1);
228 this.expanded
= expanded
;
231 if (view
.getTreeRoot() == null) {
232 view
.setTreeRoot(this, true);
241 * @param text text for this item
242 * @return the new child item
244 public TTreeItem
addChild(final String text
) {
245 return addChild(text
, true);
251 * @param text text for this item
252 * @param expanded if true, have it expanded immediately
253 * @return the new child item
255 public TTreeItem
addChild(final String text
, final boolean expanded
) {
256 TTreeItem item
= new TTreeItem(view
, text
, expanded
);
257 item
.level
= this.level
+ 1;
258 getChildren().add(item
);
264 * Recursively expand the tree into a linear array of items.
266 * @param prefix vertical bar of parent levels and such that is set on
268 * @param last if true, this is the "last" leaf node of a tree
269 * @return additional items to add to the array
271 public List
<TTreeItem
> expandTree(final String prefix
, final boolean last
) {
272 List
<TTreeItem
> array
= new ArrayList
<TTreeItem
>();
274 this.prefix
= prefix
;
277 if ((getChildren().size() == 0) || !expanded
) {
281 String newPrefix
= prefix
;
286 newPrefix
+= GraphicsChars
.CP437
[0xB3];
290 for (int i
= 0; i
< getChildren().size(); i
++) {
291 TTreeItem item
= (TTreeItem
) getChildren().get(i
);
292 if (i
== getChildren().size() - 1) {
293 array
.addAll(item
.expandTree(newPrefix
, true));
295 array
.addAll(item
.expandTree(newPrefix
, false));
302 * Get the x spot for the + or - to expand/collapse.
304 * @return column of the expand/collapse button
306 private int getExpanderX() {
307 if ((level
== 0) || (!expandable
)) {
310 return prefix
.length() + 3;
314 * Recursively unselect my or my children.
316 public void unselect() {
317 if (selected
== true) {
319 view
.setSelected(null);
321 for (TWidget widget
: getChildren()) {
322 if (widget
instanceof TTreeItem
) {
323 TTreeItem item
= (TTreeItem
) widget
;
330 * Handle mouse release events.
332 * @param mouse mouse button release event
335 public void onMouseUp(final TMouseEvent mouse
) {
336 if ((mouse
.getX() == (getExpanderX() - view
.getHScroller().getValue()))
337 && (mouse
.getY() == 0)
340 // Flip expanded flag
341 expanded
= !expanded
;
342 if (expanded
== false) {
343 // Unselect children that became invisible
347 // Let subclasses do something with this
349 } else if (mouse
.getY() == 0) {
350 view
.setSelected(this);
354 // Update the screen after any thing has expanded/contracted
359 * Called when this item is expanded or collapsed. this.expanded will be
360 * true if this item was just expanded from a mouse click or keypress.
362 public void onExpand() {
363 // Default: do nothing.
372 * @param keypress keystroke event
375 public void onKeypress(final TKeypressEvent keypress
) {
376 if (keypress
.equals(kbLeft
)
377 || keypress
.equals(kbRight
)
378 || keypress
.equals(kbSpace
)
381 // Flip expanded flag
382 expanded
= !expanded
;
383 if (expanded
== false) {
384 // Unselect children that became invisible
387 view
.setSelected(this);
389 // Let subclasses do something with this
392 // Pass other keys (tab etc.) on to TWidget's handler.
393 super.onKeypress(keypress
);
398 * Draw this item to a window.
406 int offset
= -view
.getHScroller().getValue();
408 CellAttributes color
= getTheme().getColor("ttreeview");
409 CellAttributes textColor
= getTheme().getColor("ttreeview");
410 CellAttributes expanderColor
= getTheme().getColor("ttreeview.expandbutton");
411 CellAttributes selectedColor
= getTheme().getColor("ttreeview.selected");
413 if (!getParent().isAbsoluteActive()) {
414 color
= getTheme().getColor("ttreeview.inactive");
415 textColor
= getTheme().getColor("ttreeview.inactive");
419 textColor
= getTheme().getColor("ttreeview.unreadable");
422 // Blank out the background
423 getScreen().hLineXY(0, 0, getWidth(), ' ', color
);
425 String line
= prefix
;
428 line
+= GraphicsChars
.CP437
[0xC0];
430 line
+= GraphicsChars
.CP437
[0xC3];
432 line
+= GraphicsChars
.CP437
[0xC4];
437 getScreen().putStringXY(offset
, 0, line
, color
);
439 getScreen().putStringXY(offset
+ line
.length(), 0, text
,
442 getScreen().putStringXY(offset
+ line
.length(), 0, text
, textColor
);
444 if ((level
> 0) && (expandable
)) {
446 getScreen().putCharXY(offset
+ getExpanderX(), 0, '-',
449 getScreen().putCharXY(offset
+ getExpanderX(), 0, '+',