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