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