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
;
35 import jexer
.bits
.CellAttributes
;
36 import jexer
.bits
.GraphicsChars
;
37 import jexer
.event
.TKeypressEvent
;
38 import jexer
.event
.TMouseEvent
;
39 import static jexer
.TKeypress
.*;
42 * TTreeItem is a single item in a tree view.
44 public class TTreeItem
extends TWidget
{
46 // ------------------------------------------------------------------------
47 // Variables --------------------------------------------------------------
48 // ------------------------------------------------------------------------
51 * Hang onto reference to my parent TTreeView so I can call its reflow()
52 * when I add a child node.
54 private TTreeView view
;
57 * Displayable text for this item.
62 * If true, this item is expanded in the tree view.
64 private boolean expanded
= true;
67 * If true, this item can be expanded in the tree view.
69 private boolean expandable
= false;
72 * The vertical bars and such along the left side.
74 private String prefix
= "";
79 protected int level
= 0;
82 * True means selected.
84 private boolean selected
= false;
87 * True means select-able.
89 private boolean selectable
= true;
92 * Whether or not this item is last in its parent's list of children.
94 private boolean last
= false;
97 * Pointer to the previous keyboard-navigable item (kbUp). Note package
100 TTreeItem keyboardPrevious
= null;
103 * Pointer to the next keyboard-navigable item (kbDown). Note package
106 TTreeItem keyboardNext
= null;
108 // ------------------------------------------------------------------------
109 // Constructors -----------------------------------------------------------
110 // ------------------------------------------------------------------------
113 * Public constructor.
115 * @param view root TTreeView
116 * @param text text for this item
117 * @param expanded if true, have it expanded immediately
119 public TTreeItem(final TTreeView view
, final String text
,
120 final boolean expanded
) {
122 super(view
, 0, 0, view
.getWidth() - 3, 1);
125 this.expanded
= expanded
;
128 if (view
.getTreeRoot() == null) {
129 view
.setTreeRoot(this);
135 // ------------------------------------------------------------------------
136 // Event handlers ---------------------------------------------------------
137 // ------------------------------------------------------------------------
140 * Handle mouse release events.
142 * @param mouse mouse button release event
145 public void onMouseUp(final TMouseEvent mouse
) {
146 if ((mouse
.getX() == (getExpanderX() - view
.getLeftColumn()))
147 && (mouse
.getY() == 0)
150 // Root node can't switch.
154 // Flip expanded flag
155 expanded
= !expanded
;
156 if (expanded
== false) {
157 // Unselect children that became invisible
160 view
.setSelected(this, false);
162 // Let subclasses do something with this
165 // Update the screen after any thing has expanded/contracted
167 } else if (mouse
.getY() == 0) {
168 // Do the action associated with this item.
169 view
.setSelected(this, false);
175 * Called when this item is expanded or collapsed. this.expanded will be
176 * true if this item was just expanded from a mouse click or keypress.
178 public void onExpand() {
179 // Default: do nothing.
188 * @param keypress keystroke event
191 public void onKeypress(final TKeypressEvent keypress
) {
192 if (keypress
.equals(kbLeft
)
193 || keypress
.equals(kbRight
)
194 || keypress
.equals(kbSpace
)
197 // Root node can't switch.
201 // Flip expanded flag
202 expanded
= !expanded
;
203 if (expanded
== false) {
204 // Unselect children that became invisible
207 view
.setSelected(this, false);
209 // Let subclasses do something with this
211 } else if (keypress
.equals(kbEnter
)) {
212 // Do the action associated with this item.
215 // Pass other keys (tab etc.) on to TWidget's handler.
216 super.onKeypress(keypress
);
220 // ------------------------------------------------------------------------
221 // TWidget ----------------------------------------------------------------
222 // ------------------------------------------------------------------------
225 * Draw this item to a window.
229 if ((getY() < 0) || (getY() > getParent().getHeight() - 1)) {
233 int offset
= -view
.getLeftColumn();
235 CellAttributes color
= getTheme().getColor("ttreeview");
236 CellAttributes textColor
= getTheme().getColor("ttreeview");
237 CellAttributes expanderColor
= getTheme().getColor("ttreeview.expandbutton");
238 CellAttributes selectedColor
= getTheme().getColor("ttreeview.selected");
240 if (!getParent().isAbsoluteActive()) {
241 color
= getTheme().getColor("ttreeview.inactive");
242 textColor
= getTheme().getColor("ttreeview.inactive");
243 selectedColor
= getTheme().getColor("ttreeview.selected.inactive");
247 textColor
= getTheme().getColor("ttreeview.unreadable");
250 // Blank out the background
251 getScreen().hLineXY(0, 0, getWidth(), ' ', color
);
253 String line
= prefix
;
256 line
+= GraphicsChars
.CP437
[0xC0];
258 line
+= GraphicsChars
.CP437
[0xC3];
260 line
+= GraphicsChars
.CP437
[0xC4];
267 getScreen().putStringXY(offset
, 0, line
, color
);
269 getScreen().putStringXY(offset
+ line
.length(), 0, text
,
272 getScreen().putStringXY(offset
+ line
.length(), 0, text
, textColor
);
274 if ((level
> 0) && (expandable
)) {
276 getScreen().putCharXY(offset
+ getExpanderX(), 0, '-',
279 getScreen().putCharXY(offset
+ getExpanderX(), 0, '+',
285 // ------------------------------------------------------------------------
286 // TTreeItem --------------------------------------------------------------
287 // ------------------------------------------------------------------------
290 * Get the parent TTreeView.
292 * @return the parent TTreeView
294 public final TTreeView
getTreeView() {
299 * Get the displayable text for this item.
301 * @return the displayable text for this item
303 public final String
getText() {
308 * Set the displayable text for this item.
310 * @param text the displayable text for this item
312 public final void setText(final String text
) {
317 * Get expanded value.
319 * @return if true, this item is expanded
321 public final boolean isExpanded() {
326 * Set expanded value.
328 * @param expanded new value
330 public final void setExpanded(final boolean expanded
) {
332 // Root node can't be unexpanded, ever.
333 this.expanded
= true;
337 this.expanded
= expanded
;
342 * Get expandable value.
344 * @return if true, this item is expandable
346 public final boolean isExpandable() {
351 * Set expandable value.
353 * @param expandable new value
355 public final void setExpandable(final boolean expandable
) {
357 // Root node can't be unexpanded, ever.
358 this.expandable
= true;
362 this.expandable
= expandable
;
367 * Get the vertical bars and such along the left side.
369 * @return the vertical bars and such along the left side
371 public final String
getPrefix() {
376 * Get selected value.
378 * @return if true, this item is selected
380 public final boolean isSelected() {
385 * Set selected value.
387 * @param selected new value
389 public final void setSelected(final boolean selected
) {
390 this.selected
= selected
;
394 * Set selectable value.
396 * @param selectable new value
398 public final void setSelectable(final boolean selectable
) {
399 this.selectable
= selectable
;
403 * Get the length of the widest item to display.
405 * @return the maximum number of columns for this item or its children
407 public int getMaximumColumn() {
408 int max
= prefix
.length() + 4 + text
.length();
409 for (TWidget widget
: getChildren()) {
410 TTreeItem item
= (TTreeItem
) widget
;
411 int n
= item
.prefix
.length() + 4 + item
.text
.length();
420 * Recursively expand the tree into a linear array of items.
422 * @param prefix vertical bar of parent levels and such that is set on
424 * @param last if true, this is the "last" leaf node of a tree
425 * @return additional items to add to the array
427 public List
<TTreeItem
> expandTree(final String prefix
, final boolean last
) {
428 List
<TTreeItem
> array
= new ArrayList
<TTreeItem
>();
430 this.prefix
= prefix
;
433 if ((getChildren().size() == 0) || !expanded
) {
437 String newPrefix
= prefix
;
442 newPrefix
+= GraphicsChars
.CP437
[0xB3];
446 for (int i
= 0; i
< getChildren().size(); i
++) {
447 TTreeItem item
= (TTreeItem
) getChildren().get(i
);
448 if (i
== getChildren().size() - 1) {
449 array
.addAll(item
.expandTree(newPrefix
, true));
451 array
.addAll(item
.expandTree(newPrefix
, false));
458 * Get the x spot for the + or - to expand/collapse.
460 * @return column of the expand/collapse button
462 private int getExpanderX() {
463 if ((level
== 0) || (!expandable
)) {
466 return prefix
.length() + 3;
470 * Recursively unselect me and my children.
472 public void unselect() {
473 if (selected
== true) {
475 view
.setSelected(null, false);
477 for (TWidget widget
: getChildren()) {
478 if (widget
instanceof TTreeItem
) {
479 TTreeItem item
= (TTreeItem
) widget
;