Add 'src/jexer/' from commit 'cf01c92f5809a0732409e280fb0f32f27393618d'
[fanfix.git] / src / jexer / ttree / TTreeView.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
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.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer.ttree;
30
31 import jexer.TAction;
32 import jexer.TKeypress;
33 import jexer.TWidget;
34 import jexer.event.TKeypressEvent;
35 import static jexer.TKeypress.*;
36
37 /**
38 * TTreeView implements a simple tree view.
39 */
40 public class TTreeView extends TWidget {
41
42 // ------------------------------------------------------------------------
43 // Variables --------------------------------------------------------------
44 // ------------------------------------------------------------------------
45
46 /**
47 * Root of the tree.
48 */
49 private TTreeItem treeRoot;
50
51 /**
52 * Only one of my children can be selected.
53 */
54 private TTreeItem selectedItem = null;
55
56 /**
57 * The action to perform when the user selects an item.
58 */
59 private TAction action = null;
60
61 /**
62 * The top line currently visible.
63 */
64 private int topLine = 0;
65
66 /**
67 * The left column currently visible.
68 */
69 private int leftColumn = 0;
70
71 // ------------------------------------------------------------------------
72 // Constructors -----------------------------------------------------------
73 // ------------------------------------------------------------------------
74
75 /**
76 * Public constructor.
77 *
78 * @param parent parent widget
79 * @param x column relative to parent
80 * @param y row relative to parent
81 * @param width width of tree view
82 * @param height height of tree view
83 */
84 public TTreeView(final TWidget parent, final int x, final int y,
85 final int width, final int height) {
86
87 this(parent, x, y, width, height, null);
88 }
89
90 /**
91 * Public constructor.
92 *
93 * @param parent parent widget
94 * @param x column relative to parent
95 * @param y row relative to parent
96 * @param width width of tree view
97 * @param height height of tree view
98 * @param action action to perform when an item is selected
99 */
100 public TTreeView(final TWidget parent, final int x, final int y,
101 final int width, final int height, final TAction action) {
102
103 super(parent, x, y, width, height);
104 this.action = action;
105 }
106
107 // ------------------------------------------------------------------------
108 // Event handlers ---------------------------------------------------------
109 // ------------------------------------------------------------------------
110
111 /**
112 * Handle keystrokes.
113 *
114 * @param keypress keystroke event
115 */
116 @Override
117 public void onKeypress(final TKeypressEvent keypress) {
118 if (keypress.equals(kbUp)) {
119 // Select the previous item
120 if (selectedItem != null) {
121 if (selectedItem.keyboardPrevious != null) {
122 setSelected(selectedItem.keyboardPrevious, true);
123 }
124 }
125 } else if (keypress.equals(kbDown)) {
126 // Select the next item
127 if (selectedItem != null) {
128 if (selectedItem.keyboardNext != null) {
129 setSelected(selectedItem.keyboardNext, true);
130 }
131 }
132 } else if (keypress.equals(kbPgDn)) {
133 for (int i = 0; i < getHeight() - 1; i++) {
134 onKeypress(new TKeypressEvent(TKeypress.kbDown));
135 }
136 } else if (keypress.equals(kbPgUp)) {
137 for (int i = 0; i < getHeight() - 1; i++) {
138 onKeypress(new TKeypressEvent(TKeypress.kbUp));
139 }
140 } else if (keypress.equals(kbHome)) {
141 setSelected((TTreeItem) getChildren().get(0), false);
142 setTopLine(0);
143 } else if (keypress.equals(kbEnd)) {
144 setSelected((TTreeItem) getChildren().get(getChildren().size() - 1),
145 true);
146 } else {
147 if (selectedItem != null) {
148 selectedItem.onKeypress(keypress);
149 } else {
150 // Pass other keys (tab etc.) on to TWidget's handler.
151 super.onKeypress(keypress);
152 }
153 }
154 }
155
156 // ------------------------------------------------------------------------
157 // TWidget ----------------------------------------------------------------
158 // ------------------------------------------------------------------------
159
160
161 // ------------------------------------------------------------------------
162 // TTreeView --------------------------------------------------------------
163 // ------------------------------------------------------------------------
164
165 /**
166 * Get the root of the tree.
167 *
168 * @return the root of the tree
169 */
170 public final TTreeItem getTreeRoot() {
171 return treeRoot;
172 }
173
174 /**
175 * Set the root of the tree.
176 *
177 * @param treeRoot the new root of the tree
178 */
179 public final void setTreeRoot(final TTreeItem treeRoot) {
180 this.treeRoot = treeRoot;
181 alignTree();
182 }
183
184 /**
185 * Get the tree view item that was selected.
186 *
187 * @return the selected item, or null if no item is selected
188 */
189 public final TTreeItem getSelected() {
190 return selectedItem;
191 }
192
193 /**
194 * Set the new selected tree view item.
195 *
196 * @param item new item that became selected
197 * @param centerWindow if true, move the window to put the selected into
198 * view
199 */
200 public void setSelected(final TTreeItem item, final boolean centerWindow) {
201 if (item != null) {
202 item.setSelected(true);
203 }
204 if ((selectedItem != null) && (selectedItem != item)) {
205 selectedItem.setSelected(false);
206 }
207 selectedItem = item;
208
209 if (centerWindow) {
210 int y = 0;
211 for (TWidget widget: getChildren()) {
212 if (widget == selectedItem) {
213 break;
214 }
215 y++;
216 }
217 topLine = y - (getHeight() - 1)/2;
218 if (topLine > getChildren().size() - getHeight()) {
219 topLine = getChildren().size() - getHeight();
220 }
221 if (topLine < 0) {
222 topLine = 0;
223 }
224 }
225
226 if (selectedItem != null) {
227 activate(selectedItem);
228 }
229 }
230
231 /**
232 * Perform user selection action.
233 */
234 public void dispatch() {
235 if (action != null) {
236 action.DO(this);
237 }
238 }
239
240 /**
241 * Get the left column value. 0 is the leftmost column.
242 *
243 * @return the left column
244 */
245 public int getLeftColumn() {
246 return leftColumn;
247 }
248
249 /**
250 * Set the left column value. 0 is the leftmost column.
251 *
252 * @param leftColumn the new left column
253 */
254 public void setLeftColumn(final int leftColumn) {
255 this.leftColumn = leftColumn;
256 }
257
258 /**
259 * Get the top line (row) value. 0 is the topmost line.
260 *
261 * @return the top line
262 */
263 public int getTopLine() {
264 return topLine;
265 }
266
267 /**
268 * Set the top line value. 0 is the topmost line.
269 *
270 * @param topLine the new top line
271 */
272 public void setTopLine(final int topLine) {
273 this.topLine = topLine;
274 }
275
276 /**
277 * Get the total line (rows) count, based on the items that are visible
278 * and expanded.
279 *
280 * @return the line count
281 */
282 public int getTotalLineCount() {
283 if (treeRoot == null) {
284 return 0;
285 }
286 return getChildren().size();
287 }
288
289 /**
290 * Get the length of the widest item to display.
291 *
292 * @return the maximum number of columns for this item or its children
293 */
294 public int getMaximumColumn() {
295 if (treeRoot == null) {
296 return 0;
297 }
298 return treeRoot.getMaximumColumn();
299 }
300
301 /**
302 * Update the Y positions of all the children items to match the current
303 * topLine value. Note package private access.
304 */
305 void alignTree() {
306 if (treeRoot == null) {
307 return;
308 }
309
310 // As we walk the list we also adjust next/previous pointers,
311 // resulting in a doubly-linked list but only of the expanded items.
312 TTreeItem p = null;
313
314 for (int i = 0; i < getChildren().size(); i++) {
315 TTreeItem item = (TTreeItem) getChildren().get(i);
316
317 if (p != null) {
318 item.keyboardPrevious = p;
319 p.keyboardNext = item;
320 }
321 p = item;
322
323 item.setY(i - topLine);
324 item.setWidth(getWidth());
325 }
326
327 }
328
329 }