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