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