#46 StretchLayoutManager working
[fanfix.git] / src / jexer / ttree / TTreeViewWidget.java
CommitLineData
daa4106c 1/*
7668cb45
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
7668cb45 5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
7668cb45 7 *
e16dda65
KL
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:
7668cb45 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
7668cb45 17 *
e16dda65
KL
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.
7668cb45
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
d36057df
KL
29package jexer.ttree;
30
31import jexer.TAction;
32import jexer.THScroller;
33import jexer.TKeypress;
34import jexer.TScrollableWidget;
35import jexer.TVScroller;
36import jexer.TWidget;
e820d5dd 37import jexer.bits.StringUtils;
7668cb45
KL
38import jexer.event.TKeypressEvent;
39import jexer.event.TMouseEvent;
40import static jexer.TKeypress.*;
41
42/**
d36057df 43 * TTreeViewWidget wraps a tree view with horizontal and vertical scrollbars.
7668cb45 44 */
d36057df 45public class TTreeViewWidget extends TScrollableWidget {
7668cb45 46
d36057df
KL
47 // ------------------------------------------------------------------------
48 // Variables --------------------------------------------------------------
49 // ------------------------------------------------------------------------
7668cb45
KL
50
51 /**
d36057df 52 * The TTreeView
7668cb45 53 */
d36057df 54 private TTreeView treeView;
7668cb45
KL
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 */
329fd62e 60 private boolean centerWindow = false;
7668cb45
KL
61
62 /**
d36057df 63 * Maximum width of a single line.
7668cb45 64 */
d36057df 65 private int maxLineWidth;
329fd62e 66
d36057df
KL
67 // ------------------------------------------------------------------------
68 // Constructors -----------------------------------------------------------
69 // ------------------------------------------------------------------------
7668cb45
KL
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 */
d36057df 80 public TTreeViewWidget(final TWidget parent, final int x, final int y,
7668cb45
KL
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 */
d36057df 96 public TTreeViewWidget(final TWidget parent, final int x, final int y,
7668cb45
KL
97 final int width, final int height, final TAction action) {
98
99 super(parent, x, y, width, height);
d36057df
KL
100
101 treeView = new TTreeView(this, 0, 0, getWidth() - 1, getHeight() - 1,
102 action);
56661844
KL
103
104 vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
105 hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
7668cb45 106
7668cb45
KL
107 }
108
d36057df
KL
109 // ------------------------------------------------------------------------
110 // Event handlers ---------------------------------------------------------
111 // ------------------------------------------------------------------------
7668cb45
KL
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()) {
56661844 121 verticalDecrement();
7668cb45 122 } else if (mouse.isMouseWheelDown()) {
56661844 123 verticalIncrement();
7668cb45 124 } else {
d36057df 125 // Pass to the TreeView or scrollbars
7668cb45
KL
126 super.onMouseDown(mouse);
127 }
128
d36057df
KL
129 // Update the view to reflect the new scrollbar positions
130 treeView.setTopLine(getVerticalValue());
131 treeView.setLeftColumn(getHorizontalValue());
56661844 132 reflowData();
7668cb45
KL
133 }
134
135 /**
136 * Handle mouse release events.
137 *
138 * @param mouse mouse button release event
139 */
140 @Override
329fd62e 141 public void onMouseUp(final TMouseEvent mouse) {
d36057df
KL
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);
7668cb45 160
d36057df
KL
161 // Update the view to reflect the new scrollbar positions
162 treeView.setTopLine(getVerticalValue());
163 treeView.setLeftColumn(getHorizontalValue());
56661844 164 reflowData();
7668cb45
KL
165 }
166
167 /**
168 * Handle keystrokes.
169 *
170 * @param keypress keystroke event
171 */
172 @Override
173 public void onKeypress(final TKeypressEvent keypress) {
0d47c546
KL
174 if (keypress.equals(kbShiftLeft)
175 || keypress.equals(kbCtrlLeft)
176 || keypress.equals(kbAltLeft)
177 ) {
56661844 178 horizontalDecrement();
0d47c546
KL
179 } else if (keypress.equals(kbShiftRight)
180 || keypress.equals(kbCtrlRight)
181 || keypress.equals(kbAltRight)
182 ) {
56661844 183 horizontalIncrement();
0d47c546
KL
184 } else if (keypress.equals(kbShiftUp)
185 || keypress.equals(kbCtrlUp)
186 || keypress.equals(kbAltUp)
187 ) {
56661844 188 verticalDecrement();
0d47c546
KL
189 } else if (keypress.equals(kbShiftDown)
190 || keypress.equals(kbCtrlDown)
191 || keypress.equals(kbAltDown)
192 ) {
56661844 193 verticalIncrement();
0d47c546
KL
194 } else if (keypress.equals(kbShiftPgUp)
195 || keypress.equals(kbCtrlPgUp)
196 || keypress.equals(kbAltPgUp)
197 ) {
56661844 198 bigVerticalDecrement();
0d47c546
KL
199 } else if (keypress.equals(kbShiftPgDn)
200 || keypress.equals(kbCtrlPgDn)
201 || keypress.equals(kbAltPgDn)
202 ) {
56661844 203 bigVerticalIncrement();
d36057df
KL
204 } else if (keypress.equals(kbPgDn)) {
205 for (int i = 0; i < getHeight() - 2; i++) {
206 treeView.onKeypress(new TKeypressEvent(TKeypress.kbDown));
7668cb45 207 }
d36057df
KL
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));
0d47c546 213 }
d36057df
KL
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;
a043164f
KL
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;
7668cb45 234 } else {
d36057df
KL
235 treeView.onKeypress(keypress);
236
237 // Update the scrollbars to reflect the new data position
238 reflowData();
a043164f 239 return;
7668cb45
KL
240 }
241
d36057df
KL
242 // Update the view to reflect the new scrollbar position
243 treeView.setTopLine(getVerticalValue());
244 treeView.setLeftColumn(getHorizontalValue());
56661844 245 reflowData();
7668cb45
KL
246 }
247
d36057df
KL
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
e820d5dd 283 int lineWidth = StringUtils.width(item.getText())
d36057df
KL
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
7668cb45 387}