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