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