Allow parentless TWidget (req. for TComboBox fixes)
[nikiroo-utils.git] / src / jexer / TWidget.java
CommitLineData
daa4106c 1/*
48e27807
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
48e27807 5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
48e27807 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:
48e27807 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.
48e27807 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.
48e27807
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer;
30
382bc294 31import java.awt.image.BufferedImage;
0d47c546 32import java.io.IOException;
48e27807 33import java.util.List;
2ce6dab2 34import java.util.ArrayList;
48e27807 35
42873e30 36import jexer.backend.Screen;
a69ed767
KL
37import jexer.bits.Cell;
38import jexer.bits.CellAttributes;
928811d8 39import jexer.bits.ColorTheme;
48e27807
KL
40import jexer.event.TCommandEvent;
41import jexer.event.TInputEvent;
42import jexer.event.TKeypressEvent;
43import jexer.event.TMenuEvent;
44import jexer.event.TMouseEvent;
45import jexer.event.TResizeEvent;
d8dc8aea 46import jexer.layout.LayoutManager;
928811d8 47import jexer.menu.TMenu;
d36057df
KL
48import jexer.ttree.TTreeItem;
49import jexer.ttree.TTreeView;
50import jexer.ttree.TTreeViewWidget;
48e27807
KL
51import static jexer.TKeypress.*;
52
53/**
54 * TWidget is the base class of all objects that can be drawn on screen or
55 * handle user input events.
56 */
2b9c27db 57public abstract class TWidget implements Comparable<TWidget> {
48e27807 58
2ce6dab2 59 // ------------------------------------------------------------------------
d36057df 60 // Variables --------------------------------------------------------------
2ce6dab2
KL
61 // ------------------------------------------------------------------------
62
48e27807
KL
63 /**
64 * Every widget has a parent widget that it may be "contained" in. For
051e2913
KL
65 * example, a TWindow might contain several TFields, or a TComboBox may
66 * contain a TList that itself contains a TVScroller.
48e27807 67 */
fca67db0
KL
68 private TWidget parent = null;
69
48e27807
KL
70 /**
71 * Child widgets that this widget contains.
72 */
73 private List<TWidget> children;
74
75 /**
76 * The currently active child widget that will receive keypress events.
77 */
78 private TWidget activeChild = null;
79
80 /**
81 * If true, this widget will receive events.
82 */
fca67db0
KL
83 private boolean active = false;
84
48e27807
KL
85 /**
86 * The window that this widget draws to.
87 */
fca67db0 88 private TWindow window = null;
48e27807
KL
89
90 /**
91 * Absolute X position of the top-left corner.
92 */
fca67db0
KL
93 private int x = 0;
94
95 /**
d36057df 96 * Absolute Y position of the top-left corner.
fca67db0 97 */
d36057df 98 private int y = 0;
fca67db0
KL
99
100 /**
d36057df 101 * Width.
fca67db0 102 */
d36057df 103 private int width = 0;
48e27807
KL
104
105 /**
d36057df 106 * Height.
48e27807 107 */
d36057df 108 private int height = 0;
fca67db0
KL
109
110 /**
d36057df 111 * My tab order inside a window or containing widget.
fca67db0 112 */
d36057df 113 private int tabOrder = 0;
fca67db0
KL
114
115 /**
d36057df 116 * If true, this widget can be tabbed to or receive events.
fca67db0 117 */
d36057df 118 private boolean enabled = true;
48e27807 119
051e2913
KL
120 /**
121 * If true, this widget will be rendered.
122 */
123 private boolean visible = true;
124
48e27807 125 /**
d36057df 126 * If true, this widget has a cursor.
48e27807 127 */
d36057df 128 private boolean cursorVisible = false;
fca67db0
KL
129
130 /**
d36057df 131 * Cursor column position in relative coordinates.
fca67db0 132 */
d36057df 133 private int cursorX = 0;
fca67db0
KL
134
135 /**
d36057df 136 * Cursor row position in relative coordinates.
fca67db0 137 */
d36057df 138 private int cursorY = 0;
48e27807 139
d8dc8aea
KL
140 /**
141 * Layout manager.
142 */
143 private LayoutManager layout = null;
144
d36057df
KL
145 // ------------------------------------------------------------------------
146 // Constructors -----------------------------------------------------------
147 // ------------------------------------------------------------------------
fca67db0
KL
148
149 /**
d36057df 150 * Default constructor for subclasses.
fca67db0 151 */
d36057df
KL
152 protected TWidget() {
153 children = new ArrayList<TWidget>();
fca67db0
KL
154 }
155
156 /**
d36057df 157 * Protected constructor.
fca67db0 158 *
d36057df 159 * @param parent parent widget
fca67db0 160 */
d36057df
KL
161 protected TWidget(final TWidget parent) {
162 this(parent, true);
fca67db0 163 }
48e27807 164
0ee88b6d 165 /**
d36057df 166 * Protected constructor.
0ee88b6d 167 *
d36057df
KL
168 * @param parent parent widget
169 * @param x column relative to parent
170 * @param y row relative to parent
171 * @param width width of widget
172 * @param height height of widget
0ee88b6d 173 */
d36057df
KL
174 protected TWidget(final TWidget parent, final int x, final int y,
175 final int width, final int height) {
0ee88b6d 176
d36057df 177 this(parent, true, x, y, width, height);
0ee88b6d
KL
178 }
179
48e27807 180 /**
d36057df 181 * Protected constructor used by subclasses that are disabled by default.
48e27807 182 *
d36057df
KL
183 * @param parent parent widget
184 * @param enabled if true assume enabled
48e27807 185 */
d36057df 186 protected TWidget(final TWidget parent, final boolean enabled) {
713de2aa 187 this(parent, enabled, 0, 0, 0, 0);
48e27807
KL
188 }
189
190 /**
d36057df 191 * Protected constructor used by subclasses that are disabled by default.
48e27807 192 *
d36057df
KL
193 * @param parent parent widget
194 * @param enabled if true assume enabled
195 * @param x column relative to parent
196 * @param y row relative to parent
197 * @param width width of widget
198 * @param height height of widget
48e27807 199 */
d36057df
KL
200 protected TWidget(final TWidget parent, final boolean enabled,
201 final int x, final int y, final int width, final int height) {
202
5d26b504
KL
203 if (width < 0) {
204 throw new IllegalArgumentException("width cannot be negative");
205 }
206 if (height < 0) {
207 throw new IllegalArgumentException("height cannot be negative");
208 }
209
48e27807 210 this.enabled = enabled;
d36057df
KL
211 children = new ArrayList<TWidget>();
212
713de2aa
NR
213 // Allow parentless widgets
214 if (parent != null) {
215 // Do not add TStatusBars, they are drawn by TApplication.
216 if (this instanceof TStatusBar) {
217 // We don't add the child to the children list here
218 this.parent = parent;
219 this.window = parent.window;
220 } else {
221 parent.addChild(this);
222 }
223 }
224
d36057df
KL
225 this.x = x;
226 this.y = y;
227 this.width = width;
228 this.height = height;
d8dc8aea
KL
229
230 if (parent != null) {
231 this.window = parent.window;
232 parent.addChild(this);
233 }
d36057df 234 }
48e27807 235
7272e49f 236 /**
d36057df 237 * Backdoor access for TWindow's constructor. ONLY TWindow USES THIS.
7272e49f 238 *
d36057df
KL
239 * @param window the top-level window
240 * @param x column relative to parent
241 * @param y row relative to parent
242 * @param width width of window
243 * @param height height of window
7272e49f 244 */
d36057df
KL
245 protected final void setupForTWindow(final TWindow window,
246 final int x, final int y, final int width, final int height) {
247
5d26b504
KL
248 if (width < 0) {
249 throw new IllegalArgumentException("width cannot be negative");
250 }
251 if (height < 0) {
252 throw new IllegalArgumentException("height cannot be negative");
253 }
254
d36057df
KL
255 this.parent = window;
256 this.window = window;
257 this.x = x;
258 this.y = y;
259 this.width = width;
260 this.height = height;
7272e49f
KL
261 }
262
d36057df
KL
263 // ------------------------------------------------------------------------
264 // Event handlers ---------------------------------------------------------
265 // ------------------------------------------------------------------------
266
a69ed767
KL
267 /**
268 * Subclasses should override this method to cleanup resources. This is
269 * called by TWindow.onClose().
270 */
271 protected void close() {
272 // Default: call close() on children.
273 for (TWidget w: getChildren()) {
274 w.close();
275 }
276 }
277
a06459bd 278 /**
d36057df
KL
279 * Check if a mouse press/release event coordinate is contained in this
280 * widget.
a06459bd 281 *
d36057df
KL
282 * @param mouse a mouse-based event
283 * @return whether or not a mouse click would be sent to this widget
a06459bd 284 */
d36057df
KL
285 public final boolean mouseWouldHit(final TMouseEvent mouse) {
286
287 if (!enabled) {
24489803
KL
288 return false;
289 }
290
d36057df
KL
291 if ((this instanceof TTreeItem)
292 && ((y < 0) || (y > parent.getHeight() - 1))
24489803
KL
293 ) {
294 return false;
295 }
a06459bd 296
d36057df
KL
297 if ((mouse.getAbsoluteX() >= getAbsoluteX())
298 && (mouse.getAbsoluteX() < getAbsoluteX() + width)
299 && (mouse.getAbsoluteY() >= getAbsoluteY())
300 && (mouse.getAbsoluteY() < getAbsoluteY() + height)
301 ) {
302 return true;
303 }
304 return false;
305 }
48e27807 306
7272e49f 307 /**
d36057df 308 * Method that subclasses can override to handle keystrokes.
7272e49f 309 *
d36057df 310 * @param keypress keystroke event
7272e49f 311 */
d36057df 312 public void onKeypress(final TKeypressEvent keypress) {
5218e73c 313 assert (parent != null);
d36057df
KL
314
315 if ((children.size() == 0)
316 || (this instanceof TTreeView)
317 || (this instanceof TText)
a69ed767 318 || (this instanceof TComboBox)
d36057df
KL
319 ) {
320
321 // Defaults:
322 // tab / shift-tab - switch to next/previous widget
323 // left-arrow or up-arrow: same as shift-tab
324 if ((keypress.equals(kbTab))
a69ed767 325 || (keypress.equals(kbDown) && !(this instanceof TComboBox))
d36057df
KL
326 ) {
327 parent.switchWidget(true);
328 return;
329 } else if ((keypress.equals(kbShiftTab))
330 || (keypress.equals(kbBackTab))
a69ed767 331 || (keypress.equals(kbUp) && !(this instanceof TComboBox))
d36057df
KL
332 ) {
333 parent.switchWidget(false);
334 return;
335 }
336 }
337
338 if ((children.size() == 0)
339 && !(this instanceof TTreeView)
340 ) {
341
342 // Defaults:
343 // right-arrow or down-arrow: same as tab
344 if (keypress.equals(kbRight)) {
345 parent.switchWidget(true);
346 return;
347 } else if (keypress.equals(kbLeft)) {
348 parent.switchWidget(false);
349 return;
350 }
351 }
352
353 // If I have any buttons on me AND this is an Alt-key that matches
00691e80 354 // its mnemonic, send it an Enter keystroke.
d36057df
KL
355 for (TWidget widget: children) {
356 if (widget instanceof TButton) {
357 TButton button = (TButton) widget;
358 if (button.isEnabled()
359 && !keypress.getKey().isFnKey()
360 && keypress.getKey().isAlt()
361 && !keypress.getKey().isCtrl()
362 && (Character.toLowerCase(button.getMnemonic().getShortcut())
363 == Character.toLowerCase(keypress.getKey().getChar()))
364 ) {
365
366 widget.onKeypress(new TKeypressEvent(kbEnter));
367 return;
368 }
369 }
370 }
371
00691e80
KL
372 // If I have any labels on me AND this is an Alt-key that matches
373 // its mnemonic, call its action.
374 for (TWidget widget: children) {
375 if (widget instanceof TLabel) {
376 TLabel label = (TLabel) widget;
377 if (!keypress.getKey().isFnKey()
378 && keypress.getKey().isAlt()
379 && !keypress.getKey().isCtrl()
380 && (Character.toLowerCase(label.getMnemonic().getShortcut())
381 == Character.toLowerCase(keypress.getKey().getChar()))
382 ) {
383
384 label.dispatch();
385 return;
386 }
387 }
388 }
389
390 // If I have any radiobuttons on me AND this is an Alt-key that
391 // matches its mnemonic, select it and send a Space to it.
392 for (TWidget widget: children) {
393 if (widget instanceof TRadioButton) {
394 TRadioButton button = (TRadioButton) widget;
395 if (button.isEnabled()
396 && !keypress.getKey().isFnKey()
397 && keypress.getKey().isAlt()
398 && !keypress.getKey().isCtrl()
399 && (Character.toLowerCase(button.getMnemonic().getShortcut())
400 == Character.toLowerCase(keypress.getKey().getChar()))
401 ) {
402 activate(widget);
403 widget.onKeypress(new TKeypressEvent(kbSpace));
404 return;
405 }
406 }
407 if (widget instanceof TRadioGroup) {
408 for (TWidget child: widget.getChildren()) {
409 if (child instanceof TRadioButton) {
410 TRadioButton button = (TRadioButton) child;
411 if (button.isEnabled()
412 && !keypress.getKey().isFnKey()
413 && keypress.getKey().isAlt()
414 && !keypress.getKey().isCtrl()
415 && (Character.toLowerCase(button.getMnemonic().getShortcut())
416 == Character.toLowerCase(keypress.getKey().getChar()))
417 ) {
418 activate(widget);
419 widget.activate(child);
420 child.onKeypress(new TKeypressEvent(kbSpace));
421 return;
422 }
423 }
424 }
425 }
426 }
427
428 // If I have any checkboxes on me AND this is an Alt-key that matches
429 // its mnemonic, select it and set it to checked.
430 for (TWidget widget: children) {
431 if (widget instanceof TCheckBox) {
432 TCheckBox checkBox = (TCheckBox) widget;
433 if (checkBox.isEnabled()
434 && !keypress.getKey().isFnKey()
435 && keypress.getKey().isAlt()
436 && !keypress.getKey().isCtrl()
437 && (Character.toLowerCase(checkBox.getMnemonic().getShortcut())
438 == Character.toLowerCase(keypress.getKey().getChar()))
439 ) {
440 activate(checkBox);
441 checkBox.setChecked(true);
442 return;
443 }
444 }
445 }
446
d36057df
KL
447 // Dispatch the keypress to an active widget
448 for (TWidget widget: children) {
449 if (widget.active) {
450 widget.onKeypress(keypress);
451 return;
452 }
453 }
7272e49f
KL
454 }
455
456 /**
d36057df 457 * Method that subclasses can override to handle mouse button presses.
7272e49f 458 *
d36057df 459 * @param mouse mouse button event
7272e49f 460 */
d36057df
KL
461 public void onMouseDown(final TMouseEvent mouse) {
462 // Default: do nothing, pass to children instead
a69ed767
KL
463 if (activeChild != null) {
464 if (activeChild.mouseWouldHit(mouse)) {
465 // Dispatch to the active child
466
467 // Set x and y relative to the child's coordinates
468 mouse.setX(mouse.getAbsoluteX() - activeChild.getAbsoluteX());
469 mouse.setY(mouse.getAbsoluteY() - activeChild.getAbsoluteY());
470 activeChild.onMouseDown(mouse);
471 return;
472 }
473 }
d36057df
KL
474 for (int i = children.size() - 1 ; i >= 0 ; i--) {
475 TWidget widget = children.get(i);
476 if (widget.mouseWouldHit(mouse)) {
477 // Dispatch to this child, also activate it
478 activate(widget);
7272e49f 479
d36057df
KL
480 // Set x and y relative to the child's coordinates
481 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
482 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
483 widget.onMouseDown(mouse);
484 return;
485 }
486 }
487 }
48e27807 488
7272e49f 489 /**
d36057df 490 * Method that subclasses can override to handle mouse button releases.
7272e49f 491 *
d36057df 492 * @param mouse mouse button event
7272e49f 493 */
d36057df
KL
494 public void onMouseUp(final TMouseEvent mouse) {
495 // Default: do nothing, pass to children instead
a69ed767
KL
496 if (activeChild != null) {
497 if (activeChild.mouseWouldHit(mouse)) {
498 // Dispatch to the active child
499
500 // Set x and y relative to the child's coordinates
501 mouse.setX(mouse.getAbsoluteX() - activeChild.getAbsoluteX());
502 mouse.setY(mouse.getAbsoluteY() - activeChild.getAbsoluteY());
503 activeChild.onMouseUp(mouse);
504 return;
505 }
506 }
d36057df
KL
507 for (int i = children.size() - 1 ; i >= 0 ; i--) {
508 TWidget widget = children.get(i);
509 if (widget.mouseWouldHit(mouse)) {
510 // Dispatch to this child, also activate it
511 activate(widget);
512
513 // Set x and y relative to the child's coordinates
514 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
515 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
516 widget.onMouseUp(mouse);
517 return;
518 }
519 }
7272e49f
KL
520 }
521
522 /**
d36057df 523 * Method that subclasses can override to handle mouse movements.
7272e49f 524 *
d36057df 525 * @param mouse mouse motion event
7272e49f 526 */
d36057df
KL
527 public void onMouseMotion(final TMouseEvent mouse) {
528 // Default: do nothing, pass it on to ALL of my children. This way
529 // the children can see the mouse "leaving" their area.
530 for (TWidget widget: children) {
531 // Set x and y relative to the child's coordinates
532 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
533 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
534 widget.onMouseMotion(mouse);
535 }
7272e49f
KL
536 }
537
2ce6dab2 538 /**
d36057df
KL
539 * Method that subclasses can override to handle mouse button
540 * double-clicks.
2ce6dab2 541 *
d36057df 542 * @param mouse mouse button event
2ce6dab2 543 */
d36057df
KL
544 public void onMouseDoubleClick(final TMouseEvent mouse) {
545 // Default: do nothing, pass to children instead
a69ed767
KL
546 if (activeChild != null) {
547 if (activeChild.mouseWouldHit(mouse)) {
548 // Dispatch to the active child
549
550 // Set x and y relative to the child's coordinates
551 mouse.setX(mouse.getAbsoluteX() - activeChild.getAbsoluteX());
552 mouse.setY(mouse.getAbsoluteY() - activeChild.getAbsoluteY());
553 activeChild.onMouseDoubleClick(mouse);
554 return;
555 }
556 }
d36057df
KL
557 for (int i = children.size() - 1 ; i >= 0 ; i--) {
558 TWidget widget = children.get(i);
559 if (widget.mouseWouldHit(mouse)) {
560 // Dispatch to this child, also activate it
561 activate(widget);
562
563 // Set x and y relative to the child's coordinates
564 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
565 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
566 widget.onMouseDoubleClick(mouse);
567 return;
568 }
569 }
2ce6dab2
KL
570 }
571
572 /**
d36057df
KL
573 * Method that subclasses can override to handle window/screen resize
574 * events.
2ce6dab2 575 *
d36057df 576 * @param resize resize event
2ce6dab2 577 */
d36057df
KL
578 public void onResize(final TResizeEvent resize) {
579 // Default: change my width/height.
580 if (resize.getType() == TResizeEvent.Type.WIDGET) {
581 width = resize.getWidth();
582 height = resize.getHeight();
d8dc8aea 583 if (layout != null) {
fc2af494
KL
584 if (this instanceof TWindow) {
585 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
586 width - 2, height - 2));
587 } else {
588 layout.onResize(resize);
589 }
d8dc8aea 590 }
d36057df
KL
591 } else {
592 // Let children see the screen resize
593 for (TWidget widget: children) {
594 widget.onResize(resize);
595 }
596 }
2ce6dab2
KL
597 }
598
48e27807 599 /**
d36057df 600 * Method that subclasses can override to handle posted command events.
7668cb45 601 *
d36057df 602 * @param command command event
2b9c27db 603 */
d36057df
KL
604 public void onCommand(final TCommandEvent command) {
605 // Default: do nothing, pass to children instead
606 for (TWidget widget: children) {
607 widget.onCommand(command);
7668cb45 608 }
48e27807
KL
609 }
610
611 /**
d36057df
KL
612 * Method that subclasses can override to handle menu or posted menu
613 * events.
48e27807 614 *
d36057df 615 * @param menu menu event
48e27807 616 */
d36057df
KL
617 public void onMenu(final TMenuEvent menu) {
618 // Default: do nothing, pass to children instead
619 for (TWidget widget: children) {
620 widget.onMenu(menu);
48e27807 621 }
48e27807
KL
622 }
623
624 /**
d36057df
KL
625 * Method that subclasses can override to do processing when the UI is
626 * idle. Note that repainting is NOT assumed. To get a refresh after
627 * onIdle, call doRepaint().
48e27807 628 */
d36057df
KL
629 public void onIdle() {
630 // Default: do nothing, pass to children instead
631 for (TWidget widget: children) {
632 widget.onIdle();
633 }
48e27807
KL
634 }
635
636 /**
d36057df
KL
637 * Consume event. Subclasses that want to intercept all events in one go
638 * can override this method.
48e27807 639 *
d36057df 640 * @param event keyboard, mouse, resize, command, or menu event
48e27807 641 */
d36057df
KL
642 public void handleEvent(final TInputEvent event) {
643 /*
644 System.err.printf("TWidget (%s) event: %s\n", this.getClass().getName(),
645 event);
646 */
647
648 if (!enabled) {
649 // Discard event
650 // System.err.println(" -- discard --");
651 return;
652 }
653
654 if (event instanceof TKeypressEvent) {
655 onKeypress((TKeypressEvent) event);
656 } else if (event instanceof TMouseEvent) {
657
658 TMouseEvent mouse = (TMouseEvent) event;
659
660 switch (mouse.getType()) {
661
662 case MOUSE_DOWN:
663 onMouseDown(mouse);
664 break;
665
666 case MOUSE_UP:
667 onMouseUp(mouse);
668 break;
669
670 case MOUSE_MOTION:
671 onMouseMotion(mouse);
672 break;
673
674 case MOUSE_DOUBLE_CLICK:
675 onMouseDoubleClick(mouse);
676 break;
677
678 default:
679 throw new IllegalArgumentException("Invalid mouse event type: "
680 + mouse.getType());
681 }
682 } else if (event instanceof TResizeEvent) {
683 onResize((TResizeEvent) event);
684 } else if (event instanceof TCommandEvent) {
685 onCommand((TCommandEvent) event);
686 } else if (event instanceof TMenuEvent) {
687 onMenu((TMenuEvent) event);
688 }
689
690 // Do nothing else
691 return;
48e27807
KL
692 }
693
d36057df
KL
694 // ------------------------------------------------------------------------
695 // TWidget ----------------------------------------------------------------
696 // ------------------------------------------------------------------------
697
48e27807 698 /**
d36057df 699 * Get parent widget.
48e27807 700 *
d36057df 701 * @return parent widget
48e27807 702 */
d36057df
KL
703 public final TWidget getParent() {
704 return parent;
48e27807
KL
705 }
706
707 /**
d36057df 708 * Get the list of child widgets that this widget contains.
48e27807 709 *
d36057df 710 * @return the list of child widgets
48e27807 711 */
d36057df
KL
712 public List<TWidget> getChildren() {
713 return children;
48e27807
KL
714 }
715
5218e73c
KL
716 /**
717 * Remove this widget from its parent container. close() will be called
718 * before it is removed.
719 */
720 public final void remove() {
721 remove(true);
722 }
723
724 /**
725 * Remove this widget from its parent container.
726 *
727 * @param doClose if true, call the close() method before removing the
728 * child
729 */
730 public final void remove(final boolean doClose) {
731 if (parent != null) {
732 parent.remove(this, doClose);
733 }
734 }
735
5ffeabcc
KL
736 /**
737 * Remove a child widget from this container.
738 *
739 * @param child the child widget to remove
740 */
741 public final void remove(final TWidget child) {
742 remove(child, true);
743 }
744
5218e73c
KL
745 /**
746 * Remove a child widget from this container.
747 *
748 * @param child the child widget to remove
749 * @param doClose if true, call the close() method before removing the
750 * child
751 */
752 public final void remove(final TWidget child, final boolean doClose) {
753 if (!children.contains(child)) {
754 throw new IndexOutOfBoundsException("child widget is not in " +
755 "list of children of this parent");
756 }
757 if (doClose) {
758 child.close();
759 }
760 children.remove(child);
761 child.parent = null;
5ffeabcc 762 child.window = null;
d8dc8aea
KL
763 if (layout != null) {
764 layout.remove(this);
765 }
5218e73c
KL
766 }
767
768 /**
769 * Set this widget's parent to a different widget.
770 *
771 * @param newParent new parent widget
772 * @param doClose if true, call the close() method before removing the
773 * child from its existing parent widget
774 */
775 public final void setParent(final TWidget newParent,
776 final boolean doClose) {
777
778 if (parent != null) {
779 parent.remove(this, doClose);
5ffeabcc 780 window = null;
5218e73c
KL
781 }
782 assert (parent == null);
5ffeabcc
KL
783 assert (window == null);
784 parent = newParent;
785 setWindow(parent.window);
786 parent.addChild(this);
5218e73c
KL
787 }
788
789 /**
5ffeabcc
KL
790 * Set this widget's window to a specific window.
791 *
792 * Having a null parent with a specified window is only used within Jexer
793 * by TStatusBar because TApplication routes events directly to it and
794 * calls its draw() method. Any other non-parented widgets will require
795 * similar special case functionality to receive events or be drawn to
796 * screen.
5218e73c
KL
797 *
798 * @param window the window to use
799 */
800 public final void setWindow(final TWindow window) {
5218e73c 801 this.window = window;
5ffeabcc
KL
802 for (TWidget child: getChildren()) {
803 child.setWindow(window);
804 }
5218e73c
KL
805 }
806
807 /**
808 * Remove a child widget from this container, and all of its children
809 * recursively from their parent containers.
810 *
811 * @param child the child widget to remove
812 * @param doClose if true, call the close() method before removing each
813 * child
814 */
815 public final void removeAll(final TWidget child, final boolean doClose) {
816 remove(child, doClose);
817 for (TWidget w: child.children) {
818 child.removeAll(w, doClose);
819 }
820 }
821
928811d8 822 /**
d36057df 823 * Get active flag.
928811d8 824 *
d36057df 825 * @return if true, this widget will receive events
928811d8 826 */
d36057df
KL
827 public final boolean isActive() {
828 return active;
928811d8
KL
829 }
830
48e27807 831 /**
d36057df
KL
832 * Set active flag.
833 *
834 * @param active if true, this widget will receive events
48e27807 835 */
d36057df
KL
836 public final void setActive(final boolean active) {
837 this.active = active;
48e27807
KL
838 }
839
840 /**
d36057df
KL
841 * Get the window this widget is on.
842 *
843 * @return the window
48e27807 844 */
d36057df
KL
845 public final TWindow getWindow() {
846 return window;
48e27807
KL
847 }
848
be72cb5c 849 /**
d36057df
KL
850 * Get X position.
851 *
852 * @return absolute X position of the top-left corner
be72cb5c 853 */
d36057df
KL
854 public final int getX() {
855 return x;
be72cb5c
KL
856 }
857
48e27807 858 /**
d36057df
KL
859 * Set X position.
860 *
861 * @param x absolute X position of the top-left corner
48e27807 862 */
d36057df
KL
863 public final void setX(final int x) {
864 this.x = x;
48e27807
KL
865 }
866
867 /**
d36057df 868 * Get Y position.
48e27807 869 *
d36057df 870 * @return absolute Y position of the top-left corner
48e27807 871 */
d36057df
KL
872 public final int getY() {
873 return y;
30d336cc
KL
874 }
875
a83fea2b 876 /**
d36057df 877 * Set Y position.
a83fea2b 878 *
d36057df 879 * @param y absolute Y position of the top-left corner
a83fea2b 880 */
d36057df
KL
881 public final void setY(final int y) {
882 this.y = y;
a83fea2b
KL
883 }
884
30d336cc 885 /**
d36057df 886 * Get the width.
30d336cc 887 *
d36057df 888 * @return widget width
30d336cc 889 */
d8dc8aea 890 public int getWidth() {
d36057df 891 return this.width;
48e27807
KL
892 }
893
a83fea2b 894 /**
d36057df 895 * Change the width.
a83fea2b 896 *
d36057df 897 * @param width new widget width
a83fea2b 898 */
d8dc8aea 899 public void setWidth(final int width) {
a83fea2b 900 this.width = width;
d8dc8aea
KL
901 if (layout != null) {
902 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
903 width, height));
904 }
a83fea2b
KL
905 }
906
2ce6dab2 907 /**
d36057df 908 * Get the height.
2ce6dab2 909 *
d36057df 910 * @return widget height
2ce6dab2 911 */
d8dc8aea 912 public int getHeight() {
d36057df
KL
913 return this.height;
914 }
2ce6dab2 915
d36057df
KL
916 /**
917 * Change the height.
918 *
919 * @param height new widget height
920 */
d8dc8aea 921 public void setHeight(final int height) {
2ce6dab2 922 this.height = height;
d8dc8aea
KL
923 if (layout != null) {
924 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
925 width, height));
926 }
2ce6dab2
KL
927 }
928
48e27807 929 /**
d36057df 930 * Change the dimensions.
48e27807 931 *
d36057df
KL
932 * @param x absolute X position of the top-left corner
933 * @param y absolute Y position of the top-left corner
934 * @param width new widget width
935 * @param height new widget height
48e27807 936 */
d36057df
KL
937 public final void setDimensions(final int x, final int y, final int width,
938 final int height) {
48e27807 939
90d87fca
KL
940 this.x = x;
941 this.y = y;
8afe8fa7
KL
942 // Call the functions so that subclasses can choose how to handle it.
943 setWidth(width);
944 setHeight(height);
d8dc8aea
KL
945 if (layout != null) {
946 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
947 width, height));
948 }
949 }
950
951 /**
952 * Get the layout manager.
953 *
954 * @return the layout manager, or null if not set
955 */
956 public LayoutManager getLayoutManager() {
957 return layout;
958 }
959
960 /**
961 * Set the layout manager.
962 *
963 * @param layout the new layout manager
964 */
965 public void setLayoutManager(LayoutManager layout) {
966 if (this.layout != null) {
967 for (TWidget w: children) {
968 this.layout.remove(w);
969 }
970 this.layout = null;
971 }
972 this.layout = layout;
973 if (this.layout != null) {
974 for (TWidget w: children) {
975 this.layout.add(w);
976 }
977 }
48e27807
KL
978 }
979
980 /**
d36057df 981 * Get enabled flag.
48e27807 982 *
d36057df 983 * @return if true, this widget can be tabbed to or receive events
48e27807 984 */
d36057df
KL
985 public final boolean isEnabled() {
986 return enabled;
987 }
48e27807 988
d36057df
KL
989 /**
990 * Set enabled flag.
991 *
992 * @param enabled if true, this widget can be tabbed to or receive events
993 */
994 public final void setEnabled(final boolean enabled) {
995 this.enabled = enabled;
996 if (!enabled) {
997 active = false;
998 // See if there are any active siblings to switch to
999 boolean foundSibling = false;
1000 if (parent != null) {
1001 for (TWidget w: parent.children) {
1002 if ((w.enabled)
1003 && !(this instanceof THScroller)
1004 && !(this instanceof TVScroller)
1005 ) {
1006 parent.activate(w);
1007 foundSibling = true;
1008 break;
1009 }
1010 }
1011 if (!foundSibling) {
1012 parent.activeChild = null;
1013 }
48e27807 1014 }
48e27807
KL
1015 }
1016 }
1017
051e2913
KL
1018 /**
1019 * Set visible flag.
1020 *
1021 * @param visible if true, this widget will be drawn
1022 */
1023 public final void setVisible(final boolean visible) {
1024 this.visible = visible;
1025 }
1026
1027 /**
1028 * See if this widget is visible.
1029 *
1030 * @return if true, this widget will be drawn
1031 */
1032 public final boolean isVisible() {
1033 return visible;
1034 }
1035
48e27807 1036 /**
d36057df 1037 * Set visible cursor flag.
48e27807 1038 *
d36057df 1039 * @param cursorVisible if true, this widget has a cursor
48e27807 1040 */
d36057df
KL
1041 public final void setCursorVisible(final boolean cursorVisible) {
1042 this.cursorVisible = cursorVisible;
48e27807
KL
1043 }
1044
1045 /**
d36057df 1046 * See if this widget has a visible cursor.
48e27807 1047 *
d36057df 1048 * @return if true, this widget has a visible cursor
48e27807 1049 */
d36057df
KL
1050 public final boolean isCursorVisible() {
1051 // If cursor is out of my bounds, it is not visible.
1052 if ((cursorX >= width)
1053 || (cursorX < 0)
1054 || (cursorY >= height)
1055 || (cursorY < 0)
1056 ) {
1057 return false;
1058 }
48e27807 1059
5218e73c
KL
1060 assert (window != null);
1061
2bc32111
KL
1062 if (window instanceof TDesktop) {
1063 // Desktop doesn't have a window border.
1064 return cursorVisible;
1065 }
1066
d36057df
KL
1067 // If cursor is out of my window's bounds, it is not visible.
1068 if ((getCursorAbsoluteX() >= window.getAbsoluteX()
1069 + window.getWidth() - 1)
1070 || (getCursorAbsoluteX() < 0)
1071 || (getCursorAbsoluteY() >= window.getAbsoluteY()
1072 + window.getHeight() - 1)
1073 || (getCursorAbsoluteY() < 0)
1074 ) {
1075 return false;
48e27807 1076 }
d36057df
KL
1077 return cursorVisible;
1078 }
48e27807 1079
d36057df
KL
1080 /**
1081 * Get cursor X value.
1082 *
1083 * @return cursor column position in relative coordinates
1084 */
1085 public final int getCursorX() {
1086 return cursorX;
1087 }
48e27807 1088
d36057df
KL
1089 /**
1090 * Set cursor X value.
1091 *
1092 * @param cursorX column position in relative coordinates
1093 */
1094 public final void setCursorX(final int cursorX) {
1095 this.cursorX = cursorX;
1096 }
48e27807 1097
d36057df
KL
1098 /**
1099 * Get cursor Y value.
1100 *
1101 * @return cursor row position in relative coordinates
1102 */
1103 public final int getCursorY() {
1104 return cursorY;
1105 }
48e27807 1106
d36057df
KL
1107 /**
1108 * Set cursor Y value.
1109 *
1110 * @param cursorY row position in relative coordinates
1111 */
1112 public final void setCursorY(final int cursorY) {
1113 this.cursorY = cursorY;
1114 }
48e27807 1115
d36057df
KL
1116 /**
1117 * Get this TWidget's parent TApplication.
1118 *
c7a75ad3 1119 * @return the parent TApplication, or null if not assigned
d36057df
KL
1120 */
1121 public TApplication getApplication() {
c7a75ad3
KL
1122 if (window != null) {
1123 return window.getApplication();
1124 }
1125 return null;
d36057df 1126 }
48e27807 1127
d36057df
KL
1128 /**
1129 * Get the Screen.
1130 *
c7a75ad3 1131 * @return the Screen, or null if not assigned
d36057df
KL
1132 */
1133 public Screen getScreen() {
c7a75ad3
KL
1134 if (window != null) {
1135 return window.getScreen();
1136 }
1137 return null;
48e27807
KL
1138 }
1139
1140 /**
d36057df
KL
1141 * Comparison operator. For various subclasses it sorts on:
1142 * <ul>
1143 * <li>tabOrder for TWidgets</li>
1144 * <li>z for TWindows</li>
1145 * <li>text for TTreeItems</li>
1146 * </ul>
48e27807 1147 *
d36057df
KL
1148 * @param that another TWidget, TWindow, or TTreeItem instance
1149 * @return difference between this.tabOrder and that.tabOrder, or
1150 * difference between this.z and that.z, or String.compareTo(text)
48e27807 1151 */
713de2aa 1152 @Override
d36057df
KL
1153 public final int compareTo(final TWidget that) {
1154 if ((this instanceof TWindow)
1155 && (that instanceof TWindow)
48e27807 1156 ) {
d36057df
KL
1157 return (((TWindow) this).getZ() - ((TWindow) that).getZ());
1158 }
1159 if ((this instanceof TTreeItem)
1160 && (that instanceof TTreeItem)
1161 ) {
1162 return (((TTreeItem) this).getText().compareTo(
1163 ((TTreeItem) that).getText()));
48e27807 1164 }
d36057df
KL
1165 return (this.tabOrder - that.tabOrder);
1166 }
48e27807 1167
d36057df
KL
1168 /**
1169 * See if this widget should render with the active color.
1170 *
1171 * @return true if this widget is active and all of its parents are
1172 * active.
1173 */
1174 public final boolean isAbsoluteActive() {
1175 if (parent == this) {
1176 return active;
48e27807 1177 }
5218e73c 1178 return (active && (parent == null ? true : parent.isAbsoluteActive()));
48e27807
KL
1179 }
1180
d36057df
KL
1181 /**
1182 * Returns the cursor X position.
1183 *
1184 * @return absolute screen column number for the cursor's X position
1185 */
1186 public final int getCursorAbsoluteX() {
1187 return getAbsoluteX() + cursorX;
1188 }
2ce6dab2
KL
1189
1190 /**
d36057df 1191 * Returns the cursor Y position.
2ce6dab2 1192 *
d36057df 1193 * @return absolute screen row number for the cursor's Y position
2ce6dab2 1194 */
d36057df
KL
1195 public final int getCursorAbsoluteY() {
1196 return getAbsoluteY() + cursorY;
1197 }
2ce6dab2 1198
d36057df
KL
1199 /**
1200 * Compute my absolute X position as the sum of my X plus all my parent's
1201 * X's.
1202 *
1203 * @return absolute screen column number for my X position
1204 */
1205 public final int getAbsoluteX() {
1206 assert (parent != null);
1207 if (parent == this) {
1208 return x;
2ce6dab2 1209 }
d36057df
KL
1210 if ((parent instanceof TWindow)
1211 && !(parent instanceof TMenu)
1212 && !(parent instanceof TDesktop)
1213 ) {
1214 // Widgets on a TWindow have (0,0) as their top-left, but this is
1215 // actually the TWindow's (1,1).
1216 return parent.getAbsoluteX() + x + 1;
1217 }
1218 return parent.getAbsoluteX() + x;
1219 }
2ce6dab2 1220
d36057df
KL
1221 /**
1222 * Compute my absolute Y position as the sum of my Y plus all my parent's
1223 * Y's.
1224 *
1225 * @return absolute screen row number for my Y position
1226 */
1227 public final int getAbsoluteY() {
1228 assert (parent != null);
1229 if (parent == this) {
1230 return y;
1231 }
1232 if ((parent instanceof TWindow)
1233 && !(parent instanceof TMenu)
1234 && !(parent instanceof TDesktop)
2ce6dab2 1235 ) {
d36057df
KL
1236 // Widgets on a TWindow have (0,0) as their top-left, but this is
1237 // actually the TWindow's (1,1).
1238 return parent.getAbsoluteY() + y + 1;
2ce6dab2 1239 }
d36057df 1240 return parent.getAbsoluteY() + y;
2ce6dab2
KL
1241 }
1242
48e27807 1243 /**
d36057df 1244 * Get the global color theme.
48e27807 1245 *
d36057df 1246 * @return the ColorTheme
48e27807 1247 */
a69ed767 1248 protected final ColorTheme getTheme() {
d36057df
KL
1249 return window.getApplication().getTheme();
1250 }
48e27807 1251
1e71bba2
KL
1252 /**
1253 * See if this widget can be drawn onto a screen.
1254 *
1255 * @return true if this widget is part of the hierarchy that can draw to
1256 * a screen
1257 */
1258 public final boolean isDrawable() {
1259 if ((window == null)
1260 || (window.getScreen() == null)
1261 || (parent == null)
1262 ) {
1263 return false;
1264 }
1265 if (parent == this) {
1266 return true;
1267 }
1268 return (parent.isDrawable());
1269 }
1270
d36057df
KL
1271 /**
1272 * Draw my specific widget. When called, the screen rectangle I draw
1273 * into is already setup (offset and clipping).
1274 */
1275 public void draw() {
1276 // Default widget draws nothing.
1277 }
48e27807 1278
d36057df 1279 /**
a69ed767 1280 * Called by parent to render to TWindow. Note package private access.
d36057df 1281 */
a69ed767 1282 final void drawChildren() {
1e71bba2 1283 if (!isDrawable()) {
2bc32111
KL
1284 return;
1285 }
1286
d36057df
KL
1287 // Set my clipping rectangle
1288 assert (window != null);
1289 assert (getScreen() != null);
1290 Screen screen = getScreen();
1291
1292 // Special case: TStatusBar is drawn by TApplication, not anything
1293 // else.
1294 if (this instanceof TStatusBar) {
1295 return;
48e27807
KL
1296 }
1297
d36057df
KL
1298 screen.setClipRight(width);
1299 screen.setClipBottom(height);
92554d64 1300
d36057df
KL
1301 int absoluteRightEdge = window.getAbsoluteX() + window.getWidth();
1302 int absoluteBottomEdge = window.getAbsoluteY() + window.getHeight();
2bc32111
KL
1303 if (!(this instanceof TWindow)
1304 && !(this instanceof TVScroller)
a524aa2e 1305 && !(window instanceof TDesktop)
2bc32111 1306 ) {
d36057df
KL
1307 absoluteRightEdge -= 1;
1308 }
2bc32111
KL
1309 if (!(this instanceof TWindow)
1310 && !(this instanceof THScroller)
a524aa2e 1311 && !(window instanceof TDesktop)
2bc32111 1312 ) {
d36057df 1313 absoluteBottomEdge -= 1;
48e27807 1314 }
d36057df
KL
1315 int myRightEdge = getAbsoluteX() + width;
1316 int myBottomEdge = getAbsoluteY() + height;
1317 if (getAbsoluteX() > absoluteRightEdge) {
1318 // I am offscreen
1319 screen.setClipRight(0);
1320 } else if (myRightEdge > absoluteRightEdge) {
1321 screen.setClipRight(screen.getClipRight()
1322 - (myRightEdge - absoluteRightEdge));
1323 }
1324 if (getAbsoluteY() > absoluteBottomEdge) {
1325 // I am offscreen
1326 screen.setClipBottom(0);
1327 } else if (myBottomEdge > absoluteBottomEdge) {
1328 screen.setClipBottom(screen.getClipBottom()
1329 - (myBottomEdge - absoluteBottomEdge));
1330 }
1331
1332 // Set my offset
1333 screen.setOffsetX(getAbsoluteX());
1334 screen.setOffsetY(getAbsoluteY());
48e27807 1335
d36057df
KL
1336 // Draw me
1337 draw();
1e71bba2
KL
1338 if (!isDrawable()) {
1339 // An action taken by a draw method unhooked me from the UI.
1340 // Bail out.
1341 return;
1342 }
1343
11cedc9a 1344 assert (visible == true);
d36057df 1345
a69ed767
KL
1346 // Continue down the chain. Draw the active child last so that it
1347 // is on top.
48e27807 1348 for (TWidget widget: children) {
a69ed767 1349 if (widget.isVisible() && (widget != activeChild)) {
051e2913 1350 widget.drawChildren();
1e71bba2
KL
1351 if (!isDrawable()) {
1352 // An action taken by a draw method unhooked me from the UI.
1353 // Bail out.
1354 return;
1355 }
051e2913 1356 }
48e27807 1357 }
a69ed767
KL
1358 if (activeChild != null) {
1359 activeChild.drawChildren();
1360 }
48e27807
KL
1361 }
1362
1363 /**
d36057df 1364 * Repaint the screen on the next update.
48e27807 1365 */
a69ed767 1366 protected final void doRepaint() {
d36057df 1367 window.getApplication().doRepaint();
48e27807
KL
1368 }
1369
1370 /**
d36057df
KL
1371 * Add a child widget to my list of children. We set its tabOrder to 0
1372 * and increment the tabOrder of all other children.
48e27807 1373 *
d36057df 1374 * @param child TWidget to add
48e27807 1375 */
d36057df
KL
1376 private void addChild(final TWidget child) {
1377 children.add(child);
713de2aa
NR
1378 child.parent = this;
1379 child.window = this.window;
48e27807 1380
d36057df
KL
1381 if ((child.enabled)
1382 && !(child instanceof THScroller)
1383 && !(child instanceof TVScroller)
1384 ) {
1385 for (TWidget widget: children) {
1386 widget.active = false;
48e27807 1387 }
d36057df
KL
1388 child.active = true;
1389 activeChild = child;
48e27807 1390 }
d36057df
KL
1391 for (int i = 0; i < children.size(); i++) {
1392 children.get(i).tabOrder = i;
48e27807 1393 }
d8dc8aea
KL
1394 if (layout != null) {
1395 layout.add(child);
1396 }
48e27807
KL
1397 }
1398
00691e80
KL
1399 /**
1400 * Reset the tab order of children to match their position in the list.
1401 * Available so that subclasses can re-order their widgets if needed.
1402 */
1403 protected void resetTabOrder() {
1404 for (int i = 0; i < children.size(); i++) {
1405 children.get(i).tabOrder = i;
1406 }
1407 }
713de2aa
NR
1408
1409 /**
1410 * Remove and {@link TWidget#close()} the given child from this {@link TWidget}.
1411 * <p>
1412 * Will also reorder the tab values of the remaining children.
1413 *
1414 * @param child the child to remove
1415 *
1416 * @return TRUE if the child was removed, FALSE if it was not found
1417 */
1418 public boolean removeChild(final TWidget child) {
1419 if (children.remove(child)) {
1420 child.close();
1421 child.parent = null;
1422 child.window = null;
1423
1424 resetTabOrder();
1425
1426 return true;
1427 }
1428
1429 return false;
1430 }
00691e80 1431
b6faeac0 1432 /**
d36057df 1433 * Switch the active child.
b6faeac0 1434 *
d36057df 1435 * @param child TWidget to activate
b6faeac0 1436 */
d36057df
KL
1437 public final void activate(final TWidget child) {
1438 assert (child.enabled);
1439 if ((child instanceof THScroller)
1440 || (child instanceof TVScroller)
1441 ) {
1442 return;
b6faeac0 1443 }
b6faeac0 1444
e23ea538
KL
1445 if (children.size() == 1) {
1446 if (children.get(0).enabled == true) {
1447 child.active = true;
1448 activeChild = child;
1449 }
1450 } else {
1451 if (child != activeChild) {
1452 if (activeChild != null) {
1453 activeChild.active = false;
1454 }
1455 child.active = true;
1456 activeChild = child;
12b55d76 1457 }
48e27807
KL
1458 }
1459 }
1460
1461 /**
d36057df 1462 * Switch the active child.
48e27807 1463 *
d36057df
KL
1464 * @param tabOrder tabOrder of the child to activate. If that child
1465 * isn't enabled, then the next enabled child will be activated.
48e27807 1466 */
d36057df 1467 public final void activate(final int tabOrder) {
e23ea538
KL
1468 if (children.size() == 1) {
1469 if (children.get(0).enabled == true) {
1470 children.get(0).active = true;
1471 activeChild = children.get(0);
1472 }
1473 return;
1474 }
1475
d36057df 1476 TWidget child = null;
48e27807 1477 for (TWidget widget: children) {
d36057df
KL
1478 if ((widget.enabled)
1479 && !(widget instanceof THScroller)
1480 && !(widget instanceof TVScroller)
1481 && (widget.tabOrder >= tabOrder)
1482 ) {
1483 child = widget;
1484 break;
1485 }
48e27807 1486 }
d36057df 1487 if ((child != null) && (child != activeChild)) {
00691e80
KL
1488 if (activeChild != null) {
1489 activeChild.active = false;
1490 }
d36057df
KL
1491 assert (child.enabled);
1492 child.active = true;
1493 activeChild = child;
48e27807
KL
1494 }
1495 }
1496
5218e73c
KL
1497 /**
1498 * Make this widget the active child of its parent. Note that this is
1499 * not final since TWindow overrides activate().
1500 */
1501 public void activate() {
1502 if (enabled) {
1503 if (parent != null) {
1504 parent.activate(this);
1505 }
1506 }
1507 }
1508
9917c620
KL
1509 /**
1510 * Make this widget, all of its parents, the active child.
1511 */
1512 public final void activateAll() {
1513 activate();
1514 if (parent == this) {
1515 return;
1516 }
1517 if (parent != null) {
1518 parent.activateAll();
1519 }
1520 }
1521
48e27807 1522 /**
d36057df 1523 * Switch the active widget with the next in the tab order.
48e27807 1524 *
d36057df
KL
1525 * @param forward if true, then switch to the next enabled widget in the
1526 * list, otherwise switch to the previous enabled widget in the list
48e27807 1527 */
d36057df 1528 public final void switchWidget(final boolean forward) {
48e27807 1529
e23ea538 1530 // No children: do nothing.
00691e80 1531 if (children.size() == 0) {
e23ea538
KL
1532 return;
1533 }
1534
5218e73c
KL
1535 assert (parent != null);
1536
e23ea538
KL
1537 // If there is only one child, make it active if it is enabled.
1538 if (children.size() == 1) {
1539 if (children.get(0).enabled == true) {
1540 activeChild = children.get(0);
1541 activeChild.active = true;
1542 } else {
1543 children.get(0).active = false;
1544 activeChild = null;
1545 }
48e27807
KL
1546 return;
1547 }
1548
e23ea538
KL
1549 // Two or more children: go forward or backward to the next enabled
1550 // child.
00691e80
KL
1551 int tabOrder = 0;
1552 if (activeChild != null) {
1553 tabOrder = activeChild.tabOrder;
1554 }
d36057df
KL
1555 do {
1556 if (forward) {
1557 tabOrder++;
1558 } else {
1559 tabOrder--;
1560 }
1561 if (tabOrder < 0) {
48e27807 1562
d36057df
KL
1563 // If at the end, pass the switch to my parent.
1564 if ((!forward) && (parent != this)) {
1565 parent.switchWidget(forward);
1566 return;
1567 }
48e27807 1568
d36057df
KL
1569 tabOrder = children.size() - 1;
1570 } else if (tabOrder == children.size()) {
1571 // If at the end, pass the switch to my parent.
1572 if ((forward) && (parent != this)) {
1573 parent.switchWidget(forward);
1574 return;
1575 }
48e27807 1576
d36057df
KL
1577 tabOrder = 0;
1578 }
00691e80
KL
1579 if (activeChild == null) {
1580 if (tabOrder == 0) {
1581 // We wrapped around
1582 break;
1583 }
1584 } else if (activeChild.tabOrder == tabOrder) {
d36057df 1585 // We wrapped around
48e27807 1586 break;
d36057df
KL
1587 }
1588 } while ((!children.get(tabOrder).enabled)
1589 && !(children.get(tabOrder) instanceof THScroller)
1590 && !(children.get(tabOrder) instanceof TVScroller));
48e27807 1591
00691e80
KL
1592 if (activeChild != null) {
1593 assert (children.get(tabOrder).enabled);
48e27807 1594
00691e80
KL
1595 activeChild.active = false;
1596 }
1597 if (children.get(tabOrder).enabled == true) {
1598 children.get(tabOrder).active = true;
1599 activeChild = children.get(tabOrder);
1600 }
d36057df 1601 }
48e27807 1602
d36057df
KL
1603 /**
1604 * Returns my active widget.
1605 *
1606 * @return widget that is active, or this if no children
1607 */
1608 public TWidget getActiveChild() {
1609 if ((this instanceof THScroller)
1610 || (this instanceof TVScroller)
1611 ) {
1612 return parent;
1613 }
b6faeac0 1614
d36057df
KL
1615 for (TWidget widget: children) {
1616 if (widget.active) {
1617 return widget.getActiveChild();
48e27807 1618 }
48e27807 1619 }
d36057df
KL
1620 // No active children, return me
1621 return this;
48e27807
KL
1622 }
1623
469c2b3c 1624 /**
a524aa2e
KL
1625 * Insert a vertical split between this widget and parent, and optionally
1626 * put another widget in the other side of the split.
469c2b3c
KL
1627 *
1628 * @param newWidgetOnLeft if true, the new widget (if specified) will be
a524aa2e 1629 * on the left pane, and this widget will be placed on the right pane
469c2b3c
KL
1630 * @param newWidget the new widget to add to the other pane, or null
1631 * @return the new split pane widget
1632 */
1633 public TSplitPane splitVertical(final boolean newWidgetOnLeft,
1634 final TWidget newWidget) {
1635
469c2b3c 1636 TSplitPane splitPane = new TSplitPane(null, x, y, width, height, true);
a524aa2e
KL
1637 TWidget myParent = parent;
1638 remove(false);
90d87fca
KL
1639 if (myParent instanceof TSplitPane) {
1640 // TSplitPane has a left/right/top/bottom link to me somewhere,
1641 // replace it with a link to splitPane.
1642 ((TSplitPane) myParent).replaceWidget(this, splitPane);
1643 }
a524aa2e 1644 splitPane.setParent(myParent, false);
469c2b3c
KL
1645 if (newWidgetOnLeft) {
1646 splitPane.setLeft(newWidget);
a524aa2e 1647 splitPane.setRight(this);
469c2b3c 1648 } else {
a524aa2e 1649 splitPane.setLeft(this);
90d87fca 1650 splitPane.setRight(newWidget);
469c2b3c 1651 }
a524aa2e 1652 if (newWidget != null) {
9917c620 1653 newWidget.activateAll();
a524aa2e 1654 } else {
9917c620 1655 activateAll();
a524aa2e 1656 }
90d87fca 1657
a524aa2e
KL
1658 assert (parent != null);
1659 assert (window != null);
469c2b3c
KL
1660 assert (splitPane.getWindow() != null);
1661 assert (splitPane.getParent() != null);
469c2b3c 1662 assert (splitPane.isActive() == true);
90d87fca
KL
1663 assert (parent == splitPane);
1664 if (newWidget != null) {
1665 assert (newWidget.parent == parent);
1666 assert (newWidget.active == true);
1667 assert (active == false);
1668 } else {
1669 assert (active == true);
1670 }
469c2b3c
KL
1671 return splitPane;
1672 }
1673
1674 /**
a524aa2e
KL
1675 * Insert a horizontal split between this widget and parent, and
1676 * optionally put another widget in the other side of the split.
469c2b3c
KL
1677 *
1678 * @param newWidgetOnTop if true, the new widget (if specified) will be
1679 * on the top pane, and this widget's children will be placed on the
1680 * bottom pane
1681 * @param newWidget the new widget to add to the other pane, or null
1682 * @return the new split pane widget
1683 */
1684 public TSplitPane splitHorizontal(final boolean newWidgetOnTop,
1685 final TWidget newWidget) {
1686
469c2b3c 1687 TSplitPane splitPane = new TSplitPane(null, x, y, width, height, false);
a524aa2e
KL
1688 TWidget myParent = parent;
1689 remove(false);
90d87fca
KL
1690 if (myParent instanceof TSplitPane) {
1691 // TSplitPane has a left/right/top/bottom link to me somewhere,
1692 // replace it with a link to splitPane.
1693 ((TSplitPane) myParent).replaceWidget(this, splitPane);
1694 }
a524aa2e 1695 splitPane.setParent(myParent, false);
469c2b3c
KL
1696 if (newWidgetOnTop) {
1697 splitPane.setTop(newWidget);
a524aa2e 1698 splitPane.setBottom(this);
469c2b3c 1699 } else {
a524aa2e 1700 splitPane.setTop(this);
90d87fca 1701 splitPane.setBottom(newWidget);
a524aa2e 1702 }
a524aa2e 1703 if (newWidget != null) {
9917c620 1704 newWidget.activateAll();
a524aa2e 1705 } else {
9917c620 1706 activateAll();
469c2b3c 1707 }
90d87fca 1708
a524aa2e
KL
1709 assert (parent != null);
1710 assert (window != null);
469c2b3c
KL
1711 assert (splitPane.getWindow() != null);
1712 assert (splitPane.getParent() != null);
469c2b3c 1713 assert (splitPane.isActive() == true);
90d87fca
KL
1714 assert (parent == splitPane);
1715 if (newWidget != null) {
1716 assert (newWidget.parent == parent);
1717 assert (newWidget.active == true);
1718 assert (active == false);
1719 } else {
1720 assert (active == true);
1721 }
469c2b3c
KL
1722 return splitPane;
1723 }
1724
90d87fca
KL
1725 /**
1726 * Generate a human-readable string for this widget.
1727 *
1728 * @return a human-readable string
1729 */
1730 @Override
1731 public String toString() {
1732 return String.format("%s(%8x) position (%d, %d) geometry %dx%d " +
1733 "active %s enabled %s visible %s", getClass().getName(),
1734 hashCode(), x, y, width, height, active, enabled, visible);
1735 }
1736
1737 /**
1738 * Generate a string for this widget's hierarchy.
1739 *
1740 * @param prefix a prefix to use for this widget's place in the hierarchy
1741 * @return a pretty-printable string of this hierarchy
1742 */
1743 protected String toPrettyString(final String prefix) {
1744 StringBuilder sb = new StringBuilder(prefix);
1745 sb.append(toString());
1746 String newPrefix = "";
1747 for (int i = 0; i < prefix.length(); i++) {
1748 newPrefix += " ";
1749 }
1750 for (int i = 0; i < children.size(); i++) {
1751 TWidget child= children.get(i);
1752 sb.append("\n");
1753 if (i == children.size() - 1) {
1754 sb.append(child.toPrettyString(newPrefix + " \u2514\u2500"));
1755 } else {
1756 sb.append(child.toPrettyString(newPrefix + " \u251c\u2500"));
1757 }
1758 }
1759 return sb.toString();
1760 }
1761
1762 /**
1763 * Generate a string for this widget's hierarchy.
1764 *
1765 * @return a pretty-printable string of this hierarchy
1766 */
1767 public String toPrettyString() {
1768 return toPrettyString("");
1769 }
1770
a69ed767
KL
1771 // ------------------------------------------------------------------------
1772 // Passthru for Screen functions ------------------------------------------
1773 // ------------------------------------------------------------------------
1774
1775 /**
1776 * Get the attributes at one location.
1777 *
1778 * @param x column coordinate. 0 is the left-most column.
1779 * @param y row coordinate. 0 is the top-most row.
1780 * @return attributes at (x, y)
1781 */
1782 protected final CellAttributes getAttrXY(final int x, final int y) {
1783 return getScreen().getAttrXY(x, y);
1784 }
1785
1786 /**
1787 * Set the attributes at one location.
1788 *
1789 * @param x column coordinate. 0 is the left-most column.
1790 * @param y row coordinate. 0 is the top-most row.
1791 * @param attr attributes to use (bold, foreColor, backColor)
1792 */
1793 protected final void putAttrXY(final int x, final int y,
1794 final CellAttributes attr) {
1795
1796 getScreen().putAttrXY(x, y, attr);
1797 }
1798
1799 /**
1800 * Set the attributes at one location.
1801 *
1802 * @param x column coordinate. 0 is the left-most column.
1803 * @param y row coordinate. 0 is the top-most row.
1804 * @param attr attributes to use (bold, foreColor, backColor)
1805 * @param clip if true, honor clipping/offset
1806 */
1807 protected final void putAttrXY(final int x, final int y,
1808 final CellAttributes attr, final boolean clip) {
1809
1810 getScreen().putAttrXY(x, y, attr, clip);
1811 }
1812
1813 /**
1814 * Fill the entire screen with one character with attributes.
1815 *
1816 * @param ch character to draw
1817 * @param attr attributes to use (bold, foreColor, backColor)
1818 */
218d18db 1819 protected final void putAll(final int ch, final CellAttributes attr) {
a69ed767
KL
1820 getScreen().putAll(ch, attr);
1821 }
1822
1823 /**
1824 * Render one character with attributes.
1825 *
1826 * @param x column coordinate. 0 is the left-most column.
1827 * @param y row coordinate. 0 is the top-most row.
1828 * @param ch character + attributes to draw
1829 */
1830 protected final void putCharXY(final int x, final int y, final Cell ch) {
1831 getScreen().putCharXY(x, y, ch);
1832 }
1833
1834 /**
1835 * Render one character with attributes.
1836 *
1837 * @param x column coordinate. 0 is the left-most column.
1838 * @param y row coordinate. 0 is the top-most row.
1839 * @param ch character to draw
1840 * @param attr attributes to use (bold, foreColor, backColor)
1841 */
218d18db 1842 protected final void putCharXY(final int x, final int y, final int ch,
a69ed767
KL
1843 final CellAttributes attr) {
1844
1845 getScreen().putCharXY(x, y, ch, attr);
1846 }
1847
1848 /**
1849 * Render one character without changing the underlying attributes.
1850 *
1851 * @param x column coordinate. 0 is the left-most column.
1852 * @param y row coordinate. 0 is the top-most row.
1853 * @param ch character to draw
1854 */
218d18db 1855 protected final void putCharXY(final int x, final int y, final int ch) {
a69ed767
KL
1856 getScreen().putCharXY(x, y, ch);
1857 }
1858
1859 /**
1860 * Render a string. Does not wrap if the string exceeds the line.
1861 *
1862 * @param x column coordinate. 0 is the left-most column.
1863 * @param y row coordinate. 0 is the top-most row.
1864 * @param str string to draw
1865 * @param attr attributes to use (bold, foreColor, backColor)
1866 */
1867 protected final void putStringXY(final int x, final int y, final String str,
1868 final CellAttributes attr) {
1869
1870 getScreen().putStringXY(x, y, str, attr);
1871 }
1872
1873 /**
1874 * Render a string without changing the underlying attribute. Does not
1875 * wrap if the string exceeds the line.
1876 *
1877 * @param x column coordinate. 0 is the left-most column.
1878 * @param y row coordinate. 0 is the top-most row.
1879 * @param str string to draw
1880 */
1881 protected final void putStringXY(final int x, final int y, final String str) {
1882 getScreen().putStringXY(x, y, str);
1883 }
1884
1885 /**
1886 * Draw a vertical line from (x, y) to (x, y + n).
1887 *
1888 * @param x column coordinate. 0 is the left-most column.
1889 * @param y row coordinate. 0 is the top-most row.
1890 * @param n number of characters to draw
1891 * @param ch character to draw
1892 * @param attr attributes to use (bold, foreColor, backColor)
1893 */
1894 protected final void vLineXY(final int x, final int y, final int n,
218d18db 1895 final int ch, final CellAttributes attr) {
a69ed767
KL
1896
1897 getScreen().vLineXY(x, y, n, ch, attr);
1898 }
1899
1900 /**
1901 * Draw a horizontal line from (x, y) to (x + n, y).
1902 *
1903 * @param x column coordinate. 0 is the left-most column.
1904 * @param y row coordinate. 0 is the top-most row.
1905 * @param n number of characters to draw
1906 * @param ch character to draw
1907 * @param attr attributes to use (bold, foreColor, backColor)
1908 */
1909 protected final void hLineXY(final int x, final int y, final int n,
218d18db 1910 final int ch, final CellAttributes attr) {
a69ed767
KL
1911
1912 getScreen().hLineXY(x, y, n, ch, attr);
1913 }
1914
1915 /**
1916 * Draw a box with a border and empty background.
1917 *
1918 * @param left left column of box. 0 is the left-most row.
1919 * @param top top row of the box. 0 is the top-most row.
1920 * @param right right column of box
1921 * @param bottom bottom row of the box
1922 * @param border attributes to use for the border
1923 * @param background attributes to use for the background
1924 */
1925 protected final void drawBox(final int left, final int top,
1926 final int right, final int bottom,
1927 final CellAttributes border, final CellAttributes background) {
1928
1929 getScreen().drawBox(left, top, right, bottom, border, background);
1930 }
1931
1932 /**
1933 * Draw a box with a border and empty background.
1934 *
1935 * @param left left column of box. 0 is the left-most row.
1936 * @param top top row of the box. 0 is the top-most row.
1937 * @param right right column of box
1938 * @param bottom bottom row of the box
1939 * @param border attributes to use for the border
1940 * @param background attributes to use for the background
1941 * @param borderType if 1, draw a single-line border; if 2, draw a
1942 * double-line border; if 3, draw double-line top/bottom edges and
1943 * single-line left/right edges (like Qmodem)
1944 * @param shadow if true, draw a "shadow" on the box
1945 */
1946 protected final void drawBox(final int left, final int top,
1947 final int right, final int bottom,
1948 final CellAttributes border, final CellAttributes background,
1949 final int borderType, final boolean shadow) {
1950
1951 getScreen().drawBox(left, top, right, bottom, border, background,
1952 borderType, shadow);
1953 }
1954
1955 /**
1956 * Draw a box shadow.
1957 *
1958 * @param left left column of box. 0 is the left-most row.
1959 * @param top top row of the box. 0 is the top-most row.
1960 * @param right right column of box
1961 * @param bottom bottom row of the box
1962 */
1963 protected final void drawBoxShadow(final int left, final int top,
1964 final int right, final int bottom) {
1965
1966 getScreen().drawBoxShadow(left, top, right, bottom);
1967 }
1968
2ce6dab2
KL
1969 // ------------------------------------------------------------------------
1970 // Other TWidget constructors ---------------------------------------------
1971 // ------------------------------------------------------------------------
48e27807 1972
30d336cc
KL
1973 /**
1974 * Convenience function to add a label to this container/window.
1975 *
1976 * @param text label
1977 * @param x column relative to parent
1978 * @param y row relative to parent
1979 * @return the new label
1980 */
1981 public final TLabel addLabel(final String text, final int x, final int y) {
1982 return addLabel(text, x, y, "tlabel");
1983 }
1984
00691e80
KL
1985 /**
1986 * Convenience function to add a label to this container/window.
1987 *
1988 * @param text label
1989 * @param x column relative to parent
1990 * @param y row relative to parent
1991 * @param action to call when shortcut is pressed
1992 * @return the new label
1993 */
1994 public final TLabel addLabel(final String text, final int x, final int y,
1995 final TAction action) {
1996
1997 return addLabel(text, x, y, "tlabel", action);
1998 }
1999
30d336cc
KL
2000 /**
2001 * Convenience function to add a label to this container/window.
2002 *
2003 * @param text label
2004 * @param x column relative to parent
2005 * @param y row relative to parent
2006 * @param colorKey ColorTheme key color to use for foreground text.
2007 * Default is "tlabel"
2008 * @return the new label
2009 */
2010 public final TLabel addLabel(final String text, final int x, final int y,
2011 final String colorKey) {
2012
2013 return new TLabel(this, text, x, y, colorKey);
2014 }
2015
00691e80
KL
2016 /**
2017 * Convenience function to add a label to this container/window.
2018 *
2019 * @param text label
2020 * @param x column relative to parent
2021 * @param y row relative to parent
2022 * @param colorKey ColorTheme key color to use for foreground text.
2023 * Default is "tlabel"
2024 * @param action to call when shortcut is pressed
2025 * @return the new label
2026 */
2027 public final TLabel addLabel(final String text, final int x, final int y,
2028 final String colorKey, final TAction action) {
2029
2030 return new TLabel(this, text, x, y, colorKey, action);
2031 }
2032
051e2913
KL
2033 /**
2034 * Convenience function to add a label to this container/window.
2035 *
2036 * @param text label
2037 * @param x column relative to parent
2038 * @param y row relative to parent
2039 * @param colorKey ColorTheme key color to use for foreground text.
2040 * Default is "tlabel"
2041 * @param useWindowBackground if true, use the window's background color
2042 * @return the new label
2043 */
2044 public final TLabel addLabel(final String text, final int x, final int y,
2045 final String colorKey, final boolean useWindowBackground) {
2046
2047 return new TLabel(this, text, x, y, colorKey, useWindowBackground);
2048 }
2049
00691e80
KL
2050 /**
2051 * Convenience function to add a label to this container/window.
2052 *
2053 * @param text label
2054 * @param x column relative to parent
2055 * @param y row relative to parent
2056 * @param colorKey ColorTheme key color to use for foreground text.
2057 * Default is "tlabel"
2058 * @param useWindowBackground if true, use the window's background color
2059 * @param action to call when shortcut is pressed
2060 * @return the new label
2061 */
2062 public final TLabel addLabel(final String text, final int x, final int y,
2063 final String colorKey, final boolean useWindowBackground,
2064 final TAction action) {
2065
2066 return new TLabel(this, text, x, y, colorKey, useWindowBackground,
2067 action);
2068 }
2069
30d336cc
KL
2070 /**
2071 * Convenience function to add a button to this container/window.
2072 *
2073 * @param text label on the button
2074 * @param x column relative to parent
2075 * @param y row relative to parent
051e2913 2076 * @param action action to call when button is pressed
30d336cc
KL
2077 * @return the new button
2078 */
2079 public final TButton addButton(final String text, final int x, final int y,
2080 final TAction action) {
2081
2082 return new TButton(this, text, x, y, action);
2083 }
2084
7272e49f
KL
2085 /**
2086 * Convenience function to add a checkbox to this container/window.
2087 *
2088 * @param x column relative to parent
2089 * @param y row relative to parent
2090 * @param label label to display next to (right of) the checkbox
2091 * @param checked initial check state
2092 * @return the new checkbox
2093 */
051e2913 2094 public final TCheckBox addCheckBox(final int x, final int y,
7272e49f
KL
2095 final String label, final boolean checked) {
2096
051e2913
KL
2097 return new TCheckBox(this, x, y, label, checked);
2098 }
2099
2100 /**
2101 * Convenience function to add a combobox to this container/window.
2102 *
2103 * @param x column relative to parent
2104 * @param y row relative to parent
2105 * @param width visible combobox width, including the down-arrow
2106 * @param values the possible values for the box, shown in the drop-down
2107 * @param valuesIndex the initial index in values, or -1 for no default
2108 * value
8ab60a33
KL
2109 * @param maxValuesHeight the maximum height of the values drop-down when
2110 * it is visible
051e2913
KL
2111 * @param updateAction action to call when a new value is selected from
2112 * the list or enter is pressed in the edit field
2113 * @return the new combobox
2114 */
2115 public final TComboBox addComboBox(final int x, final int y,
2116 final int width, final List<String> values, final int valuesIndex,
8ab60a33 2117 final int maxValuesHeight, final TAction updateAction) {
051e2913
KL
2118
2119 return new TComboBox(this, x, y, width, values, valuesIndex,
8ab60a33 2120 maxValuesHeight, updateAction);
051e2913
KL
2121 }
2122
2123 /**
2124 * Convenience function to add a spinner to this container/window.
2125 *
2126 * @param x column relative to parent
2127 * @param y row relative to parent
2128 * @param upAction action to call when the up arrow is clicked or pressed
2129 * @param downAction action to call when the down arrow is clicked or
2130 * pressed
2131 * @return the new spinner
2132 */
2133 public final TSpinner addSpinner(final int x, final int y,
2134 final TAction upAction, final TAction downAction) {
2135
2136 return new TSpinner(this, x, y, upAction, downAction);
2137 }
2138
2139 /**
2140 * Convenience function to add a calendar to this container/window.
2141 *
2142 * @param x column relative to parent
2143 * @param y row relative to parent
2144 * @param updateAction action to call when the user changes the value of
2145 * the calendar
2146 * @return the new calendar
2147 */
2148 public final TCalendar addCalendar(final int x, final int y,
2149 final TAction updateAction) {
2150
2151 return new TCalendar(this, x, y, updateAction);
7272e49f 2152 }
30d336cc 2153
d502a0e9
KL
2154 /**
2155 * Convenience function to add a progress bar to this container/window.
2156 *
2157 * @param x column relative to parent
2158 * @param y row relative to parent
2159 * @param width width of progress bar
2160 * @param value initial value of percent complete
00d2622b 2161 * @return the new progress bar
d502a0e9
KL
2162 */
2163 public final TProgressBar addProgressBar(final int x, final int y,
2164 final int width, final int value) {
2165
2166 return new TProgressBar(this, x, y, width, value);
2167 }
2168
00d2622b
KL
2169 /**
2170 * Convenience function to add a radio button group to this
2171 * container/window.
2172 *
2173 * @param x column relative to parent
2174 * @param y row relative to parent
2175 * @param label label to display on the group box
2176 * @return the new radio button group
2177 */
2178 public final TRadioGroup addRadioGroup(final int x, final int y,
2179 final String label) {
2180
2181 return new TRadioGroup(this, x, y, label);
2182 }
2183
128e5be1
KL
2184 /**
2185 * Convenience function to add a text field to this container/window.
2186 *
2187 * @param x column relative to parent
2188 * @param y row relative to parent
2189 * @param width visible text width
2190 * @param fixed if true, the text cannot exceed the display width
2191 * @return the new text field
2192 */
2193 public final TField addField(final int x, final int y,
2194 final int width, final boolean fixed) {
2195
2196 return new TField(this, x, y, width, fixed);
2197 }
2198
2199 /**
2200 * Convenience function to add a text field to this container/window.
2201 *
2202 * @param x column relative to parent
2203 * @param y row relative to parent
2204 * @param width visible text width
2205 * @param fixed if true, the text cannot exceed the display width
2206 * @param text initial text, default is empty string
2207 * @return the new text field
2208 */
2209 public final TField addField(final int x, final int y,
2210 final int width, final boolean fixed, final String text) {
2211
2212 return new TField(this, x, y, width, fixed, text);
2213 }
2214
2215 /**
2216 * Convenience function to add a text field to this container/window.
2217 *
2218 * @param x column relative to parent
2219 * @param y row relative to parent
2220 * @param width visible text width
2221 * @param fixed if true, the text cannot exceed the display width
2222 * @param text initial text, default is empty string
2223 * @param enterAction function to call when enter key is pressed
2224 * @param updateAction function to call when the text is updated
2225 * @return the new text field
2226 */
2227 public final TField addField(final int x, final int y,
2228 final int width, final boolean fixed, final String text,
2229 final TAction enterAction, final TAction updateAction) {
2230
2231 return new TField(this, x, y, width, fixed, text, enterAction,
2232 updateAction);
2233 }
00d2622b 2234
cc99cba8
KL
2235 /**
2236 * Convenience function to add a scrollable text box to this
2237 * container/window.
2238 *
2239 * @param text text on the screen
2240 * @param x column relative to parent
2241 * @param y row relative to parent
2242 * @param width width of text area
2243 * @param height height of text area
2244 * @param colorKey ColorTheme key color to use for foreground text
2245 * @return the new text box
2246 */
c6940ed9 2247 public final TText addText(final String text, final int x,
cc99cba8
KL
2248 final int y, final int width, final int height, final String colorKey) {
2249
2250 return new TText(this, text, x, y, width, height, colorKey);
2251 }
2252
2253 /**
2254 * Convenience function to add a scrollable text box to this
2255 * container/window.
2256 *
2257 * @param text text on the screen
2258 * @param x column relative to parent
2259 * @param y row relative to parent
2260 * @param width width of text area
2261 * @param height height of text area
2262 * @return the new text box
2263 */
c6940ed9 2264 public final TText addText(final String text, final int x, final int y,
cc99cba8
KL
2265 final int width, final int height) {
2266
2267 return new TText(this, text, x, y, width, height, "ttext");
2268 }
2269
12b55d76
KL
2270 /**
2271 * Convenience function to add an editable text area box to this
2272 * container/window.
2273 *
2274 * @param text text on the screen
2275 * @param x column relative to parent
2276 * @param y row relative to parent
2277 * @param width width of text area
2278 * @param height height of text area
2279 * @return the new text box
2280 */
2281 public final TEditorWidget addEditor(final String text, final int x,
2282 final int y, final int width, final int height) {
2283
2284 return new TEditorWidget(this, text, x, y, width, height);
2285 }
2286
c6940ed9
KL
2287 /**
2288 * Convenience function to spawn a message box.
2289 *
2290 * @param title window title, will be centered along the top border
2291 * @param caption message to display. Use embedded newlines to get a
2292 * multi-line box.
2293 * @return the new message box
2294 */
2295 public final TMessageBox messageBox(final String title,
2296 final String caption) {
2297
2298 return getApplication().messageBox(title, caption, TMessageBox.Type.OK);
2299 }
2300
2301 /**
2302 * Convenience function to spawn a message box.
2303 *
2304 * @param title window title, will be centered along the top border
2305 * @param caption message to display. Use embedded newlines to get a
2306 * multi-line box.
2307 * @param type one of the TMessageBox.Type constants. Default is
2308 * Type.OK.
2309 * @return the new message box
2310 */
2311 public final TMessageBox messageBox(final String title,
2312 final String caption, final TMessageBox.Type type) {
2313
2314 return getApplication().messageBox(title, caption, type);
2315 }
2316
2317 /**
2318 * Convenience function to spawn an input box.
2319 *
2320 * @param title window title, will be centered along the top border
2321 * @param caption message to display. Use embedded newlines to get a
2322 * multi-line box.
2323 * @return the new input box
2324 */
2325 public final TInputBox inputBox(final String title, final String caption) {
2326
2327 return getApplication().inputBox(title, caption);
2328 }
2329
2330 /**
2331 * Convenience function to spawn an input box.
2332 *
2333 * @param title window title, will be centered along the top border
2334 * @param caption message to display. Use embedded newlines to get a
2335 * multi-line box.
2336 * @param text initial text to seed the field with
2337 * @return the new input box
2338 */
2339 public final TInputBox inputBox(final String title, final String caption,
2340 final String text) {
2341
2342 return getApplication().inputBox(title, caption, text);
2343 }
cc99cba8 2344
2b427404
KL
2345 /**
2346 * Convenience function to spawn an input box.
2347 *
2348 * @param title window title, will be centered along the top border
2349 * @param caption message to display. Use embedded newlines to get a
2350 * multi-line box.
2351 * @param text initial text to seed the field with
2352 * @param type one of the Type constants. Default is Type.OK.
2353 * @return the new input box
2354 */
2355 public final TInputBox inputBox(final String title, final String caption,
2356 final String text, final TInputBox.Type type) {
2357
2358 return getApplication().inputBox(title, caption, text, type);
2359 }
2360
87a17f3c
KL
2361 /**
2362 * Convenience function to add a password text field to this
2363 * container/window.
2364 *
2365 * @param x column relative to parent
2366 * @param y row relative to parent
2367 * @param width visible text width
2368 * @param fixed if true, the text cannot exceed the display width
2369 * @return the new text field
2370 */
2371 public final TPasswordField addPasswordField(final int x, final int y,
2372 final int width, final boolean fixed) {
2373
2374 return new TPasswordField(this, x, y, width, fixed);
2375 }
2376
2377 /**
2378 * Convenience function to add a password text field to this
2379 * container/window.
2380 *
2381 * @param x column relative to parent
2382 * @param y row relative to parent
2383 * @param width visible text width
2384 * @param fixed if true, the text cannot exceed the display width
2385 * @param text initial text, default is empty string
2386 * @return the new text field
2387 */
2388 public final TPasswordField addPasswordField(final int x, final int y,
2389 final int width, final boolean fixed, final String text) {
2390
2391 return new TPasswordField(this, x, y, width, fixed, text);
2392 }
2393
2394 /**
2395 * Convenience function to add a password text field to this
2396 * container/window.
2397 *
2398 * @param x column relative to parent
2399 * @param y row relative to parent
2400 * @param width visible text width
2401 * @param fixed if true, the text cannot exceed the display width
2402 * @param text initial text, default is empty string
2403 * @param enterAction function to call when enter key is pressed
2404 * @param updateAction function to call when the text is updated
2405 * @return the new text field
2406 */
2407 public final TPasswordField addPasswordField(final int x, final int y,
2408 final int width, final boolean fixed, final String text,
2409 final TAction enterAction, final TAction updateAction) {
2410
2411 return new TPasswordField(this, x, y, width, fixed, text, enterAction,
2412 updateAction);
2413 }
2414
7668cb45 2415 /**
d36057df
KL
2416 * Convenience function to add a scrollable tree view to this
2417 * container/window.
7668cb45
KL
2418 *
2419 * @param x column relative to parent
2420 * @param y row relative to parent
2421 * @param width width of tree view
2422 * @param height height of tree view
329fd62e 2423 * @return the new tree view
7668cb45 2424 */
d36057df 2425 public final TTreeViewWidget addTreeViewWidget(final int x, final int y,
7668cb45
KL
2426 final int width, final int height) {
2427
d36057df 2428 return new TTreeViewWidget(this, x, y, width, height);
7668cb45
KL
2429 }
2430
2431 /**
d36057df
KL
2432 * Convenience function to add a scrollable tree view to this
2433 * container/window.
7668cb45
KL
2434 *
2435 * @param x column relative to parent
2436 * @param y row relative to parent
2437 * @param width width of tree view
2438 * @param height height of tree view
2439 * @param action action to perform when an item is selected
329fd62e 2440 * @return the new tree view
7668cb45 2441 */
d36057df 2442 public final TTreeViewWidget addTreeViewWidget(final int x, final int y,
7668cb45
KL
2443 final int width, final int height, final TAction action) {
2444
d36057df 2445 return new TTreeViewWidget(this, x, y, width, height, action);
7668cb45
KL
2446 }
2447
0d47c546
KL
2448 /**
2449 * Convenience function to spawn a file open box.
2450 *
2451 * @param path path of selected file
2452 * @return the result of the new file open box
329fd62e 2453 * @throws IOException if a java.io operation throws
0d47c546
KL
2454 */
2455 public final String fileOpenBox(final String path) throws IOException {
2456 return getApplication().fileOpenBox(path);
2457 }
2458
a69ed767
KL
2459 /**
2460 * Convenience function to spawn a file save box.
2461 *
2462 * @param path path of selected file
2463 * @return the result of the new file open box
2464 * @throws IOException if a java.io operation throws
2465 */
2466 public final String fileSaveBox(final String path) throws IOException {
2467 return getApplication().fileOpenBox(path, TFileOpenBox.Type.SAVE);
2468 }
2469
0d47c546
KL
2470 /**
2471 * Convenience function to spawn a file open box.
2472 *
2473 * @param path path of selected file
2474 * @param type one of the Type constants
2475 * @return the result of the new file open box
329fd62e 2476 * @throws IOException if a java.io operation throws
0d47c546
KL
2477 */
2478 public final String fileOpenBox(final String path,
2479 final TFileOpenBox.Type type) throws IOException {
2480
2481 return getApplication().fileOpenBox(path, type);
2482 }
a69ed767
KL
2483
2484 /**
2485 * Convenience function to spawn a file open box.
2486 *
2487 * @param path path of selected file
2488 * @param type one of the Type constants
2489 * @param filter a string that files must match to be displayed
2490 * @return the result of the new file open box
2491 * @throws IOException of a java.io operation throws
2492 */
2493 public final String fileOpenBox(final String path,
2494 final TFileOpenBox.Type type, final String filter) throws IOException {
2495
2496 ArrayList<String> filters = new ArrayList<String>();
2497 filters.add(filter);
2498
2499 return getApplication().fileOpenBox(path, type, filters);
2500 }
2501
2502 /**
2503 * Convenience function to spawn a file open box.
2504 *
2505 * @param path path of selected file
2506 * @param type one of the Type constants
2507 * @param filters a list of strings that files must match to be displayed
2508 * @return the result of the new file open box
2509 * @throws IOException of a java.io operation throws
2510 */
2511 public final String fileOpenBox(final String path,
2512 final TFileOpenBox.Type type,
2513 final List<String> filters) throws IOException {
2514
2515 return getApplication().fileOpenBox(path, type, filters);
2516 }
2517
0d47c546
KL
2518 /**
2519 * Convenience function to add a directory list to this container/window.
2520 *
2521 * @param path directory path, must be a directory
2522 * @param x column relative to parent
2523 * @param y row relative to parent
2524 * @param width width of text area
2525 * @param height height of text area
329fd62e 2526 * @return the new directory list
0d47c546
KL
2527 */
2528 public final TDirectoryList addDirectoryList(final String path, final int x,
2529 final int y, final int width, final int height) {
2530
2531 return new TDirectoryList(this, path, x, y, width, height, null);
2532 }
2533
2534 /**
2535 * Convenience function to add a directory list to this container/window.
2536 *
2537 * @param path directory path, must be a directory
2538 * @param x column relative to parent
2539 * @param y row relative to parent
2540 * @param width width of text area
2541 * @param height height of text area
a69ed767
KL
2542 * @param action action to perform when an item is selected (enter or
2543 * double-click)
329fd62e 2544 * @return the new directory list
0d47c546
KL
2545 */
2546 public final TDirectoryList addDirectoryList(final String path, final int x,
2547 final int y, final int width, final int height, final TAction action) {
2548
2549 return new TDirectoryList(this, path, x, y, width, height, action);
2550 }
7668cb45 2551
3649b921
KL
2552 /**
2553 * Convenience function to add a directory list to this container/window.
2554 *
a69ed767
KL
2555 * @param path directory path, must be a directory
2556 * @param x column relative to parent
2557 * @param y row relative to parent
2558 * @param width width of text area
2559 * @param height height of text area
2560 * @param action action to perform when an item is selected (enter or
2561 * double-click)
2562 * @param singleClickAction action to perform when an item is selected
2563 * (single-click)
2564 * @return the new directory list
2565 */
2566 public final TDirectoryList addDirectoryList(final String path, final int x,
2567 final int y, final int width, final int height, final TAction action,
2568 final TAction singleClickAction) {
2569
2570 return new TDirectoryList(this, path, x, y, width, height, action,
2571 singleClickAction);
2572 }
2573
2574 /**
2575 * Convenience function to add a directory list to this container/window.
2576 *
2577 * @param path directory path, must be a directory
2578 * @param x column relative to parent
2579 * @param y row relative to parent
2580 * @param width width of text area
2581 * @param height height of text area
2582 * @param action action to perform when an item is selected (enter or
2583 * double-click)
2584 * @param singleClickAction action to perform when an item is selected
2585 * (single-click)
2586 * @param filters a list of strings that files must match to be displayed
2587 * @return the new directory list
2588 */
2589 public final TDirectoryList addDirectoryList(final String path, final int x,
2590 final int y, final int width, final int height, final TAction action,
2591 final TAction singleClickAction, final List<String> filters) {
2592
2593 return new TDirectoryList(this, path, x, y, width, height, action,
2594 singleClickAction, filters);
2595 }
2596
2597 /**
2598 * Convenience function to add a list to this container/window.
2599 *
3649b921
KL
2600 * @param strings list of strings to show
2601 * @param x column relative to parent
2602 * @param y row relative to parent
2603 * @param width width of text area
2604 * @param height height of text area
2605 * @return the new directory list
2606 */
2607 public final TList addList(final List<String> strings, final int x,
2608 final int y, final int width, final int height) {
2609
2610 return new TList(this, strings, x, y, width, height, null);
2611 }
2612
2613 /**
a69ed767 2614 * Convenience function to add a list to this container/window.
3649b921
KL
2615 *
2616 * @param strings list of strings to show
2617 * @param x column relative to parent
2618 * @param y row relative to parent
2619 * @param width width of text area
2620 * @param height height of text area
2621 * @param enterAction action to perform when an item is selected
2622 * @return the new directory list
2623 */
2624 public final TList addList(final List<String> strings, final int x,
2625 final int y, final int width, final int height,
2626 final TAction enterAction) {
2627
2628 return new TList(this, strings, x, y, width, height, enterAction);
2629 }
2630
2631 /**
a69ed767 2632 * Convenience function to add a list to this container/window.
3649b921
KL
2633 *
2634 * @param strings list of strings to show
2635 * @param x column relative to parent
2636 * @param y row relative to parent
2637 * @param width width of text area
2638 * @param height height of text area
2639 * @param enterAction action to perform when an item is selected
2640 * @param moveAction action to perform when the user navigates to a new
2641 * item with arrow/page keys
2642 * @return the new directory list
2643 */
2644 public final TList addList(final List<String> strings, final int x,
2645 final int y, final int width, final int height,
2646 final TAction enterAction, final TAction moveAction) {
2647
2648 return new TList(this, strings, x, y, width, height, enterAction,
2649 moveAction);
2650 }
2651
9577a9d0
KL
2652 /**
2653 * Convenience function to add a list to this container/window.
2654 *
2655 * @param strings list of strings to show. This is allowed to be null
2656 * and set later with setList() or by subclasses.
2657 * @param x column relative to parent
2658 * @param y row relative to parent
2659 * @param width width of text area
2660 * @param height height of text area
2661 * @param enterAction action to perform when an item is selected
2662 * @param moveAction action to perform when the user navigates to a new
2663 * item with arrow/page keys
2664 * @param singleClickAction action to perform when the user clicks on an
2665 * item
2666 */
2667 public TList addList(final List<String> strings, final int x,
2668 final int y, final int width, final int height,
2669 final TAction enterAction, final TAction moveAction,
2670 final TAction singleClickAction) {
2671
2672 return new TList(this, strings, x, y, width, height, enterAction,
2673 moveAction, singleClickAction);
2674 }
2675
2676
382bc294
KL
2677 /**
2678 * Convenience function to add an image to this container/window.
2679 *
2680 * @param x column relative to parent
2681 * @param y row relative to parent
2682 * @param width number of text cells for width of the image
2683 * @param height number of text cells for height of the image
2684 * @param image the image to display
2685 * @param left left column of the image. 0 is the left-most column.
2686 * @param top top row of the image. 0 is the top-most row.
2687 */
2688 public final TImage addImage(final int x, final int y,
2689 final int width, final int height,
2690 final BufferedImage image, final int left, final int top) {
2691
2692 return new TImage(this, x, y, width, height, image, left, top);
2693 }
2694
2695 /**
2696 * Convenience function to add an image to this container/window.
2697 *
2698 * @param x column relative to parent
2699 * @param y row relative to parent
2700 * @param width number of text cells for width of the image
2701 * @param height number of text cells for height of the image
2702 * @param image the image to display
2703 * @param left left column of the image. 0 is the left-most column.
2704 * @param top top row of the image. 0 is the top-most row.
2705 * @param clickAction function to call when mouse is pressed
2706 */
2707 public final TImage addImage(final int x, final int y,
2708 final int width, final int height,
2709 final BufferedImage image, final int left, final int top,
2710 final TAction clickAction) {
2711
2712 return new TImage(this, x, y, width, height, image, left, top,
2713 clickAction);
2714 }
2715
2716 /**
2717 * Convenience function to add an editable 2D data table to this
2718 * container/window.
2719 *
2720 * @param x column relative to parent
2721 * @param y row relative to parent
2722 * @param width width of widget
2723 * @param height height of widget
2724 */
2725 public TTableWidget addTable(final int x, final int y, final int width,
2726 final int height) {
2727
2728 return new TTableWidget(this, x, y, width, height);
2729 }
2730
656c0ddd
KL
2731 /**
2732 * Convenience function to add an editable 2D data table to this
2733 * container/window.
2734 *
2735 * @param x column relative to parent
2736 * @param y row relative to parent
2737 * @param width width of widget
2738 * @param height height of widget
2739 * @param gridColumns number of columns in grid
2740 * @param gridRows number of rows in grid
2741 */
2742 public TTableWidget addTable(final int x, final int y, final int width,
2743 final int height, final int gridColumns, final int gridRows) {
2744
2745 return new TTableWidget(this, x, y, width, height, gridColumns,
2746 gridRows);
2747 }
2748
fc2af494
KL
2749 /**
2750 * Convenience function to add a panel to this container/window.
2751 *
2752 * @param x column relative to parent
2753 * @param y row relative to parent
2754 * @param width width of text area
2755 * @param height height of text area
2756 * @return the new panel
2757 */
2758 public final TPanel addPanel(final int x, final int y, final int width,
2759 final int height) {
2760
2761 return new TPanel(this, x, y, width, height);
2762 }
2763
5ffeabcc
KL
2764 /**
2765 * Convenience function to add a split pane to this container/window.
2766 *
2767 * @param x column relative to parent
2768 * @param y row relative to parent
2769 * @param width width of text area
2770 * @param height height of text area
2771 * @param vertical if true, split vertically
2772 * @return the new split pane
2773 */
2774 public final TSplitPane addSplitPane(final int x, final int y,
2775 final int width, final int height, final boolean vertical) {
2776
2777 return new TSplitPane(this, x, y, width, height, vertical);
2778 }
2779
48e27807 2780}