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