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