remove unneeded imports
[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;
938 this.width = width;
939 this.height = height;
d8dc8aea
KL
940 if (layout != null) {
941 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
942 width, height));
943 }
944 }
945
946 /**
947 * Get the layout manager.
948 *
949 * @return the layout manager, or null if not set
950 */
951 public LayoutManager getLayoutManager() {
952 return layout;
953 }
954
955 /**
956 * Set the layout manager.
957 *
958 * @param layout the new layout manager
959 */
960 public void setLayoutManager(LayoutManager layout) {
961 if (this.layout != null) {
962 for (TWidget w: children) {
963 this.layout.remove(w);
964 }
965 this.layout = null;
966 }
967 this.layout = layout;
968 if (this.layout != null) {
969 for (TWidget w: children) {
970 this.layout.add(w);
971 }
972 }
48e27807
KL
973 }
974
975 /**
d36057df 976 * Get enabled flag.
48e27807 977 *
d36057df 978 * @return if true, this widget can be tabbed to or receive events
48e27807 979 */
d36057df
KL
980 public final boolean isEnabled() {
981 return enabled;
982 }
48e27807 983
d36057df
KL
984 /**
985 * Set enabled flag.
986 *
987 * @param enabled if true, this widget can be tabbed to or receive events
988 */
989 public final void setEnabled(final boolean enabled) {
990 this.enabled = enabled;
991 if (!enabled) {
992 active = false;
993 // See if there are any active siblings to switch to
994 boolean foundSibling = false;
995 if (parent != null) {
996 for (TWidget w: parent.children) {
997 if ((w.enabled)
998 && !(this instanceof THScroller)
999 && !(this instanceof TVScroller)
1000 ) {
1001 parent.activate(w);
1002 foundSibling = true;
1003 break;
1004 }
1005 }
1006 if (!foundSibling) {
1007 parent.activeChild = null;
1008 }
48e27807 1009 }
48e27807
KL
1010 }
1011 }
1012
051e2913
KL
1013 /**
1014 * Set visible flag.
1015 *
1016 * @param visible if true, this widget will be drawn
1017 */
1018 public final void setVisible(final boolean visible) {
1019 this.visible = visible;
1020 }
1021
1022 /**
1023 * See if this widget is visible.
1024 *
1025 * @return if true, this widget will be drawn
1026 */
1027 public final boolean isVisible() {
1028 return visible;
1029 }
1030
48e27807 1031 /**
d36057df 1032 * Set visible cursor flag.
48e27807 1033 *
d36057df 1034 * @param cursorVisible if true, this widget has a cursor
48e27807 1035 */
d36057df
KL
1036 public final void setCursorVisible(final boolean cursorVisible) {
1037 this.cursorVisible = cursorVisible;
48e27807
KL
1038 }
1039
1040 /**
d36057df 1041 * See if this widget has a visible cursor.
48e27807 1042 *
d36057df 1043 * @return if true, this widget has a visible cursor
48e27807 1044 */
d36057df
KL
1045 public final boolean isCursorVisible() {
1046 // If cursor is out of my bounds, it is not visible.
1047 if ((cursorX >= width)
1048 || (cursorX < 0)
1049 || (cursorY >= height)
1050 || (cursorY < 0)
1051 ) {
1052 return false;
1053 }
48e27807 1054
5218e73c
KL
1055 assert (window != null);
1056
2bc32111
KL
1057 if (window instanceof TDesktop) {
1058 // Desktop doesn't have a window border.
1059 return cursorVisible;
1060 }
1061
d36057df
KL
1062 // If cursor is out of my window's bounds, it is not visible.
1063 if ((getCursorAbsoluteX() >= window.getAbsoluteX()
1064 + window.getWidth() - 1)
1065 || (getCursorAbsoluteX() < 0)
1066 || (getCursorAbsoluteY() >= window.getAbsoluteY()
1067 + window.getHeight() - 1)
1068 || (getCursorAbsoluteY() < 0)
1069 ) {
1070 return false;
48e27807 1071 }
d36057df
KL
1072 return cursorVisible;
1073 }
48e27807 1074
d36057df
KL
1075 /**
1076 * Get cursor X value.
1077 *
1078 * @return cursor column position in relative coordinates
1079 */
1080 public final int getCursorX() {
1081 return cursorX;
1082 }
48e27807 1083
d36057df
KL
1084 /**
1085 * Set cursor X value.
1086 *
1087 * @param cursorX column position in relative coordinates
1088 */
1089 public final void setCursorX(final int cursorX) {
1090 this.cursorX = cursorX;
1091 }
48e27807 1092
d36057df
KL
1093 /**
1094 * Get cursor Y value.
1095 *
1096 * @return cursor row position in relative coordinates
1097 */
1098 public final int getCursorY() {
1099 return cursorY;
1100 }
48e27807 1101
d36057df
KL
1102 /**
1103 * Set cursor Y value.
1104 *
1105 * @param cursorY row position in relative coordinates
1106 */
1107 public final void setCursorY(final int cursorY) {
1108 this.cursorY = cursorY;
1109 }
48e27807 1110
d36057df
KL
1111 /**
1112 * Get this TWidget's parent TApplication.
1113 *
c7a75ad3 1114 * @return the parent TApplication, or null if not assigned
d36057df
KL
1115 */
1116 public TApplication getApplication() {
c7a75ad3
KL
1117 if (window != null) {
1118 return window.getApplication();
1119 }
1120 return null;
d36057df 1121 }
48e27807 1122
d36057df
KL
1123 /**
1124 * Get the Screen.
1125 *
c7a75ad3 1126 * @return the Screen, or null if not assigned
d36057df
KL
1127 */
1128 public Screen getScreen() {
c7a75ad3
KL
1129 if (window != null) {
1130 return window.getScreen();
1131 }
1132 return null;
48e27807
KL
1133 }
1134
1135 /**
d36057df
KL
1136 * Comparison operator. For various subclasses it sorts on:
1137 * <ul>
1138 * <li>tabOrder for TWidgets</li>
1139 * <li>z for TWindows</li>
1140 * <li>text for TTreeItems</li>
1141 * </ul>
48e27807 1142 *
d36057df
KL
1143 * @param that another TWidget, TWindow, or TTreeItem instance
1144 * @return difference between this.tabOrder and that.tabOrder, or
1145 * difference between this.z and that.z, or String.compareTo(text)
48e27807 1146 */
d36057df
KL
1147 public final int compareTo(final TWidget that) {
1148 if ((this instanceof TWindow)
1149 && (that instanceof TWindow)
48e27807 1150 ) {
d36057df
KL
1151 return (((TWindow) this).getZ() - ((TWindow) that).getZ());
1152 }
1153 if ((this instanceof TTreeItem)
1154 && (that instanceof TTreeItem)
1155 ) {
1156 return (((TTreeItem) this).getText().compareTo(
1157 ((TTreeItem) that).getText()));
48e27807 1158 }
d36057df
KL
1159 return (this.tabOrder - that.tabOrder);
1160 }
48e27807 1161
d36057df
KL
1162 /**
1163 * See if this widget should render with the active color.
1164 *
1165 * @return true if this widget is active and all of its parents are
1166 * active.
1167 */
1168 public final boolean isAbsoluteActive() {
1169 if (parent == this) {
1170 return active;
48e27807 1171 }
5218e73c 1172 return (active && (parent == null ? true : parent.isAbsoluteActive()));
48e27807
KL
1173 }
1174
d36057df
KL
1175 /**
1176 * Returns the cursor X position.
1177 *
1178 * @return absolute screen column number for the cursor's X position
1179 */
1180 public final int getCursorAbsoluteX() {
1181 return getAbsoluteX() + cursorX;
1182 }
2ce6dab2
KL
1183
1184 /**
d36057df 1185 * Returns the cursor Y position.
2ce6dab2 1186 *
d36057df 1187 * @return absolute screen row number for the cursor's Y position
2ce6dab2 1188 */
d36057df
KL
1189 public final int getCursorAbsoluteY() {
1190 return getAbsoluteY() + cursorY;
1191 }
2ce6dab2 1192
d36057df
KL
1193 /**
1194 * Compute my absolute X position as the sum of my X plus all my parent's
1195 * X's.
1196 *
1197 * @return absolute screen column number for my X position
1198 */
1199 public final int getAbsoluteX() {
1200 assert (parent != null);
1201 if (parent == this) {
1202 return x;
2ce6dab2 1203 }
d36057df
KL
1204 if ((parent instanceof TWindow)
1205 && !(parent instanceof TMenu)
1206 && !(parent instanceof TDesktop)
1207 ) {
1208 // Widgets on a TWindow have (0,0) as their top-left, but this is
1209 // actually the TWindow's (1,1).
1210 return parent.getAbsoluteX() + x + 1;
1211 }
1212 return parent.getAbsoluteX() + x;
1213 }
2ce6dab2 1214
d36057df
KL
1215 /**
1216 * Compute my absolute Y position as the sum of my Y plus all my parent's
1217 * Y's.
1218 *
1219 * @return absolute screen row number for my Y position
1220 */
1221 public final int getAbsoluteY() {
1222 assert (parent != null);
1223 if (parent == this) {
1224 return y;
1225 }
1226 if ((parent instanceof TWindow)
1227 && !(parent instanceof TMenu)
1228 && !(parent instanceof TDesktop)
2ce6dab2 1229 ) {
d36057df
KL
1230 // Widgets on a TWindow have (0,0) as their top-left, but this is
1231 // actually the TWindow's (1,1).
1232 return parent.getAbsoluteY() + y + 1;
2ce6dab2 1233 }
d36057df 1234 return parent.getAbsoluteY() + y;
2ce6dab2
KL
1235 }
1236
48e27807 1237 /**
d36057df 1238 * Get the global color theme.
48e27807 1239 *
d36057df 1240 * @return the ColorTheme
48e27807 1241 */
a69ed767 1242 protected final ColorTheme getTheme() {
d36057df
KL
1243 return window.getApplication().getTheme();
1244 }
48e27807 1245
1e71bba2
KL
1246 /**
1247 * See if this widget can be drawn onto a screen.
1248 *
1249 * @return true if this widget is part of the hierarchy that can draw to
1250 * a screen
1251 */
1252 public final boolean isDrawable() {
1253 if ((window == null)
1254 || (window.getScreen() == null)
1255 || (parent == null)
1256 ) {
1257 return false;
1258 }
1259 if (parent == this) {
1260 return true;
1261 }
1262 return (parent.isDrawable());
1263 }
1264
d36057df
KL
1265 /**
1266 * Draw my specific widget. When called, the screen rectangle I draw
1267 * into is already setup (offset and clipping).
1268 */
1269 public void draw() {
1270 // Default widget draws nothing.
1271 }
48e27807 1272
d36057df 1273 /**
a69ed767 1274 * Called by parent to render to TWindow. Note package private access.
d36057df 1275 */
a69ed767 1276 final void drawChildren() {
1e71bba2 1277 if (!isDrawable()) {
2bc32111
KL
1278 return;
1279 }
1280
d36057df
KL
1281 // Set my clipping rectangle
1282 assert (window != null);
1283 assert (getScreen() != null);
1284 Screen screen = getScreen();
1285
1286 // Special case: TStatusBar is drawn by TApplication, not anything
1287 // else.
1288 if (this instanceof TStatusBar) {
1289 return;
48e27807
KL
1290 }
1291
d36057df
KL
1292 screen.setClipRight(width);
1293 screen.setClipBottom(height);
92554d64 1294
d36057df
KL
1295 int absoluteRightEdge = window.getAbsoluteX() + window.getWidth();
1296 int absoluteBottomEdge = window.getAbsoluteY() + window.getHeight();
2bc32111
KL
1297 if (!(this instanceof TWindow)
1298 && !(this instanceof TVScroller)
a524aa2e 1299 && !(window instanceof TDesktop)
2bc32111 1300 ) {
d36057df
KL
1301 absoluteRightEdge -= 1;
1302 }
2bc32111
KL
1303 if (!(this instanceof TWindow)
1304 && !(this instanceof THScroller)
a524aa2e 1305 && !(window instanceof TDesktop)
2bc32111 1306 ) {
d36057df 1307 absoluteBottomEdge -= 1;
48e27807 1308 }
d36057df
KL
1309 int myRightEdge = getAbsoluteX() + width;
1310 int myBottomEdge = getAbsoluteY() + height;
1311 if (getAbsoluteX() > absoluteRightEdge) {
1312 // I am offscreen
1313 screen.setClipRight(0);
1314 } else if (myRightEdge > absoluteRightEdge) {
1315 screen.setClipRight(screen.getClipRight()
1316 - (myRightEdge - absoluteRightEdge));
1317 }
1318 if (getAbsoluteY() > absoluteBottomEdge) {
1319 // I am offscreen
1320 screen.setClipBottom(0);
1321 } else if (myBottomEdge > absoluteBottomEdge) {
1322 screen.setClipBottom(screen.getClipBottom()
1323 - (myBottomEdge - absoluteBottomEdge));
1324 }
1325
1326 // Set my offset
1327 screen.setOffsetX(getAbsoluteX());
1328 screen.setOffsetY(getAbsoluteY());
48e27807 1329
d36057df
KL
1330 // Draw me
1331 draw();
1e71bba2
KL
1332 if (!isDrawable()) {
1333 // An action taken by a draw method unhooked me from the UI.
1334 // Bail out.
1335 return;
1336 }
1337
11cedc9a 1338 assert (visible == true);
d36057df 1339
a69ed767
KL
1340 // Continue down the chain. Draw the active child last so that it
1341 // is on top.
48e27807 1342 for (TWidget widget: children) {
a69ed767 1343 if (widget.isVisible() && (widget != activeChild)) {
051e2913 1344 widget.drawChildren();
1e71bba2
KL
1345 if (!isDrawable()) {
1346 // An action taken by a draw method unhooked me from the UI.
1347 // Bail out.
1348 return;
1349 }
051e2913 1350 }
48e27807 1351 }
a69ed767
KL
1352 if (activeChild != null) {
1353 activeChild.drawChildren();
1354 }
48e27807
KL
1355 }
1356
1357 /**
d36057df 1358 * Repaint the screen on the next update.
48e27807 1359 */
a69ed767 1360 protected final void doRepaint() {
d36057df 1361 window.getApplication().doRepaint();
48e27807
KL
1362 }
1363
1364 /**
d36057df
KL
1365 * Add a child widget to my list of children. We set its tabOrder to 0
1366 * and increment the tabOrder of all other children.
48e27807 1367 *
d36057df 1368 * @param child TWidget to add
48e27807 1369 */
d36057df
KL
1370 private void addChild(final TWidget child) {
1371 children.add(child);
48e27807 1372
d36057df
KL
1373 if ((child.enabled)
1374 && !(child instanceof THScroller)
1375 && !(child instanceof TVScroller)
1376 ) {
1377 for (TWidget widget: children) {
1378 widget.active = false;
48e27807 1379 }
d36057df
KL
1380 child.active = true;
1381 activeChild = child;
48e27807 1382 }
d36057df
KL
1383 for (int i = 0; i < children.size(); i++) {
1384 children.get(i).tabOrder = i;
48e27807 1385 }
d8dc8aea
KL
1386 if (layout != null) {
1387 layout.add(child);
1388 }
48e27807
KL
1389 }
1390
00691e80
KL
1391 /**
1392 * Reset the tab order of children to match their position in the list.
1393 * Available so that subclasses can re-order their widgets if needed.
1394 */
1395 protected void resetTabOrder() {
1396 for (int i = 0; i < children.size(); i++) {
1397 children.get(i).tabOrder = i;
1398 }
1399 }
1400
b6faeac0 1401 /**
d36057df 1402 * Switch the active child.
b6faeac0 1403 *
d36057df 1404 * @param child TWidget to activate
b6faeac0 1405 */
d36057df
KL
1406 public final void activate(final TWidget child) {
1407 assert (child.enabled);
1408 if ((child instanceof THScroller)
1409 || (child instanceof TVScroller)
1410 ) {
1411 return;
b6faeac0 1412 }
b6faeac0 1413
e23ea538
KL
1414 if (children.size() == 1) {
1415 if (children.get(0).enabled == true) {
1416 child.active = true;
1417 activeChild = child;
1418 }
1419 } else {
1420 if (child != activeChild) {
1421 if (activeChild != null) {
1422 activeChild.active = false;
1423 }
1424 child.active = true;
1425 activeChild = child;
12b55d76 1426 }
48e27807
KL
1427 }
1428 }
1429
1430 /**
d36057df 1431 * Switch the active child.
48e27807 1432 *
d36057df
KL
1433 * @param tabOrder tabOrder of the child to activate. If that child
1434 * isn't enabled, then the next enabled child will be activated.
48e27807 1435 */
d36057df 1436 public final void activate(final int tabOrder) {
e23ea538
KL
1437 if (children.size() == 1) {
1438 if (children.get(0).enabled == true) {
1439 children.get(0).active = true;
1440 activeChild = children.get(0);
1441 }
1442 return;
1443 }
1444
d36057df 1445 TWidget child = null;
48e27807 1446 for (TWidget widget: children) {
d36057df
KL
1447 if ((widget.enabled)
1448 && !(widget instanceof THScroller)
1449 && !(widget instanceof TVScroller)
1450 && (widget.tabOrder >= tabOrder)
1451 ) {
1452 child = widget;
1453 break;
1454 }
48e27807 1455 }
d36057df 1456 if ((child != null) && (child != activeChild)) {
00691e80
KL
1457 if (activeChild != null) {
1458 activeChild.active = false;
1459 }
d36057df
KL
1460 assert (child.enabled);
1461 child.active = true;
1462 activeChild = child;
48e27807
KL
1463 }
1464 }
1465
5218e73c
KL
1466 /**
1467 * Make this widget the active child of its parent. Note that this is
1468 * not final since TWindow overrides activate().
1469 */
1470 public void activate() {
1471 if (enabled) {
1472 if (parent != null) {
1473 parent.activate(this);
1474 }
1475 }
1476 }
1477
9917c620
KL
1478 /**
1479 * Make this widget, all of its parents, the active child.
1480 */
1481 public final void activateAll() {
1482 activate();
1483 if (parent == this) {
1484 return;
1485 }
1486 if (parent != null) {
1487 parent.activateAll();
1488 }
1489 }
1490
48e27807 1491 /**
d36057df 1492 * Switch the active widget with the next in the tab order.
48e27807 1493 *
d36057df
KL
1494 * @param forward if true, then switch to the next enabled widget in the
1495 * list, otherwise switch to the previous enabled widget in the list
48e27807 1496 */
d36057df 1497 public final void switchWidget(final boolean forward) {
48e27807 1498
e23ea538 1499 // No children: do nothing.
00691e80 1500 if (children.size() == 0) {
e23ea538
KL
1501 return;
1502 }
1503
5218e73c
KL
1504 assert (parent != null);
1505
e23ea538
KL
1506 // If there is only one child, make it active if it is enabled.
1507 if (children.size() == 1) {
1508 if (children.get(0).enabled == true) {
1509 activeChild = children.get(0);
1510 activeChild.active = true;
1511 } else {
1512 children.get(0).active = false;
1513 activeChild = null;
1514 }
48e27807
KL
1515 return;
1516 }
1517
e23ea538
KL
1518 // Two or more children: go forward or backward to the next enabled
1519 // child.
00691e80
KL
1520 int tabOrder = 0;
1521 if (activeChild != null) {
1522 tabOrder = activeChild.tabOrder;
1523 }
d36057df
KL
1524 do {
1525 if (forward) {
1526 tabOrder++;
1527 } else {
1528 tabOrder--;
1529 }
1530 if (tabOrder < 0) {
48e27807 1531
d36057df
KL
1532 // If at the end, pass the switch to my parent.
1533 if ((!forward) && (parent != this)) {
1534 parent.switchWidget(forward);
1535 return;
1536 }
48e27807 1537
d36057df
KL
1538 tabOrder = children.size() - 1;
1539 } else if (tabOrder == children.size()) {
1540 // If at the end, pass the switch to my parent.
1541 if ((forward) && (parent != this)) {
1542 parent.switchWidget(forward);
1543 return;
1544 }
48e27807 1545
d36057df
KL
1546 tabOrder = 0;
1547 }
00691e80
KL
1548 if (activeChild == null) {
1549 if (tabOrder == 0) {
1550 // We wrapped around
1551 break;
1552 }
1553 } else if (activeChild.tabOrder == tabOrder) {
d36057df 1554 // We wrapped around
48e27807 1555 break;
d36057df
KL
1556 }
1557 } while ((!children.get(tabOrder).enabled)
1558 && !(children.get(tabOrder) instanceof THScroller)
1559 && !(children.get(tabOrder) instanceof TVScroller));
48e27807 1560
00691e80
KL
1561 if (activeChild != null) {
1562 assert (children.get(tabOrder).enabled);
48e27807 1563
00691e80
KL
1564 activeChild.active = false;
1565 }
1566 if (children.get(tabOrder).enabled == true) {
1567 children.get(tabOrder).active = true;
1568 activeChild = children.get(tabOrder);
1569 }
d36057df 1570 }
48e27807 1571
d36057df
KL
1572 /**
1573 * Returns my active widget.
1574 *
1575 * @return widget that is active, or this if no children
1576 */
1577 public TWidget getActiveChild() {
1578 if ((this instanceof THScroller)
1579 || (this instanceof TVScroller)
1580 ) {
1581 return parent;
1582 }
b6faeac0 1583
d36057df
KL
1584 for (TWidget widget: children) {
1585 if (widget.active) {
1586 return widget.getActiveChild();
48e27807 1587 }
48e27807 1588 }
d36057df
KL
1589 // No active children, return me
1590 return this;
48e27807
KL
1591 }
1592
469c2b3c 1593 /**
a524aa2e
KL
1594 * Insert a vertical split between this widget and parent, and optionally
1595 * put another widget in the other side of the split.
469c2b3c
KL
1596 *
1597 * @param newWidgetOnLeft if true, the new widget (if specified) will be
a524aa2e 1598 * on the left pane, and this widget will be placed on the right pane
469c2b3c
KL
1599 * @param newWidget the new widget to add to the other pane, or null
1600 * @return the new split pane widget
1601 */
1602 public TSplitPane splitVertical(final boolean newWidgetOnLeft,
1603 final TWidget newWidget) {
1604
469c2b3c 1605 TSplitPane splitPane = new TSplitPane(null, x, y, width, height, true);
a524aa2e
KL
1606 TWidget myParent = parent;
1607 remove(false);
90d87fca
KL
1608 if (myParent instanceof TSplitPane) {
1609 // TSplitPane has a left/right/top/bottom link to me somewhere,
1610 // replace it with a link to splitPane.
1611 ((TSplitPane) myParent).replaceWidget(this, splitPane);
1612 }
a524aa2e 1613 splitPane.setParent(myParent, false);
469c2b3c
KL
1614 if (newWidgetOnLeft) {
1615 splitPane.setLeft(newWidget);
a524aa2e 1616 splitPane.setRight(this);
469c2b3c 1617 } else {
a524aa2e 1618 splitPane.setLeft(this);
90d87fca 1619 splitPane.setRight(newWidget);
469c2b3c 1620 }
a524aa2e 1621 if (newWidget != null) {
9917c620 1622 newWidget.activateAll();
a524aa2e 1623 } else {
9917c620 1624 activateAll();
a524aa2e 1625 }
90d87fca 1626
a524aa2e
KL
1627 assert (parent != null);
1628 assert (window != null);
469c2b3c
KL
1629 assert (splitPane.getWindow() != null);
1630 assert (splitPane.getParent() != null);
469c2b3c 1631 assert (splitPane.isActive() == true);
90d87fca
KL
1632 assert (parent == splitPane);
1633 if (newWidget != null) {
1634 assert (newWidget.parent == parent);
1635 assert (newWidget.active == true);
1636 assert (active == false);
1637 } else {
1638 assert (active == true);
1639 }
469c2b3c
KL
1640 return splitPane;
1641 }
1642
1643 /**
a524aa2e
KL
1644 * Insert a horizontal split between this widget and parent, and
1645 * optionally put another widget in the other side of the split.
469c2b3c
KL
1646 *
1647 * @param newWidgetOnTop if true, the new widget (if specified) will be
1648 * on the top pane, and this widget's children will be placed on the
1649 * bottom pane
1650 * @param newWidget the new widget to add to the other pane, or null
1651 * @return the new split pane widget
1652 */
1653 public TSplitPane splitHorizontal(final boolean newWidgetOnTop,
1654 final TWidget newWidget) {
1655
469c2b3c 1656 TSplitPane splitPane = new TSplitPane(null, x, y, width, height, false);
a524aa2e
KL
1657 TWidget myParent = parent;
1658 remove(false);
90d87fca
KL
1659 if (myParent instanceof TSplitPane) {
1660 // TSplitPane has a left/right/top/bottom link to me somewhere,
1661 // replace it with a link to splitPane.
1662 ((TSplitPane) myParent).replaceWidget(this, splitPane);
1663 }
a524aa2e 1664 splitPane.setParent(myParent, false);
469c2b3c
KL
1665 if (newWidgetOnTop) {
1666 splitPane.setTop(newWidget);
a524aa2e 1667 splitPane.setBottom(this);
469c2b3c 1668 } else {
a524aa2e 1669 splitPane.setTop(this);
90d87fca 1670 splitPane.setBottom(newWidget);
a524aa2e 1671 }
a524aa2e 1672 if (newWidget != null) {
9917c620 1673 newWidget.activateAll();
a524aa2e 1674 } else {
9917c620 1675 activateAll();
469c2b3c 1676 }
90d87fca 1677
a524aa2e
KL
1678 assert (parent != null);
1679 assert (window != null);
469c2b3c
KL
1680 assert (splitPane.getWindow() != null);
1681 assert (splitPane.getParent() != null);
469c2b3c 1682 assert (splitPane.isActive() == true);
90d87fca
KL
1683 assert (parent == splitPane);
1684 if (newWidget != null) {
1685 assert (newWidget.parent == parent);
1686 assert (newWidget.active == true);
1687 assert (active == false);
1688 } else {
1689 assert (active == true);
1690 }
469c2b3c
KL
1691 return splitPane;
1692 }
1693
90d87fca
KL
1694 /**
1695 * Generate a human-readable string for this widget.
1696 *
1697 * @return a human-readable string
1698 */
1699 @Override
1700 public String toString() {
1701 return String.format("%s(%8x) position (%d, %d) geometry %dx%d " +
1702 "active %s enabled %s visible %s", getClass().getName(),
1703 hashCode(), x, y, width, height, active, enabled, visible);
1704 }
1705
1706 /**
1707 * Generate a string for this widget's hierarchy.
1708 *
1709 * @param prefix a prefix to use for this widget's place in the hierarchy
1710 * @return a pretty-printable string of this hierarchy
1711 */
1712 protected String toPrettyString(final String prefix) {
1713 StringBuilder sb = new StringBuilder(prefix);
1714 sb.append(toString());
1715 String newPrefix = "";
1716 for (int i = 0; i < prefix.length(); i++) {
1717 newPrefix += " ";
1718 }
1719 for (int i = 0; i < children.size(); i++) {
1720 TWidget child= children.get(i);
1721 sb.append("\n");
1722 if (i == children.size() - 1) {
1723 sb.append(child.toPrettyString(newPrefix + " \u2514\u2500"));
1724 } else {
1725 sb.append(child.toPrettyString(newPrefix + " \u251c\u2500"));
1726 }
1727 }
1728 return sb.toString();
1729 }
1730
1731 /**
1732 * Generate a string for this widget's hierarchy.
1733 *
1734 * @return a pretty-printable string of this hierarchy
1735 */
1736 public String toPrettyString() {
1737 return toPrettyString("");
1738 }
1739
a69ed767
KL
1740 // ------------------------------------------------------------------------
1741 // Passthru for Screen functions ------------------------------------------
1742 // ------------------------------------------------------------------------
1743
1744 /**
1745 * Get the attributes at one location.
1746 *
1747 * @param x column coordinate. 0 is the left-most column.
1748 * @param y row coordinate. 0 is the top-most row.
1749 * @return attributes at (x, y)
1750 */
1751 protected final CellAttributes getAttrXY(final int x, final int y) {
1752 return getScreen().getAttrXY(x, y);
1753 }
1754
1755 /**
1756 * Set the attributes at one location.
1757 *
1758 * @param x column coordinate. 0 is the left-most column.
1759 * @param y row coordinate. 0 is the top-most row.
1760 * @param attr attributes to use (bold, foreColor, backColor)
1761 */
1762 protected final void putAttrXY(final int x, final int y,
1763 final CellAttributes attr) {
1764
1765 getScreen().putAttrXY(x, y, attr);
1766 }
1767
1768 /**
1769 * Set the attributes at one location.
1770 *
1771 * @param x column coordinate. 0 is the left-most column.
1772 * @param y row coordinate. 0 is the top-most row.
1773 * @param attr attributes to use (bold, foreColor, backColor)
1774 * @param clip if true, honor clipping/offset
1775 */
1776 protected final void putAttrXY(final int x, final int y,
1777 final CellAttributes attr, final boolean clip) {
1778
1779 getScreen().putAttrXY(x, y, attr, clip);
1780 }
1781
1782 /**
1783 * Fill the entire screen with one character with attributes.
1784 *
1785 * @param ch character to draw
1786 * @param attr attributes to use (bold, foreColor, backColor)
1787 */
218d18db 1788 protected final void putAll(final int ch, final CellAttributes attr) {
a69ed767
KL
1789 getScreen().putAll(ch, attr);
1790 }
1791
1792 /**
1793 * Render one character with attributes.
1794 *
1795 * @param x column coordinate. 0 is the left-most column.
1796 * @param y row coordinate. 0 is the top-most row.
1797 * @param ch character + attributes to draw
1798 */
1799 protected final void putCharXY(final int x, final int y, final Cell ch) {
1800 getScreen().putCharXY(x, y, ch);
1801 }
1802
1803 /**
1804 * Render one character with attributes.
1805 *
1806 * @param x column coordinate. 0 is the left-most column.
1807 * @param y row coordinate. 0 is the top-most row.
1808 * @param ch character to draw
1809 * @param attr attributes to use (bold, foreColor, backColor)
1810 */
218d18db 1811 protected final void putCharXY(final int x, final int y, final int ch,
a69ed767
KL
1812 final CellAttributes attr) {
1813
1814 getScreen().putCharXY(x, y, ch, attr);
1815 }
1816
1817 /**
1818 * Render one character without changing the underlying attributes.
1819 *
1820 * @param x column coordinate. 0 is the left-most column.
1821 * @param y row coordinate. 0 is the top-most row.
1822 * @param ch character to draw
1823 */
218d18db 1824 protected final void putCharXY(final int x, final int y, final int ch) {
a69ed767
KL
1825 getScreen().putCharXY(x, y, ch);
1826 }
1827
1828 /**
1829 * Render a string. Does not wrap if the string exceeds the line.
1830 *
1831 * @param x column coordinate. 0 is the left-most column.
1832 * @param y row coordinate. 0 is the top-most row.
1833 * @param str string to draw
1834 * @param attr attributes to use (bold, foreColor, backColor)
1835 */
1836 protected final void putStringXY(final int x, final int y, final String str,
1837 final CellAttributes attr) {
1838
1839 getScreen().putStringXY(x, y, str, attr);
1840 }
1841
1842 /**
1843 * Render a string without changing the underlying attribute. Does not
1844 * wrap if the string exceeds the line.
1845 *
1846 * @param x column coordinate. 0 is the left-most column.
1847 * @param y row coordinate. 0 is the top-most row.
1848 * @param str string to draw
1849 */
1850 protected final void putStringXY(final int x, final int y, final String str) {
1851 getScreen().putStringXY(x, y, str);
1852 }
1853
1854 /**
1855 * Draw a vertical line from (x, y) to (x, y + n).
1856 *
1857 * @param x column coordinate. 0 is the left-most column.
1858 * @param y row coordinate. 0 is the top-most row.
1859 * @param n number of characters to draw
1860 * @param ch character to draw
1861 * @param attr attributes to use (bold, foreColor, backColor)
1862 */
1863 protected final void vLineXY(final int x, final int y, final int n,
218d18db 1864 final int ch, final CellAttributes attr) {
a69ed767
KL
1865
1866 getScreen().vLineXY(x, y, n, ch, attr);
1867 }
1868
1869 /**
1870 * Draw a horizontal line from (x, y) to (x + n, y).
1871 *
1872 * @param x column coordinate. 0 is the left-most column.
1873 * @param y row coordinate. 0 is the top-most row.
1874 * @param n number of characters to draw
1875 * @param ch character to draw
1876 * @param attr attributes to use (bold, foreColor, backColor)
1877 */
1878 protected final void hLineXY(final int x, final int y, final int n,
218d18db 1879 final int ch, final CellAttributes attr) {
a69ed767
KL
1880
1881 getScreen().hLineXY(x, y, n, ch, attr);
1882 }
1883
1884 /**
1885 * Draw a box with a border and empty background.
1886 *
1887 * @param left left column of box. 0 is the left-most row.
1888 * @param top top row of the box. 0 is the top-most row.
1889 * @param right right column of box
1890 * @param bottom bottom row of the box
1891 * @param border attributes to use for the border
1892 * @param background attributes to use for the background
1893 */
1894 protected final void drawBox(final int left, final int top,
1895 final int right, final int bottom,
1896 final CellAttributes border, final CellAttributes background) {
1897
1898 getScreen().drawBox(left, top, right, bottom, border, background);
1899 }
1900
1901 /**
1902 * Draw a box with a border and empty background.
1903 *
1904 * @param left left column of box. 0 is the left-most row.
1905 * @param top top row of the box. 0 is the top-most row.
1906 * @param right right column of box
1907 * @param bottom bottom row of the box
1908 * @param border attributes to use for the border
1909 * @param background attributes to use for the background
1910 * @param borderType if 1, draw a single-line border; if 2, draw a
1911 * double-line border; if 3, draw double-line top/bottom edges and
1912 * single-line left/right edges (like Qmodem)
1913 * @param shadow if true, draw a "shadow" on the box
1914 */
1915 protected final void drawBox(final int left, final int top,
1916 final int right, final int bottom,
1917 final CellAttributes border, final CellAttributes background,
1918 final int borderType, final boolean shadow) {
1919
1920 getScreen().drawBox(left, top, right, bottom, border, background,
1921 borderType, shadow);
1922 }
1923
1924 /**
1925 * Draw a box shadow.
1926 *
1927 * @param left left column of box. 0 is the left-most row.
1928 * @param top top row of the box. 0 is the top-most row.
1929 * @param right right column of box
1930 * @param bottom bottom row of the box
1931 */
1932 protected final void drawBoxShadow(final int left, final int top,
1933 final int right, final int bottom) {
1934
1935 getScreen().drawBoxShadow(left, top, right, bottom);
1936 }
1937
2ce6dab2
KL
1938 // ------------------------------------------------------------------------
1939 // Other TWidget constructors ---------------------------------------------
1940 // ------------------------------------------------------------------------
48e27807 1941
30d336cc
KL
1942 /**
1943 * Convenience function to add a label to this container/window.
1944 *
1945 * @param text label
1946 * @param x column relative to parent
1947 * @param y row relative to parent
1948 * @return the new label
1949 */
1950 public final TLabel addLabel(final String text, final int x, final int y) {
1951 return addLabel(text, x, y, "tlabel");
1952 }
1953
00691e80
KL
1954 /**
1955 * Convenience function to add a label to this container/window.
1956 *
1957 * @param text label
1958 * @param x column relative to parent
1959 * @param y row relative to parent
1960 * @param action to call when shortcut is pressed
1961 * @return the new label
1962 */
1963 public final TLabel addLabel(final String text, final int x, final int y,
1964 final TAction action) {
1965
1966 return addLabel(text, x, y, "tlabel", action);
1967 }
1968
30d336cc
KL
1969 /**
1970 * Convenience function to add a label to this container/window.
1971 *
1972 * @param text label
1973 * @param x column relative to parent
1974 * @param y row relative to parent
1975 * @param colorKey ColorTheme key color to use for foreground text.
1976 * Default is "tlabel"
1977 * @return the new label
1978 */
1979 public final TLabel addLabel(final String text, final int x, final int y,
1980 final String colorKey) {
1981
1982 return new TLabel(this, text, x, y, colorKey);
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 colorKey ColorTheme key color to use for foreground text.
1992 * Default is "tlabel"
1993 * @param action to call when shortcut is pressed
1994 * @return the new label
1995 */
1996 public final TLabel addLabel(final String text, final int x, final int y,
1997 final String colorKey, final TAction action) {
1998
1999 return new TLabel(this, text, x, y, colorKey, action);
2000 }
2001
051e2913
KL
2002 /**
2003 * Convenience function to add a label to this container/window.
2004 *
2005 * @param text label
2006 * @param x column relative to parent
2007 * @param y row relative to parent
2008 * @param colorKey ColorTheme key color to use for foreground text.
2009 * Default is "tlabel"
2010 * @param useWindowBackground if true, use the window's background color
2011 * @return the new label
2012 */
2013 public final TLabel addLabel(final String text, final int x, final int y,
2014 final String colorKey, final boolean useWindowBackground) {
2015
2016 return new TLabel(this, text, x, y, colorKey, useWindowBackground);
2017 }
2018
00691e80
KL
2019 /**
2020 * Convenience function to add a label to this container/window.
2021 *
2022 * @param text label
2023 * @param x column relative to parent
2024 * @param y row relative to parent
2025 * @param colorKey ColorTheme key color to use for foreground text.
2026 * Default is "tlabel"
2027 * @param useWindowBackground if true, use the window's background color
2028 * @param action to call when shortcut is pressed
2029 * @return the new label
2030 */
2031 public final TLabel addLabel(final String text, final int x, final int y,
2032 final String colorKey, final boolean useWindowBackground,
2033 final TAction action) {
2034
2035 return new TLabel(this, text, x, y, colorKey, useWindowBackground,
2036 action);
2037 }
2038
30d336cc
KL
2039 /**
2040 * Convenience function to add a button to this container/window.
2041 *
2042 * @param text label on the button
2043 * @param x column relative to parent
2044 * @param y row relative to parent
051e2913 2045 * @param action action to call when button is pressed
30d336cc
KL
2046 * @return the new button
2047 */
2048 public final TButton addButton(final String text, final int x, final int y,
2049 final TAction action) {
2050
2051 return new TButton(this, text, x, y, action);
2052 }
2053
7272e49f
KL
2054 /**
2055 * Convenience function to add a checkbox to this container/window.
2056 *
2057 * @param x column relative to parent
2058 * @param y row relative to parent
2059 * @param label label to display next to (right of) the checkbox
2060 * @param checked initial check state
2061 * @return the new checkbox
2062 */
051e2913 2063 public final TCheckBox addCheckBox(final int x, final int y,
7272e49f
KL
2064 final String label, final boolean checked) {
2065
051e2913
KL
2066 return new TCheckBox(this, x, y, label, checked);
2067 }
2068
2069 /**
2070 * Convenience function to add a combobox to this container/window.
2071 *
2072 * @param x column relative to parent
2073 * @param y row relative to parent
2074 * @param width visible combobox width, including the down-arrow
2075 * @param values the possible values for the box, shown in the drop-down
2076 * @param valuesIndex the initial index in values, or -1 for no default
2077 * value
8ab60a33
KL
2078 * @param maxValuesHeight the maximum height of the values drop-down when
2079 * it is visible
051e2913
KL
2080 * @param updateAction action to call when a new value is selected from
2081 * the list or enter is pressed in the edit field
2082 * @return the new combobox
2083 */
2084 public final TComboBox addComboBox(final int x, final int y,
2085 final int width, final List<String> values, final int valuesIndex,
8ab60a33 2086 final int maxValuesHeight, final TAction updateAction) {
051e2913
KL
2087
2088 return new TComboBox(this, x, y, width, values, valuesIndex,
8ab60a33 2089 maxValuesHeight, updateAction);
051e2913
KL
2090 }
2091
2092 /**
2093 * Convenience function to add a spinner to this container/window.
2094 *
2095 * @param x column relative to parent
2096 * @param y row relative to parent
2097 * @param upAction action to call when the up arrow is clicked or pressed
2098 * @param downAction action to call when the down arrow is clicked or
2099 * pressed
2100 * @return the new spinner
2101 */
2102 public final TSpinner addSpinner(final int x, final int y,
2103 final TAction upAction, final TAction downAction) {
2104
2105 return new TSpinner(this, x, y, upAction, downAction);
2106 }
2107
2108 /**
2109 * Convenience function to add a calendar to this container/window.
2110 *
2111 * @param x column relative to parent
2112 * @param y row relative to parent
2113 * @param updateAction action to call when the user changes the value of
2114 * the calendar
2115 * @return the new calendar
2116 */
2117 public final TCalendar addCalendar(final int x, final int y,
2118 final TAction updateAction) {
2119
2120 return new TCalendar(this, x, y, updateAction);
7272e49f 2121 }
30d336cc 2122
d502a0e9
KL
2123 /**
2124 * Convenience function to add a progress bar to this container/window.
2125 *
2126 * @param x column relative to parent
2127 * @param y row relative to parent
2128 * @param width width of progress bar
2129 * @param value initial value of percent complete
00d2622b 2130 * @return the new progress bar
d502a0e9
KL
2131 */
2132 public final TProgressBar addProgressBar(final int x, final int y,
2133 final int width, final int value) {
2134
2135 return new TProgressBar(this, x, y, width, value);
2136 }
2137
00d2622b
KL
2138 /**
2139 * Convenience function to add a radio button group to this
2140 * container/window.
2141 *
2142 * @param x column relative to parent
2143 * @param y row relative to parent
2144 * @param label label to display on the group box
2145 * @return the new radio button group
2146 */
2147 public final TRadioGroup addRadioGroup(final int x, final int y,
2148 final String label) {
2149
2150 return new TRadioGroup(this, x, y, label);
2151 }
2152
128e5be1
KL
2153 /**
2154 * Convenience function to add a text field to this container/window.
2155 *
2156 * @param x column relative to parent
2157 * @param y row relative to parent
2158 * @param width visible text width
2159 * @param fixed if true, the text cannot exceed the display width
2160 * @return the new text field
2161 */
2162 public final TField addField(final int x, final int y,
2163 final int width, final boolean fixed) {
2164
2165 return new TField(this, x, y, width, fixed);
2166 }
2167
2168 /**
2169 * Convenience function to add a text field to this container/window.
2170 *
2171 * @param x column relative to parent
2172 * @param y row relative to parent
2173 * @param width visible text width
2174 * @param fixed if true, the text cannot exceed the display width
2175 * @param text initial text, default is empty string
2176 * @return the new text field
2177 */
2178 public final TField addField(final int x, final int y,
2179 final int width, final boolean fixed, final String text) {
2180
2181 return new TField(this, x, y, width, fixed, text);
2182 }
2183
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 * @param text initial text, default is empty string
2192 * @param enterAction function to call when enter key is pressed
2193 * @param updateAction function to call when the text is updated
2194 * @return the new text field
2195 */
2196 public final TField addField(final int x, final int y,
2197 final int width, final boolean fixed, final String text,
2198 final TAction enterAction, final TAction updateAction) {
2199
2200 return new TField(this, x, y, width, fixed, text, enterAction,
2201 updateAction);
2202 }
00d2622b 2203
cc99cba8
KL
2204 /**
2205 * Convenience function to add a scrollable text box to this
2206 * container/window.
2207 *
2208 * @param text text on the screen
2209 * @param x column relative to parent
2210 * @param y row relative to parent
2211 * @param width width of text area
2212 * @param height height of text area
2213 * @param colorKey ColorTheme key color to use for foreground text
2214 * @return the new text box
2215 */
c6940ed9 2216 public final TText addText(final String text, final int x,
cc99cba8
KL
2217 final int y, final int width, final int height, final String colorKey) {
2218
2219 return new TText(this, text, x, y, width, height, colorKey);
2220 }
2221
2222 /**
2223 * Convenience function to add a scrollable text box to this
2224 * container/window.
2225 *
2226 * @param text text on the screen
2227 * @param x column relative to parent
2228 * @param y row relative to parent
2229 * @param width width of text area
2230 * @param height height of text area
2231 * @return the new text box
2232 */
c6940ed9 2233 public final TText addText(final String text, final int x, final int y,
cc99cba8
KL
2234 final int width, final int height) {
2235
2236 return new TText(this, text, x, y, width, height, "ttext");
2237 }
2238
12b55d76
KL
2239 /**
2240 * Convenience function to add an editable text area box to this
2241 * container/window.
2242 *
2243 * @param text text on the screen
2244 * @param x column relative to parent
2245 * @param y row relative to parent
2246 * @param width width of text area
2247 * @param height height of text area
2248 * @return the new text box
2249 */
2250 public final TEditorWidget addEditor(final String text, final int x,
2251 final int y, final int width, final int height) {
2252
2253 return new TEditorWidget(this, text, x, y, width, height);
2254 }
2255
c6940ed9
KL
2256 /**
2257 * Convenience function to spawn a message box.
2258 *
2259 * @param title window title, will be centered along the top border
2260 * @param caption message to display. Use embedded newlines to get a
2261 * multi-line box.
2262 * @return the new message box
2263 */
2264 public final TMessageBox messageBox(final String title,
2265 final String caption) {
2266
2267 return getApplication().messageBox(title, caption, TMessageBox.Type.OK);
2268 }
2269
2270 /**
2271 * Convenience function to spawn a message box.
2272 *
2273 * @param title window title, will be centered along the top border
2274 * @param caption message to display. Use embedded newlines to get a
2275 * multi-line box.
2276 * @param type one of the TMessageBox.Type constants. Default is
2277 * Type.OK.
2278 * @return the new message box
2279 */
2280 public final TMessageBox messageBox(final String title,
2281 final String caption, final TMessageBox.Type type) {
2282
2283 return getApplication().messageBox(title, caption, type);
2284 }
2285
2286 /**
2287 * Convenience function to spawn an input box.
2288 *
2289 * @param title window title, will be centered along the top border
2290 * @param caption message to display. Use embedded newlines to get a
2291 * multi-line box.
2292 * @return the new input box
2293 */
2294 public final TInputBox inputBox(final String title, final String caption) {
2295
2296 return getApplication().inputBox(title, caption);
2297 }
2298
2299 /**
2300 * Convenience function to spawn an input box.
2301 *
2302 * @param title window title, will be centered along the top border
2303 * @param caption message to display. Use embedded newlines to get a
2304 * multi-line box.
2305 * @param text initial text to seed the field with
2306 * @return the new input box
2307 */
2308 public final TInputBox inputBox(final String title, final String caption,
2309 final String text) {
2310
2311 return getApplication().inputBox(title, caption, text);
2312 }
cc99cba8 2313
2b427404
KL
2314 /**
2315 * Convenience function to spawn an input box.
2316 *
2317 * @param title window title, will be centered along the top border
2318 * @param caption message to display. Use embedded newlines to get a
2319 * multi-line box.
2320 * @param text initial text to seed the field with
2321 * @param type one of the Type constants. Default is Type.OK.
2322 * @return the new input box
2323 */
2324 public final TInputBox inputBox(final String title, final String caption,
2325 final String text, final TInputBox.Type type) {
2326
2327 return getApplication().inputBox(title, caption, text, type);
2328 }
2329
87a17f3c
KL
2330 /**
2331 * Convenience function to add a password text field to this
2332 * container/window.
2333 *
2334 * @param x column relative to parent
2335 * @param y row relative to parent
2336 * @param width visible text width
2337 * @param fixed if true, the text cannot exceed the display width
2338 * @return the new text field
2339 */
2340 public final TPasswordField addPasswordField(final int x, final int y,
2341 final int width, final boolean fixed) {
2342
2343 return new TPasswordField(this, x, y, width, fixed);
2344 }
2345
2346 /**
2347 * Convenience function to add a password text field to this
2348 * container/window.
2349 *
2350 * @param x column relative to parent
2351 * @param y row relative to parent
2352 * @param width visible text width
2353 * @param fixed if true, the text cannot exceed the display width
2354 * @param text initial text, default is empty string
2355 * @return the new text field
2356 */
2357 public final TPasswordField addPasswordField(final int x, final int y,
2358 final int width, final boolean fixed, final String text) {
2359
2360 return new TPasswordField(this, x, y, width, fixed, text);
2361 }
2362
2363 /**
2364 * Convenience function to add a password text field to this
2365 * container/window.
2366 *
2367 * @param x column relative to parent
2368 * @param y row relative to parent
2369 * @param width visible text width
2370 * @param fixed if true, the text cannot exceed the display width
2371 * @param text initial text, default is empty string
2372 * @param enterAction function to call when enter key is pressed
2373 * @param updateAction function to call when the text is updated
2374 * @return the new text field
2375 */
2376 public final TPasswordField addPasswordField(final int x, final int y,
2377 final int width, final boolean fixed, final String text,
2378 final TAction enterAction, final TAction updateAction) {
2379
2380 return new TPasswordField(this, x, y, width, fixed, text, enterAction,
2381 updateAction);
2382 }
2383
7668cb45 2384 /**
d36057df
KL
2385 * Convenience function to add a scrollable tree view to this
2386 * container/window.
7668cb45
KL
2387 *
2388 * @param x column relative to parent
2389 * @param y row relative to parent
2390 * @param width width of tree view
2391 * @param height height of tree view
329fd62e 2392 * @return the new tree view
7668cb45 2393 */
d36057df 2394 public final TTreeViewWidget addTreeViewWidget(final int x, final int y,
7668cb45
KL
2395 final int width, final int height) {
2396
d36057df 2397 return new TTreeViewWidget(this, x, y, width, height);
7668cb45
KL
2398 }
2399
2400 /**
d36057df
KL
2401 * Convenience function to add a scrollable tree view to this
2402 * container/window.
7668cb45
KL
2403 *
2404 * @param x column relative to parent
2405 * @param y row relative to parent
2406 * @param width width of tree view
2407 * @param height height of tree view
2408 * @param action action to perform when an item is selected
329fd62e 2409 * @return the new tree view
7668cb45 2410 */
d36057df 2411 public final TTreeViewWidget addTreeViewWidget(final int x, final int y,
7668cb45
KL
2412 final int width, final int height, final TAction action) {
2413
d36057df 2414 return new TTreeViewWidget(this, x, y, width, height, action);
7668cb45
KL
2415 }
2416
0d47c546
KL
2417 /**
2418 * Convenience function to spawn a file open box.
2419 *
2420 * @param path path of selected file
2421 * @return the result of the new file open box
329fd62e 2422 * @throws IOException if a java.io operation throws
0d47c546
KL
2423 */
2424 public final String fileOpenBox(final String path) throws IOException {
2425 return getApplication().fileOpenBox(path);
2426 }
2427
a69ed767
KL
2428 /**
2429 * Convenience function to spawn a file save box.
2430 *
2431 * @param path path of selected file
2432 * @return the result of the new file open box
2433 * @throws IOException if a java.io operation throws
2434 */
2435 public final String fileSaveBox(final String path) throws IOException {
2436 return getApplication().fileOpenBox(path, TFileOpenBox.Type.SAVE);
2437 }
2438
0d47c546
KL
2439 /**
2440 * Convenience function to spawn a file open box.
2441 *
2442 * @param path path of selected file
2443 * @param type one of the Type constants
2444 * @return the result of the new file open box
329fd62e 2445 * @throws IOException if a java.io operation throws
0d47c546
KL
2446 */
2447 public final String fileOpenBox(final String path,
2448 final TFileOpenBox.Type type) throws IOException {
2449
2450 return getApplication().fileOpenBox(path, type);
2451 }
a69ed767
KL
2452
2453 /**
2454 * Convenience function to spawn a file open box.
2455 *
2456 * @param path path of selected file
2457 * @param type one of the Type constants
2458 * @param filter a string that files must match to be displayed
2459 * @return the result of the new file open box
2460 * @throws IOException of a java.io operation throws
2461 */
2462 public final String fileOpenBox(final String path,
2463 final TFileOpenBox.Type type, final String filter) throws IOException {
2464
2465 ArrayList<String> filters = new ArrayList<String>();
2466 filters.add(filter);
2467
2468 return getApplication().fileOpenBox(path, type, filters);
2469 }
2470
2471 /**
2472 * Convenience function to spawn a file open box.
2473 *
2474 * @param path path of selected file
2475 * @param type one of the Type constants
2476 * @param filters a list of strings that files must match to be displayed
2477 * @return the result of the new file open box
2478 * @throws IOException of a java.io operation throws
2479 */
2480 public final String fileOpenBox(final String path,
2481 final TFileOpenBox.Type type,
2482 final List<String> filters) throws IOException {
2483
2484 return getApplication().fileOpenBox(path, type, filters);
2485 }
2486
0d47c546
KL
2487 /**
2488 * Convenience function to add a directory list to this container/window.
2489 *
2490 * @param path directory path, must be a directory
2491 * @param x column relative to parent
2492 * @param y row relative to parent
2493 * @param width width of text area
2494 * @param height height of text area
329fd62e 2495 * @return the new directory list
0d47c546
KL
2496 */
2497 public final TDirectoryList addDirectoryList(final String path, final int x,
2498 final int y, final int width, final int height) {
2499
2500 return new TDirectoryList(this, path, x, y, width, height, null);
2501 }
2502
2503 /**
2504 * Convenience function to add a directory list to this container/window.
2505 *
2506 * @param path directory path, must be a directory
2507 * @param x column relative to parent
2508 * @param y row relative to parent
2509 * @param width width of text area
2510 * @param height height of text area
a69ed767
KL
2511 * @param action action to perform when an item is selected (enter or
2512 * double-click)
329fd62e 2513 * @return the new directory list
0d47c546
KL
2514 */
2515 public final TDirectoryList addDirectoryList(final String path, final int x,
2516 final int y, final int width, final int height, final TAction action) {
2517
2518 return new TDirectoryList(this, path, x, y, width, height, action);
2519 }
7668cb45 2520
3649b921
KL
2521 /**
2522 * Convenience function to add a directory list to this container/window.
2523 *
a69ed767
KL
2524 * @param path directory path, must be a directory
2525 * @param x column relative to parent
2526 * @param y row relative to parent
2527 * @param width width of text area
2528 * @param height height of text area
2529 * @param action action to perform when an item is selected (enter or
2530 * double-click)
2531 * @param singleClickAction action to perform when an item is selected
2532 * (single-click)
2533 * @return the new directory list
2534 */
2535 public final TDirectoryList addDirectoryList(final String path, final int x,
2536 final int y, final int width, final int height, final TAction action,
2537 final TAction singleClickAction) {
2538
2539 return new TDirectoryList(this, path, x, y, width, height, action,
2540 singleClickAction);
2541 }
2542
2543 /**
2544 * Convenience function to add a directory list to this container/window.
2545 *
2546 * @param path directory path, must be a directory
2547 * @param x column relative to parent
2548 * @param y row relative to parent
2549 * @param width width of text area
2550 * @param height height of text area
2551 * @param action action to perform when an item is selected (enter or
2552 * double-click)
2553 * @param singleClickAction action to perform when an item is selected
2554 * (single-click)
2555 * @param filters a list of strings that files must match to be displayed
2556 * @return the new directory list
2557 */
2558 public final TDirectoryList addDirectoryList(final String path, final int x,
2559 final int y, final int width, final int height, final TAction action,
2560 final TAction singleClickAction, final List<String> filters) {
2561
2562 return new TDirectoryList(this, path, x, y, width, height, action,
2563 singleClickAction, filters);
2564 }
2565
2566 /**
2567 * Convenience function to add a list to this container/window.
2568 *
3649b921
KL
2569 * @param strings list of strings to show
2570 * @param x column relative to parent
2571 * @param y row relative to parent
2572 * @param width width of text area
2573 * @param height height of text area
2574 * @return the new directory list
2575 */
2576 public final TList addList(final List<String> strings, final int x,
2577 final int y, final int width, final int height) {
2578
2579 return new TList(this, strings, x, y, width, height, null);
2580 }
2581
2582 /**
a69ed767 2583 * Convenience function to add a list to this container/window.
3649b921
KL
2584 *
2585 * @param strings list of strings to show
2586 * @param x column relative to parent
2587 * @param y row relative to parent
2588 * @param width width of text area
2589 * @param height height of text area
2590 * @param enterAction action to perform when an item is selected
2591 * @return the new directory list
2592 */
2593 public final TList addList(final List<String> strings, final int x,
2594 final int y, final int width, final int height,
2595 final TAction enterAction) {
2596
2597 return new TList(this, strings, x, y, width, height, enterAction);
2598 }
2599
2600 /**
a69ed767 2601 * Convenience function to add a list to this container/window.
3649b921
KL
2602 *
2603 * @param strings list of strings to show
2604 * @param x column relative to parent
2605 * @param y row relative to parent
2606 * @param width width of text area
2607 * @param height height of text area
2608 * @param enterAction action to perform when an item is selected
2609 * @param moveAction action to perform when the user navigates to a new
2610 * item with arrow/page keys
2611 * @return the new directory list
2612 */
2613 public final TList addList(final List<String> strings, final int x,
2614 final int y, final int width, final int height,
2615 final TAction enterAction, final TAction moveAction) {
2616
2617 return new TList(this, strings, x, y, width, height, enterAction,
2618 moveAction);
2619 }
2620
9577a9d0
KL
2621 /**
2622 * Convenience function to add a list to this container/window.
2623 *
2624 * @param strings list of strings to show. This is allowed to be null
2625 * and set later with setList() or by subclasses.
2626 * @param x column relative to parent
2627 * @param y row relative to parent
2628 * @param width width of text area
2629 * @param height height of text area
2630 * @param enterAction action to perform when an item is selected
2631 * @param moveAction action to perform when the user navigates to a new
2632 * item with arrow/page keys
2633 * @param singleClickAction action to perform when the user clicks on an
2634 * item
2635 */
2636 public TList addList(final List<String> strings, final int x,
2637 final int y, final int width, final int height,
2638 final TAction enterAction, final TAction moveAction,
2639 final TAction singleClickAction) {
2640
2641 return new TList(this, strings, x, y, width, height, enterAction,
2642 moveAction, singleClickAction);
2643 }
2644
2645
382bc294
KL
2646 /**
2647 * Convenience function to add an image to this container/window.
2648 *
2649 * @param x column relative to parent
2650 * @param y row relative to parent
2651 * @param width number of text cells for width of the image
2652 * @param height number of text cells for height of the image
2653 * @param image the image to display
2654 * @param left left column of the image. 0 is the left-most column.
2655 * @param top top row of the image. 0 is the top-most row.
2656 */
2657 public final TImage addImage(final int x, final int y,
2658 final int width, final int height,
2659 final BufferedImage image, final int left, final int top) {
2660
2661 return new TImage(this, x, y, width, height, image, left, top);
2662 }
2663
2664 /**
2665 * Convenience function to add an image to this container/window.
2666 *
2667 * @param x column relative to parent
2668 * @param y row relative to parent
2669 * @param width number of text cells for width of the image
2670 * @param height number of text cells for height of the image
2671 * @param image the image to display
2672 * @param left left column of the image. 0 is the left-most column.
2673 * @param top top row of the image. 0 is the top-most row.
2674 * @param clickAction function to call when mouse is pressed
2675 */
2676 public final TImage addImage(final int x, final int y,
2677 final int width, final int height,
2678 final BufferedImage image, final int left, final int top,
2679 final TAction clickAction) {
2680
2681 return new TImage(this, x, y, width, height, image, left, top,
2682 clickAction);
2683 }
2684
2685 /**
2686 * Convenience function to add an editable 2D data table to this
2687 * container/window.
2688 *
2689 * @param x column relative to parent
2690 * @param y row relative to parent
2691 * @param width width of widget
2692 * @param height height of widget
2693 */
2694 public TTableWidget addTable(final int x, final int y, final int width,
2695 final int height) {
2696
2697 return new TTableWidget(this, x, y, width, height);
2698 }
2699
656c0ddd
KL
2700 /**
2701 * Convenience function to add an editable 2D data table to this
2702 * container/window.
2703 *
2704 * @param x column relative to parent
2705 * @param y row relative to parent
2706 * @param width width of widget
2707 * @param height height of widget
2708 * @param gridColumns number of columns in grid
2709 * @param gridRows number of rows in grid
2710 */
2711 public TTableWidget addTable(final int x, final int y, final int width,
2712 final int height, final int gridColumns, final int gridRows) {
2713
2714 return new TTableWidget(this, x, y, width, height, gridColumns,
2715 gridRows);
2716 }
2717
fc2af494
KL
2718 /**
2719 * Convenience function to add a panel to this container/window.
2720 *
2721 * @param x column relative to parent
2722 * @param y row relative to parent
2723 * @param width width of text area
2724 * @param height height of text area
2725 * @return the new panel
2726 */
2727 public final TPanel addPanel(final int x, final int y, final int width,
2728 final int height) {
2729
2730 return new TPanel(this, x, y, width, height);
2731 }
2732
5ffeabcc
KL
2733 /**
2734 * Convenience function to add a split pane to this container/window.
2735 *
2736 * @param x column relative to parent
2737 * @param y row relative to parent
2738 * @param width width of text area
2739 * @param height height of text area
2740 * @param vertical if true, split vertically
2741 * @return the new split pane
2742 */
2743 public final TSplitPane addSplitPane(final int x, final int y,
2744 final int width, final int height, final boolean vertical) {
2745
2746 return new TSplitPane(this, x, y, width, height, vertical);
2747 }
2748
48e27807 2749}