#35 wip
[nikiroo-utils.git] / src / jexer / ttree / TTreeViewWidget.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.THScroller;
33 import jexer.TKeypress;
34 import jexer.TScrollableWidget;
35 import jexer.TVScroller;
36 import jexer.TWidget;
37 import jexer.bits.StringUtils;
38 import jexer.event.TKeypressEvent;
39 import jexer.event.TMouseEvent;
40 import static jexer.TKeypress.*;
41
42 /**
43 * TTreeViewWidget wraps a tree view with horizontal and vertical scrollbars.
44 */
45 public class TTreeViewWidget extends TScrollableWidget {
46
47 // ------------------------------------------------------------------------
48 // Variables --------------------------------------------------------------
49 // ------------------------------------------------------------------------
50
51 /**
52 * The TTreeView
53 */
54 private TTreeView treeView;
55
56 /**
57 * If true, move the window to put the selected item in view. This
58 * normally only happens once after setting treeRoot.
59 */
60 private boolean centerWindow = false;
61
62 /**
63 * Maximum width of a single line.
64 */
65 private int maxLineWidth;
66
67 // ------------------------------------------------------------------------
68 // Constructors -----------------------------------------------------------
69 // ------------------------------------------------------------------------
70
71 /**
72 * Public constructor.
73 *
74 * @param parent parent widget
75 * @param x column relative to parent
76 * @param y row relative to parent
77 * @param width width of tree view
78 * @param height height of tree view
79 */
80 public TTreeViewWidget(final TWidget parent, final int x, final int y,
81 final int width, final int height) {
82
83 this(parent, x, y, width, height, null);
84 }
85
86 /**
87 * Public constructor.
88 *
89 * @param parent parent widget
90 * @param x column relative to parent
91 * @param y row relative to parent
92 * @param width width of tree view
93 * @param height height of tree view
94 * @param action action to perform when an item is selected
95 */
96 public TTreeViewWidget(final TWidget parent, final int x, final int y,
97 final int width, final int height, final TAction action) {
98
99 super(parent, x, y, width, height);
100
101 treeView = new TTreeView(this, 0, 0, getWidth() - 1, getHeight() - 1,
102 action);
103
104 vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
105 hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
106
107 }
108
109 // ------------------------------------------------------------------------
110 // Event handlers ---------------------------------------------------------
111 // ------------------------------------------------------------------------
112
113 /**
114 * Handle mouse press events.
115 *
116 * @param mouse mouse button press event
117 */
118 @Override
119 public void onMouseDown(final TMouseEvent mouse) {
120 if (mouse.isMouseWheelUp()) {
121 verticalDecrement();
122 } else if (mouse.isMouseWheelDown()) {
123 verticalIncrement();
124 } else {
125 // Pass to the TreeView or scrollbars
126 super.onMouseDown(mouse);
127 }
128
129 // Update the view to reflect the new scrollbar positions
130 treeView.setTopLine(getVerticalValue());
131 treeView.setLeftColumn(getHorizontalValue());
132 reflowData();
133 }
134
135 /**
136 * Handle mouse release events.
137 *
138 * @param mouse mouse button release event
139 */
140 @Override
141 public void onMouseUp(final TMouseEvent mouse) {
142 // Pass to the TreeView or scrollbars
143 super.onMouseUp(mouse);
144
145 // Update the view to reflect the new scrollbar positions
146 treeView.setTopLine(getVerticalValue());
147 treeView.setLeftColumn(getHorizontalValue());
148 reflowData();
149 }
150
151 /**
152 * Handle mouse motion events.
153 *
154 * @param mouse mouse motion event
155 */
156 @Override
157 public void onMouseMotion(final TMouseEvent mouse) {
158 // Pass to the TreeView or scrollbars
159 super.onMouseMotion(mouse);
160
161 // Update the view to reflect the new scrollbar positions
162 treeView.setTopLine(getVerticalValue());
163 treeView.setLeftColumn(getHorizontalValue());
164 reflowData();
165 }
166
167 /**
168 * Handle keystrokes.
169 *
170 * @param keypress keystroke event
171 */
172 @Override
173 public void onKeypress(final TKeypressEvent keypress) {
174 if (keypress.equals(kbShiftLeft)
175 || keypress.equals(kbCtrlLeft)
176 || keypress.equals(kbAltLeft)
177 ) {
178 horizontalDecrement();
179 } else if (keypress.equals(kbShiftRight)
180 || keypress.equals(kbCtrlRight)
181 || keypress.equals(kbAltRight)
182 ) {
183 horizontalIncrement();
184 } else if (keypress.equals(kbShiftUp)
185 || keypress.equals(kbCtrlUp)
186 || keypress.equals(kbAltUp)
187 ) {
188 verticalDecrement();
189 } else if (keypress.equals(kbShiftDown)
190 || keypress.equals(kbCtrlDown)
191 || keypress.equals(kbAltDown)
192 ) {
193 verticalIncrement();
194 } else if (keypress.equals(kbShiftPgUp)
195 || keypress.equals(kbCtrlPgUp)
196 || keypress.equals(kbAltPgUp)
197 ) {
198 bigVerticalDecrement();
199 } else if (keypress.equals(kbShiftPgDn)
200 || keypress.equals(kbCtrlPgDn)
201 || keypress.equals(kbAltPgDn)
202 ) {
203 bigVerticalIncrement();
204 } else if (keypress.equals(kbPgDn)) {
205 for (int i = 0; i < getHeight() - 2; i++) {
206 treeView.onKeypress(new TKeypressEvent(TKeypress.kbDown));
207 }
208 reflowData();
209 return;
210 } else if (keypress.equals(kbPgUp)) {
211 for (int i = 0; i < getHeight() - 2; i++) {
212 treeView.onKeypress(new TKeypressEvent(TKeypress.kbUp));
213 }
214 reflowData();
215 return;
216 } else if (keypress.equals(kbHome)) {
217 treeView.setSelected((TTreeItem) treeView.getChildren().get(0),
218 false);
219 treeView.setTopLine(0);
220 reflowData();
221 return;
222 } else if (keypress.equals(kbEnd)) {
223 treeView.setSelected((TTreeItem) treeView.getChildren().get(
224 treeView.getChildren().size() - 1), true);
225 reflowData();
226 return;
227 } else if (keypress.equals(kbTab)) {
228 getParent().switchWidget(true);
229 return;
230 } else if (keypress.equals(kbShiftTab)
231 || keypress.equals(kbBackTab)) {
232 getParent().switchWidget(false);
233 return;
234 } else {
235 treeView.onKeypress(keypress);
236
237 // Update the scrollbars to reflect the new data position
238 reflowData();
239 return;
240 }
241
242 // Update the view to reflect the new scrollbar position
243 treeView.setTopLine(getVerticalValue());
244 treeView.setLeftColumn(getHorizontalValue());
245 reflowData();
246 }
247
248 // ------------------------------------------------------------------------
249 // TScrollableWidget ------------------------------------------------------
250 // ------------------------------------------------------------------------
251
252 /**
253 * Resize text and scrollbars for a new width/height.
254 */
255 @Override
256 public void reflowData() {
257 int selectedRow = 0;
258 boolean foundSelectedRow = false;
259
260 // Reset the keyboard list, expandTree() will recreate it.
261 for (TWidget widget: treeView.getChildren()) {
262 TTreeItem item = (TTreeItem) widget;
263 item.keyboardPrevious = null;
264 item.keyboardNext = null;
265 }
266
267 // Expand the tree into a linear list
268 treeView.getChildren().clear();
269 treeView.getChildren().addAll(treeView.getTreeRoot().expandTree("",
270 true));
271
272 // Locate the selected row and maximum line width
273 for (TWidget widget: treeView.getChildren()) {
274 TTreeItem item = (TTreeItem) widget;
275
276 if (item == treeView.getSelected()) {
277 foundSelectedRow = true;
278 }
279 if (!foundSelectedRow) {
280 selectedRow++;
281 }
282
283 int lineWidth = StringUtils.width(item.getText())
284 + item.getPrefix().length() + 4;
285 if (lineWidth > maxLineWidth) {
286 maxLineWidth = lineWidth;
287 }
288 }
289
290 if ((centerWindow) && (foundSelectedRow)) {
291 if ((selectedRow < getVerticalValue())
292 || (selectedRow > getVerticalValue() + getHeight() - 2)
293 ) {
294 treeView.setTopLine(selectedRow);
295 centerWindow = false;
296 }
297 }
298 treeView.alignTree();
299
300 // Rescale the scroll bars
301 setVerticalValue(treeView.getTopLine());
302 setBottomValue(treeView.getTotalLineCount() - (getHeight() - 1));
303 if (getBottomValue() < getTopValue()) {
304 setBottomValue(getTopValue());
305 }
306 if (getVerticalValue() > getBottomValue()) {
307 setVerticalValue(getBottomValue());
308 }
309 setRightValue(maxLineWidth - 2);
310 if (getHorizontalValue() > getRightValue()) {
311 setHorizontalValue(getRightValue());
312 }
313
314 }
315
316 // ------------------------------------------------------------------------
317 // TTreeView --------------------------------------------------------------
318 // ------------------------------------------------------------------------
319
320 /**
321 * Get the underlying TTreeView.
322 *
323 * @return the TTreeView
324 */
325 public TTreeView getTreeView() {
326 return treeView;
327 }
328
329 /**
330 * Get the root of the tree.
331 *
332 * @return the root of the tree
333 */
334 public final TTreeItem getTreeRoot() {
335 return treeView.getTreeRoot();
336 }
337
338 /**
339 * Set the root of the tree.
340 *
341 * @param treeRoot the new root of the tree
342 */
343 public final void setTreeRoot(final TTreeItem treeRoot) {
344 treeView.setTreeRoot(treeRoot);
345 }
346
347 /**
348 * Set treeRoot.
349 *
350 * @param treeRoot ultimate root of tree
351 * @param centerWindow if true, move the window to put the root in view
352 */
353 public void setTreeRoot(final TTreeItem treeRoot,
354 final boolean centerWindow) {
355
356 treeView.setTreeRoot(treeRoot);
357 this.centerWindow = centerWindow;
358 }
359
360 /**
361 * Get the tree view item that was selected.
362 *
363 * @return the selected item, or null if no item is selected
364 */
365 public final TTreeItem getSelected() {
366 return treeView.getSelected();
367 }
368
369 /**
370 * Set the new selected tree view item.
371 *
372 * @param item new item that became selected
373 * @param centerWindow if true, move the window to put the selected into
374 * view
375 */
376 public void setSelected(final TTreeItem item, final boolean centerWindow) {
377 treeView.setSelected(item, centerWindow);
378 }
379
380 /**
381 * Perform user selection action.
382 */
383 public void dispatch() {
384 treeView.dispatch();
385 }
386
387 }