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]
31 import java
.util
.ArrayList
;
32 import java
.util
.List
;
35 import jexer
.bits
.CellAttributes
;
36 import jexer
.bits
.GraphicsChars
;
37 import jexer
.bits
.StringUtils
;
38 import jexer
.event
.TKeypressEvent
;
39 import jexer
.event
.TMouseEvent
;
40 import static jexer
.TKeypress
.*;
43 * TTreeItem is a single item in a tree view.
45 public class TTreeItem
extends TWidget
{
47 // ------------------------------------------------------------------------
48 // Variables --------------------------------------------------------------
49 // ------------------------------------------------------------------------
52 * Hang onto reference to my parent TTreeView so I can call its reflow()
53 * when I add a child node.
55 private TTreeView view
;
58 * Displayable text for this item.
63 * If true, this item is expanded in the tree view.
65 private boolean expanded
= true;
68 * If true, this item can be expanded in the tree view.
70 private boolean expandable
= false;
73 * The vertical bars and such along the left side.
75 private String prefix
= "";
80 protected int level
= 0;
83 * True means selected.
85 private boolean selected
= false;
88 * True means select-able.
90 private boolean selectable
= true;
93 * Whether or not this item is last in its parent's list of children.
95 private boolean last
= false;
98 * Pointer to the previous keyboard-navigable item (kbUp). Note package
101 TTreeItem keyboardPrevious
= null;
104 * Pointer to the next keyboard-navigable item (kbDown). Note package
107 TTreeItem keyboardNext
= null;
109 // ------------------------------------------------------------------------
110 // Constructors -----------------------------------------------------------
111 // ------------------------------------------------------------------------
114 * Public constructor.
116 * @param view root TTreeView
117 * @param text text for this item
118 * @param expanded if true, have it expanded immediately
120 public TTreeItem(final TTreeView view
, final String text
,
121 final boolean expanded
) {
123 super(view
, 0, 0, view
.getWidth() - 3, 1);
126 this.expanded
= expanded
;
129 if (view
.getTreeRoot() == null) {
130 view
.setTreeRoot(this);
136 // ------------------------------------------------------------------------
137 // Event handlers ---------------------------------------------------------
138 // ------------------------------------------------------------------------
141 * Handle mouse release events.
143 * @param mouse mouse button release event
146 public void onMouseUp(final TMouseEvent mouse
) {
147 if ((mouse
.getX() == (getExpanderX() - view
.getLeftColumn()))
148 && (mouse
.getY() == 0)
151 // Root node can't switch.
155 // Flip expanded flag
156 expanded
= !expanded
;
157 if (expanded
== false) {
158 // Unselect children that became invisible
161 view
.setSelected(this, false);
163 // Let subclasses do something with this
166 // Update the screen after any thing has expanded/contracted
168 } else if (mouse
.getY() == 0) {
169 // Do the action associated with this item.
170 view
.setSelected(this, false);
176 * Called when this item is expanded or collapsed. this.expanded will be
177 * true if this item was just expanded from a mouse click or keypress.
179 public void onExpand() {
180 // Default: do nothing.
189 * @param keypress keystroke event
192 public void onKeypress(final TKeypressEvent keypress
) {
193 if (keypress
.equals(kbLeft
)
194 || keypress
.equals(kbRight
)
195 || keypress
.equals(kbSpace
)
198 // Root node can't switch.
202 // Flip expanded flag
203 expanded
= !expanded
;
204 if (expanded
== false) {
205 // Unselect children that became invisible
208 view
.setSelected(this, false);
210 // Let subclasses do something with this
212 } else if (keypress
.equals(kbEnter
)) {
213 // Do the action associated with this item.
216 // Pass other keys (tab etc.) on to TWidget's handler.
217 super.onKeypress(keypress
);
221 // ------------------------------------------------------------------------
222 // TWidget ----------------------------------------------------------------
223 // ------------------------------------------------------------------------
226 * Draw this item to a window.
230 if ((getY() < 0) || (getY() > getParent().getHeight() - 1)) {
234 int offset
= -view
.getLeftColumn();
236 CellAttributes color
= getTheme().getColor("ttreeview");
237 CellAttributes textColor
= getTheme().getColor("ttreeview");
238 CellAttributes expanderColor
= getTheme().getColor("ttreeview.expandbutton");
239 CellAttributes selectedColor
= getTheme().getColor("ttreeview.selected");
241 if (!getParent().isAbsoluteActive()) {
242 color
= getTheme().getColor("ttreeview.inactive");
243 textColor
= getTheme().getColor("ttreeview.inactive");
244 selectedColor
= getTheme().getColor("ttreeview.selected.inactive");
248 textColor
= getTheme().getColor("ttreeview.unreadable");
251 // Blank out the background
252 hLineXY(0, 0, getWidth(), ' ', color
);
254 String line
= prefix
;
257 line
+= GraphicsChars
.CP437
[0xC0];
259 line
+= GraphicsChars
.CP437
[0xC3];
261 line
+= GraphicsChars
.CP437
[0xC4];
268 putStringXY(offset
, 0, line
, color
);
270 putStringXY(offset
+ StringUtils
.width(line
), 0, text
, selectedColor
);
272 putStringXY(offset
+ StringUtils
.width(line
), 0, text
, textColor
);
274 if ((level
> 0) && (expandable
)) {
276 putCharXY(offset
+ getExpanderX(), 0, '-', expanderColor
);
278 putCharXY(offset
+ getExpanderX(), 0, '+', expanderColor
);
283 // ------------------------------------------------------------------------
284 // TTreeItem --------------------------------------------------------------
285 // ------------------------------------------------------------------------
288 * Get the parent TTreeView.
290 * @return the parent TTreeView
292 public final TTreeView
getTreeView() {
297 * Get the displayable text for this item.
299 * @return the displayable text for this item
301 public final String
getText() {
306 * Set the displayable text for this item.
308 * @param text the displayable text for this item
310 public final void setText(final String text
) {
315 * Get expanded value.
317 * @return if true, this item is expanded
319 public final boolean isExpanded() {
324 * Set expanded value.
326 * @param expanded new value
328 public final void setExpanded(final boolean expanded
) {
330 // Root node can't be unexpanded, ever.
331 this.expanded
= true;
335 this.expanded
= expanded
;
340 * Get expandable value.
342 * @return if true, this item is expandable
344 public final boolean isExpandable() {
349 * Set expandable value.
351 * @param expandable new value
353 public final void setExpandable(final boolean expandable
) {
355 // Root node can't be unexpanded, ever.
356 this.expandable
= true;
360 this.expandable
= expandable
;
365 * Get the vertical bars and such along the left side.
367 * @return the vertical bars and such along the left side
369 public final String
getPrefix() {
374 * Get selected value.
376 * @return if true, this item is selected
378 public final boolean isSelected() {
383 * Set selected value.
385 * @param selected new value
387 public final void setSelected(final boolean selected
) {
388 this.selected
= selected
;
392 * Set selectable value.
394 * @param selectable new value
396 public final void setSelectable(final boolean selectable
) {
397 this.selectable
= selectable
;
401 * Get the length of the widest item to display.
403 * @return the maximum number of columns for this item or its children
405 public int getMaximumColumn() {
406 int max
= prefix
.length() + 4 + StringUtils
.width(text
);
407 for (TWidget widget
: getChildren()) {
408 TTreeItem item
= (TTreeItem
) widget
;
409 int n
= item
.prefix
.length() + 4 + StringUtils
.width(item
.text
);
418 * Recursively expand the tree into a linear array of items.
420 * @param prefix vertical bar of parent levels and such that is set on
422 * @param last if true, this is the "last" leaf node of a tree
423 * @return additional items to add to the array
425 public List
<TTreeItem
> expandTree(final String prefix
, final boolean last
) {
426 List
<TTreeItem
> array
= new ArrayList
<TTreeItem
>();
428 this.prefix
= prefix
;
431 if ((getChildren().size() == 0) || !expanded
) {
435 String newPrefix
= prefix
;
440 newPrefix
+= GraphicsChars
.CP437
[0xB3];
444 for (int i
= 0; i
< getChildren().size(); i
++) {
445 TTreeItem item
= (TTreeItem
) getChildren().get(i
);
446 if (i
== getChildren().size() - 1) {
447 array
.addAll(item
.expandTree(newPrefix
, true));
449 array
.addAll(item
.expandTree(newPrefix
, false));
456 * Get the x spot for the + or - to expand/collapse.
458 * @return column of the expand/collapse button
460 private int getExpanderX() {
461 if ((level
== 0) || (!expandable
)) {
464 return prefix
.length() + 3;
468 * Recursively unselect me and my children.
470 public void unselect() {
471 if (selected
== true) {
473 view
.setSelected(null, false);
475 for (TWidget widget
: getChildren()) {
476 if (widget
instanceof TTreeItem
) {
477 TTreeItem item
= (TTreeItem
) widget
;