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