#46 BoxLayoutManager working
[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 // Default: do nothing, pass to children instead
615 for (TWidget widget: children) {
616 widget.onMenu(menu);
617 }
618 }
619
620 /**
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().
624 */
625 public void onIdle() {
626 // Default: do nothing, pass to children instead
627 for (TWidget widget: children) {
628 widget.onIdle();
629 }
630 }
631
632 /**
633 * Consume event. Subclasses that want to intercept all events in one go
634 * can override this method.
635 *
636 * @param event keyboard, mouse, resize, command, or menu event
637 */
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;
688 }
689
690 // ------------------------------------------------------------------------
691 // TWidget ----------------------------------------------------------------
692 // ------------------------------------------------------------------------
693
694 /**
695 * Get parent widget.
696 *
697 * @return parent widget
698 */
699 public final TWidget getParent() {
700 return parent;
701 }
702
703 /**
704 * Get the list of child widgets that this widget contains.
705 *
706 * @return the list of child widgets
707 */
708 public List<TWidget> getChildren() {
709 return children;
710 }
711
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
732 /**
733 * Remove a child widget from this container.
734 *
735 * @param child the child widget to remove
736 * @param doClose if true, call the close() method before removing the
737 * child
738 */
739 public final void remove(final TWidget child, final boolean doClose) {
740 if (!children.contains(child)) {
741 throw new IndexOutOfBoundsException("child widget is not in " +
742 "list of children of this parent");
743 }
744 if (doClose) {
745 child.close();
746 }
747 children.remove(child);
748 child.parent = null;
749 if (layout != null) {
750 layout.remove(this);
751 }
752 }
753
754 /**
755 * Set this widget's parent to a different widget.
756 *
757 * @param newParent new parent widget
758 * @param doClose if true, call the close() method before removing the
759 * child from its existing parent widget
760 */
761 public final void setParent(final TWidget newParent,
762 final boolean doClose) {
763
764 if (parent != null) {
765 parent.remove(this, doClose);
766 }
767 assert (parent == null);
768 window = newParent.window;
769 newParent.addChild(this);
770 }
771
772 /**
773 * Set this widget's window to a specific window. Parent must already be
774 * null. Having a null parent with a specified window is only used
775 * within Jexer by TStatusBar because TApplication routes events directly
776 * to it and calls its draw() method. Any other non-parented widgets
777 * will require similar special case functionality to receive events or
778 * be drawn to screen.
779 *
780 * @param window the window to use
781 */
782 public final void setWindow(final TWindow window) {
783
784 if (parent != null) {
785 throw new IllegalArgumentException("Cannot have different " +
786 "windows for parent and child");
787 }
788 this.window = window;
789 }
790
791 /**
792 * Remove a child widget from this container, and all of its children
793 * recursively from their parent containers.
794 *
795 * @param child the child widget to remove
796 * @param doClose if true, call the close() method before removing each
797 * child
798 */
799 public final void removeAll(final TWidget child, final boolean doClose) {
800 remove(child, doClose);
801 for (TWidget w: child.children) {
802 child.removeAll(w, doClose);
803 }
804 }
805
806 /**
807 * Get active flag.
808 *
809 * @return if true, this widget will receive events
810 */
811 public final boolean isActive() {
812 return active;
813 }
814
815 /**
816 * Set active flag.
817 *
818 * @param active if true, this widget will receive events
819 */
820 public final void setActive(final boolean active) {
821 this.active = active;
822 }
823
824 /**
825 * Get the window this widget is on.
826 *
827 * @return the window
828 */
829 public final TWindow getWindow() {
830 return window;
831 }
832
833 /**
834 * Get X position.
835 *
836 * @return absolute X position of the top-left corner
837 */
838 public final int getX() {
839 return x;
840 }
841
842 /**
843 * Set X position.
844 *
845 * @param x absolute X position of the top-left corner
846 */
847 public final void setX(final int x) {
848 this.x = x;
849 }
850
851 /**
852 * Get Y position.
853 *
854 * @return absolute Y position of the top-left corner
855 */
856 public final int getY() {
857 return y;
858 }
859
860 /**
861 * Set Y position.
862 *
863 * @param y absolute Y position of the top-left corner
864 */
865 public final void setY(final int y) {
866 this.y = y;
867 }
868
869 /**
870 * Get the width.
871 *
872 * @return widget width
873 */
874 public int getWidth() {
875 return this.width;
876 }
877
878 /**
879 * Change the width.
880 *
881 * @param width new widget width
882 */
883 public void setWidth(final int width) {
884 this.width = width;
885 if (layout != null) {
886 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
887 width, height));
888 }
889 }
890
891 /**
892 * Get the height.
893 *
894 * @return widget height
895 */
896 public int getHeight() {
897 return this.height;
898 }
899
900 /**
901 * Change the height.
902 *
903 * @param height new widget height
904 */
905 public void setHeight(final int height) {
906 this.height = height;
907 if (layout != null) {
908 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
909 width, height));
910 }
911 }
912
913 /**
914 * Change the dimensions.
915 *
916 * @param x absolute X position of the top-left corner
917 * @param y absolute Y position of the top-left corner
918 * @param width new widget width
919 * @param height new widget height
920 */
921 public final void setDimensions(final int x, final int y, final int width,
922 final int height) {
923
924 setX(x);
925 setY(y);
926 setWidth(width);
927 setHeight(height);
928 if (layout != null) {
929 layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
930 width, height));
931 }
932 }
933
934 /**
935 * Get the layout manager.
936 *
937 * @return the layout manager, or null if not set
938 */
939 public LayoutManager getLayoutManager() {
940 return layout;
941 }
942
943 /**
944 * Set the layout manager.
945 *
946 * @param layout the new layout manager
947 */
948 public void setLayoutManager(LayoutManager layout) {
949 if (this.layout != null) {
950 for (TWidget w: children) {
951 this.layout.remove(w);
952 }
953 this.layout = null;
954 }
955 this.layout = layout;
956 if (this.layout != null) {
957 for (TWidget w: children) {
958 this.layout.add(w);
959 }
960 }
961 }
962
963 /**
964 * Get enabled flag.
965 *
966 * @return if true, this widget can be tabbed to or receive events
967 */
968 public final boolean isEnabled() {
969 return enabled;
970 }
971
972 /**
973 * Set enabled flag.
974 *
975 * @param enabled if true, this widget can be tabbed to or receive events
976 */
977 public final void setEnabled(final boolean enabled) {
978 this.enabled = enabled;
979 if (!enabled) {
980 active = false;
981 // See if there are any active siblings to switch to
982 boolean foundSibling = false;
983 if (parent != null) {
984 for (TWidget w: parent.children) {
985 if ((w.enabled)
986 && !(this instanceof THScroller)
987 && !(this instanceof TVScroller)
988 ) {
989 parent.activate(w);
990 foundSibling = true;
991 break;
992 }
993 }
994 if (!foundSibling) {
995 parent.activeChild = null;
996 }
997 }
998 }
999 }
1000
1001 /**
1002 * Set visible flag.
1003 *
1004 * @param visible if true, this widget will be drawn
1005 */
1006 public final void setVisible(final boolean visible) {
1007 this.visible = visible;
1008 }
1009
1010 /**
1011 * See if this widget is visible.
1012 *
1013 * @return if true, this widget will be drawn
1014 */
1015 public final boolean isVisible() {
1016 return visible;
1017 }
1018
1019 /**
1020 * Set visible cursor flag.
1021 *
1022 * @param cursorVisible if true, this widget has a cursor
1023 */
1024 public final void setCursorVisible(final boolean cursorVisible) {
1025 this.cursorVisible = cursorVisible;
1026 }
1027
1028 /**
1029 * See if this widget has a visible cursor.
1030 *
1031 * @return if true, this widget has a visible cursor
1032 */
1033 public final boolean isCursorVisible() {
1034 // If cursor is out of my bounds, it is not visible.
1035 if ((cursorX >= width)
1036 || (cursorX < 0)
1037 || (cursorY >= height)
1038 || (cursorY < 0)
1039 ) {
1040 return false;
1041 }
1042
1043 assert (window != null);
1044
1045 // If cursor is out of my window's bounds, it is not visible.
1046 if ((getCursorAbsoluteX() >= window.getAbsoluteX()
1047 + window.getWidth() - 1)
1048 || (getCursorAbsoluteX() < 0)
1049 || (getCursorAbsoluteY() >= window.getAbsoluteY()
1050 + window.getHeight() - 1)
1051 || (getCursorAbsoluteY() < 0)
1052 ) {
1053 return false;
1054 }
1055 return cursorVisible;
1056 }
1057
1058 /**
1059 * Get cursor X value.
1060 *
1061 * @return cursor column position in relative coordinates
1062 */
1063 public final int getCursorX() {
1064 return cursorX;
1065 }
1066
1067 /**
1068 * Set cursor X value.
1069 *
1070 * @param cursorX column position in relative coordinates
1071 */
1072 public final void setCursorX(final int cursorX) {
1073 this.cursorX = cursorX;
1074 }
1075
1076 /**
1077 * Get cursor Y value.
1078 *
1079 * @return cursor row position in relative coordinates
1080 */
1081 public final int getCursorY() {
1082 return cursorY;
1083 }
1084
1085 /**
1086 * Set cursor Y value.
1087 *
1088 * @param cursorY row position in relative coordinates
1089 */
1090 public final void setCursorY(final int cursorY) {
1091 this.cursorY = cursorY;
1092 }
1093
1094 /**
1095 * Get this TWidget's parent TApplication.
1096 *
1097 * @return the parent TApplication
1098 */
1099 public TApplication getApplication() {
1100 return window.getApplication();
1101 }
1102
1103 /**
1104 * Get the Screen.
1105 *
1106 * @return the Screen
1107 */
1108 public Screen getScreen() {
1109 return window.getScreen();
1110 }
1111
1112 /**
1113 * Comparison operator. For various subclasses it sorts on:
1114 * <ul>
1115 * <li>tabOrder for TWidgets</li>
1116 * <li>z for TWindows</li>
1117 * <li>text for TTreeItems</li>
1118 * </ul>
1119 *
1120 * @param that another TWidget, TWindow, or TTreeItem instance
1121 * @return difference between this.tabOrder and that.tabOrder, or
1122 * difference between this.z and that.z, or String.compareTo(text)
1123 */
1124 public final int compareTo(final TWidget that) {
1125 if ((this instanceof TWindow)
1126 && (that instanceof TWindow)
1127 ) {
1128 return (((TWindow) this).getZ() - ((TWindow) that).getZ());
1129 }
1130 if ((this instanceof TTreeItem)
1131 && (that instanceof TTreeItem)
1132 ) {
1133 return (((TTreeItem) this).getText().compareTo(
1134 ((TTreeItem) that).getText()));
1135 }
1136 return (this.tabOrder - that.tabOrder);
1137 }
1138
1139 /**
1140 * See if this widget should render with the active color.
1141 *
1142 * @return true if this widget is active and all of its parents are
1143 * active.
1144 */
1145 public final boolean isAbsoluteActive() {
1146 if (parent == this) {
1147 return active;
1148 }
1149 return (active && (parent == null ? true : parent.isAbsoluteActive()));
1150 }
1151
1152 /**
1153 * Returns the cursor X position.
1154 *
1155 * @return absolute screen column number for the cursor's X position
1156 */
1157 public final int getCursorAbsoluteX() {
1158 return getAbsoluteX() + cursorX;
1159 }
1160
1161 /**
1162 * Returns the cursor Y position.
1163 *
1164 * @return absolute screen row number for the cursor's Y position
1165 */
1166 public final int getCursorAbsoluteY() {
1167 return getAbsoluteY() + cursorY;
1168 }
1169
1170 /**
1171 * Compute my absolute X position as the sum of my X plus all my parent's
1172 * X's.
1173 *
1174 * @return absolute screen column number for my X position
1175 */
1176 public final int getAbsoluteX() {
1177 assert (parent != null);
1178 if (parent == this) {
1179 return x;
1180 }
1181 if ((parent instanceof TWindow)
1182 && !(parent instanceof TMenu)
1183 && !(parent instanceof TDesktop)
1184 ) {
1185 // Widgets on a TWindow have (0,0) as their top-left, but this is
1186 // actually the TWindow's (1,1).
1187 return parent.getAbsoluteX() + x + 1;
1188 }
1189 return parent.getAbsoluteX() + x;
1190 }
1191
1192 /**
1193 * Compute my absolute Y position as the sum of my Y plus all my parent's
1194 * Y's.
1195 *
1196 * @return absolute screen row number for my Y position
1197 */
1198 public final int getAbsoluteY() {
1199 assert (parent != null);
1200 if (parent == this) {
1201 return y;
1202 }
1203 if ((parent instanceof TWindow)
1204 && !(parent instanceof TMenu)
1205 && !(parent instanceof TDesktop)
1206 ) {
1207 // Widgets on a TWindow have (0,0) as their top-left, but this is
1208 // actually the TWindow's (1,1).
1209 return parent.getAbsoluteY() + y + 1;
1210 }
1211 return parent.getAbsoluteY() + y;
1212 }
1213
1214 /**
1215 * Get the global color theme.
1216 *
1217 * @return the ColorTheme
1218 */
1219 protected final ColorTheme getTheme() {
1220 return window.getApplication().getTheme();
1221 }
1222
1223 /**
1224 * Draw my specific widget. When called, the screen rectangle I draw
1225 * into is already setup (offset and clipping).
1226 */
1227 public void draw() {
1228 // Default widget draws nothing.
1229 }
1230
1231 /**
1232 * Called by parent to render to TWindow. Note package private access.
1233 */
1234 final void drawChildren() {
1235 // Set my clipping rectangle
1236 assert (window != null);
1237 assert (getScreen() != null);
1238 Screen screen = getScreen();
1239
1240 // Special case: TStatusBar is drawn by TApplication, not anything
1241 // else.
1242 if (this instanceof TStatusBar) {
1243 return;
1244 }
1245
1246 screen.setClipRight(width);
1247 screen.setClipBottom(height);
1248
1249 int absoluteRightEdge = window.getAbsoluteX() + window.getWidth();
1250 int absoluteBottomEdge = window.getAbsoluteY() + window.getHeight();
1251 if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
1252 absoluteRightEdge -= 1;
1253 }
1254 if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
1255 absoluteBottomEdge -= 1;
1256 }
1257 int myRightEdge = getAbsoluteX() + width;
1258 int myBottomEdge = getAbsoluteY() + height;
1259 if (getAbsoluteX() > absoluteRightEdge) {
1260 // I am offscreen
1261 screen.setClipRight(0);
1262 } else if (myRightEdge > absoluteRightEdge) {
1263 screen.setClipRight(screen.getClipRight()
1264 - (myRightEdge - absoluteRightEdge));
1265 }
1266 if (getAbsoluteY() > absoluteBottomEdge) {
1267 // I am offscreen
1268 screen.setClipBottom(0);
1269 } else if (myBottomEdge > absoluteBottomEdge) {
1270 screen.setClipBottom(screen.getClipBottom()
1271 - (myBottomEdge - absoluteBottomEdge));
1272 }
1273
1274 // Set my offset
1275 screen.setOffsetX(getAbsoluteX());
1276 screen.setOffsetY(getAbsoluteY());
1277
1278 // Draw me
1279 draw();
1280 assert (visible == true);
1281
1282 // Continue down the chain. Draw the active child last so that it
1283 // is on top.
1284 for (TWidget widget: children) {
1285 if (widget.isVisible() && (widget != activeChild)) {
1286 widget.drawChildren();
1287 }
1288 }
1289 if (activeChild != null) {
1290 activeChild.drawChildren();
1291 }
1292 }
1293
1294 /**
1295 * Repaint the screen on the next update.
1296 */
1297 protected final void doRepaint() {
1298 window.getApplication().doRepaint();
1299 }
1300
1301 /**
1302 * Add a child widget to my list of children. We set its tabOrder to 0
1303 * and increment the tabOrder of all other children.
1304 *
1305 * @param child TWidget to add
1306 */
1307 private void addChild(final TWidget child) {
1308 children.add(child);
1309
1310 if ((child.enabled)
1311 && !(child instanceof THScroller)
1312 && !(child instanceof TVScroller)
1313 ) {
1314 for (TWidget widget: children) {
1315 widget.active = false;
1316 }
1317 child.active = true;
1318 activeChild = child;
1319 }
1320 for (int i = 0; i < children.size(); i++) {
1321 children.get(i).tabOrder = i;
1322 }
1323 if (layout != null) {
1324 layout.add(child);
1325 }
1326 }
1327
1328 /**
1329 * Reset the tab order of children to match their position in the list.
1330 * Available so that subclasses can re-order their widgets if needed.
1331 */
1332 protected void resetTabOrder() {
1333 for (int i = 0; i < children.size(); i++) {
1334 children.get(i).tabOrder = i;
1335 }
1336 }
1337
1338 /**
1339 * Switch the active child.
1340 *
1341 * @param child TWidget to activate
1342 */
1343 public final void activate(final TWidget child) {
1344 assert (child.enabled);
1345 if ((child instanceof THScroller)
1346 || (child instanceof TVScroller)
1347 ) {
1348 return;
1349 }
1350
1351 if (children.size() == 1) {
1352 if (children.get(0).enabled == true) {
1353 child.active = true;
1354 activeChild = child;
1355 }
1356 } else {
1357 if (child != activeChild) {
1358 if (activeChild != null) {
1359 activeChild.active = false;
1360 }
1361 child.active = true;
1362 activeChild = child;
1363 }
1364 }
1365 }
1366
1367 /**
1368 * Switch the active child.
1369 *
1370 * @param tabOrder tabOrder of the child to activate. If that child
1371 * isn't enabled, then the next enabled child will be activated.
1372 */
1373 public final void activate(final int tabOrder) {
1374 if (children.size() == 1) {
1375 if (children.get(0).enabled == true) {
1376 children.get(0).active = true;
1377 activeChild = children.get(0);
1378 }
1379 return;
1380 }
1381
1382 TWidget child = null;
1383 for (TWidget widget: children) {
1384 if ((widget.enabled)
1385 && !(widget instanceof THScroller)
1386 && !(widget instanceof TVScroller)
1387 && (widget.tabOrder >= tabOrder)
1388 ) {
1389 child = widget;
1390 break;
1391 }
1392 }
1393 if ((child != null) && (child != activeChild)) {
1394 if (activeChild != null) {
1395 activeChild.active = false;
1396 }
1397 assert (child.enabled);
1398 child.active = true;
1399 activeChild = child;
1400 }
1401 }
1402
1403 /**
1404 * Make this widget the active child of its parent. Note that this is
1405 * not final since TWindow overrides activate().
1406 */
1407 public void activate() {
1408 if (enabled) {
1409 if (parent != null) {
1410 parent.activate(this);
1411 }
1412 }
1413 }
1414
1415 /**
1416 * Switch the active widget with the next in the tab order.
1417 *
1418 * @param forward if true, then switch to the next enabled widget in the
1419 * list, otherwise switch to the previous enabled widget in the list
1420 */
1421 public final void switchWidget(final boolean forward) {
1422
1423 // No children: do nothing.
1424 if (children.size() == 0) {
1425 return;
1426 }
1427
1428 assert (parent != null);
1429
1430 // If there is only one child, make it active if it is enabled.
1431 if (children.size() == 1) {
1432 if (children.get(0).enabled == true) {
1433 activeChild = children.get(0);
1434 activeChild.active = true;
1435 } else {
1436 children.get(0).active = false;
1437 activeChild = null;
1438 }
1439 return;
1440 }
1441
1442 // Two or more children: go forward or backward to the next enabled
1443 // child.
1444 int tabOrder = 0;
1445 if (activeChild != null) {
1446 tabOrder = activeChild.tabOrder;
1447 }
1448 do {
1449 if (forward) {
1450 tabOrder++;
1451 } else {
1452 tabOrder--;
1453 }
1454 if (tabOrder < 0) {
1455
1456 // If at the end, pass the switch to my parent.
1457 if ((!forward) && (parent != this)) {
1458 parent.switchWidget(forward);
1459 return;
1460 }
1461
1462 tabOrder = children.size() - 1;
1463 } else if (tabOrder == children.size()) {
1464 // If at the end, pass the switch to my parent.
1465 if ((forward) && (parent != this)) {
1466 parent.switchWidget(forward);
1467 return;
1468 }
1469
1470 tabOrder = 0;
1471 }
1472 if (activeChild == null) {
1473 if (tabOrder == 0) {
1474 // We wrapped around
1475 break;
1476 }
1477 } else if (activeChild.tabOrder == tabOrder) {
1478 // We wrapped around
1479 break;
1480 }
1481 } while ((!children.get(tabOrder).enabled)
1482 && !(children.get(tabOrder) instanceof THScroller)
1483 && !(children.get(tabOrder) instanceof TVScroller));
1484
1485 if (activeChild != null) {
1486 assert (children.get(tabOrder).enabled);
1487
1488 activeChild.active = false;
1489 }
1490 if (children.get(tabOrder).enabled == true) {
1491 children.get(tabOrder).active = true;
1492 activeChild = children.get(tabOrder);
1493 }
1494 }
1495
1496 /**
1497 * Returns my active widget.
1498 *
1499 * @return widget that is active, or this if no children
1500 */
1501 public TWidget getActiveChild() {
1502 if ((this instanceof THScroller)
1503 || (this instanceof TVScroller)
1504 ) {
1505 return parent;
1506 }
1507
1508 for (TWidget widget: children) {
1509 if (widget.active) {
1510 return widget.getActiveChild();
1511 }
1512 }
1513 // No active children, return me
1514 return this;
1515 }
1516
1517 // ------------------------------------------------------------------------
1518 // Passthru for Screen functions ------------------------------------------
1519 // ------------------------------------------------------------------------
1520
1521 /**
1522 * Get the attributes at one location.
1523 *
1524 * @param x column coordinate. 0 is the left-most column.
1525 * @param y row coordinate. 0 is the top-most row.
1526 * @return attributes at (x, y)
1527 */
1528 protected final CellAttributes getAttrXY(final int x, final int y) {
1529 return getScreen().getAttrXY(x, y);
1530 }
1531
1532 /**
1533 * Set the attributes at one location.
1534 *
1535 * @param x column coordinate. 0 is the left-most column.
1536 * @param y row coordinate. 0 is the top-most row.
1537 * @param attr attributes to use (bold, foreColor, backColor)
1538 */
1539 protected final void putAttrXY(final int x, final int y,
1540 final CellAttributes attr) {
1541
1542 getScreen().putAttrXY(x, y, attr);
1543 }
1544
1545 /**
1546 * Set the attributes at one location.
1547 *
1548 * @param x column coordinate. 0 is the left-most column.
1549 * @param y row coordinate. 0 is the top-most row.
1550 * @param attr attributes to use (bold, foreColor, backColor)
1551 * @param clip if true, honor clipping/offset
1552 */
1553 protected final void putAttrXY(final int x, final int y,
1554 final CellAttributes attr, final boolean clip) {
1555
1556 getScreen().putAttrXY(x, y, attr, clip);
1557 }
1558
1559 /**
1560 * Fill the entire screen with one character with attributes.
1561 *
1562 * @param ch character to draw
1563 * @param attr attributes to use (bold, foreColor, backColor)
1564 */
1565 protected final void putAll(final int ch, final CellAttributes attr) {
1566 getScreen().putAll(ch, attr);
1567 }
1568
1569 /**
1570 * Render one character with attributes.
1571 *
1572 * @param x column coordinate. 0 is the left-most column.
1573 * @param y row coordinate. 0 is the top-most row.
1574 * @param ch character + attributes to draw
1575 */
1576 protected final void putCharXY(final int x, final int y, final Cell ch) {
1577 getScreen().putCharXY(x, y, ch);
1578 }
1579
1580 /**
1581 * Render one character with attributes.
1582 *
1583 * @param x column coordinate. 0 is the left-most column.
1584 * @param y row coordinate. 0 is the top-most row.
1585 * @param ch character to draw
1586 * @param attr attributes to use (bold, foreColor, backColor)
1587 */
1588 protected final void putCharXY(final int x, final int y, final int ch,
1589 final CellAttributes attr) {
1590
1591 getScreen().putCharXY(x, y, ch, attr);
1592 }
1593
1594 /**
1595 * Render one character without changing the underlying attributes.
1596 *
1597 * @param x column coordinate. 0 is the left-most column.
1598 * @param y row coordinate. 0 is the top-most row.
1599 * @param ch character to draw
1600 */
1601 protected final void putCharXY(final int x, final int y, final int ch) {
1602 getScreen().putCharXY(x, y, ch);
1603 }
1604
1605 /**
1606 * Render a string. Does not wrap if the string exceeds the line.
1607 *
1608 * @param x column coordinate. 0 is the left-most column.
1609 * @param y row coordinate. 0 is the top-most row.
1610 * @param str string to draw
1611 * @param attr attributes to use (bold, foreColor, backColor)
1612 */
1613 protected final void putStringXY(final int x, final int y, final String str,
1614 final CellAttributes attr) {
1615
1616 getScreen().putStringXY(x, y, str, attr);
1617 }
1618
1619 /**
1620 * Render a string without changing the underlying attribute. Does not
1621 * wrap if the string exceeds the line.
1622 *
1623 * @param x column coordinate. 0 is the left-most column.
1624 * @param y row coordinate. 0 is the top-most row.
1625 * @param str string to draw
1626 */
1627 protected final void putStringXY(final int x, final int y, final String str) {
1628 getScreen().putStringXY(x, y, str);
1629 }
1630
1631 /**
1632 * Draw a vertical line from (x, y) to (x, y + n).
1633 *
1634 * @param x column coordinate. 0 is the left-most column.
1635 * @param y row coordinate. 0 is the top-most row.
1636 * @param n number of characters to draw
1637 * @param ch character to draw
1638 * @param attr attributes to use (bold, foreColor, backColor)
1639 */
1640 protected final void vLineXY(final int x, final int y, final int n,
1641 final int ch, final CellAttributes attr) {
1642
1643 getScreen().vLineXY(x, y, n, ch, attr);
1644 }
1645
1646 /**
1647 * Draw a horizontal line from (x, y) to (x + n, y).
1648 *
1649 * @param x column coordinate. 0 is the left-most column.
1650 * @param y row coordinate. 0 is the top-most row.
1651 * @param n number of characters to draw
1652 * @param ch character to draw
1653 * @param attr attributes to use (bold, foreColor, backColor)
1654 */
1655 protected final void hLineXY(final int x, final int y, final int n,
1656 final int ch, final CellAttributes attr) {
1657
1658 getScreen().hLineXY(x, y, n, ch, attr);
1659 }
1660
1661 /**
1662 * Draw a box with a border and empty background.
1663 *
1664 * @param left left column of box. 0 is the left-most row.
1665 * @param top top row of the box. 0 is the top-most row.
1666 * @param right right column of box
1667 * @param bottom bottom row of the box
1668 * @param border attributes to use for the border
1669 * @param background attributes to use for the background
1670 */
1671 protected final void drawBox(final int left, final int top,
1672 final int right, final int bottom,
1673 final CellAttributes border, final CellAttributes background) {
1674
1675 getScreen().drawBox(left, top, right, bottom, border, background);
1676 }
1677
1678 /**
1679 * Draw a box with a border and empty background.
1680 *
1681 * @param left left column of box. 0 is the left-most row.
1682 * @param top top row of the box. 0 is the top-most row.
1683 * @param right right column of box
1684 * @param bottom bottom row of the box
1685 * @param border attributes to use for the border
1686 * @param background attributes to use for the background
1687 * @param borderType if 1, draw a single-line border; if 2, draw a
1688 * double-line border; if 3, draw double-line top/bottom edges and
1689 * single-line left/right edges (like Qmodem)
1690 * @param shadow if true, draw a "shadow" on the box
1691 */
1692 protected final void drawBox(final int left, final int top,
1693 final int right, final int bottom,
1694 final CellAttributes border, final CellAttributes background,
1695 final int borderType, final boolean shadow) {
1696
1697 getScreen().drawBox(left, top, right, bottom, border, background,
1698 borderType, shadow);
1699 }
1700
1701 /**
1702 * Draw a box shadow.
1703 *
1704 * @param left left column of box. 0 is the left-most row.
1705 * @param top top row of the box. 0 is the top-most row.
1706 * @param right right column of box
1707 * @param bottom bottom row of the box
1708 */
1709 protected final void drawBoxShadow(final int left, final int top,
1710 final int right, final int bottom) {
1711
1712 getScreen().drawBoxShadow(left, top, right, bottom);
1713 }
1714
1715 // ------------------------------------------------------------------------
1716 // Other TWidget constructors ---------------------------------------------
1717 // ------------------------------------------------------------------------
1718
1719 /**
1720 * Convenience function to add a label to this container/window.
1721 *
1722 * @param text label
1723 * @param x column relative to parent
1724 * @param y row relative to parent
1725 * @return the new label
1726 */
1727 public final TLabel addLabel(final String text, final int x, final int y) {
1728 return addLabel(text, x, y, "tlabel");
1729 }
1730
1731 /**
1732 * Convenience function to add a label to this container/window.
1733 *
1734 * @param text label
1735 * @param x column relative to parent
1736 * @param y row relative to parent
1737 * @param action to call when shortcut is pressed
1738 * @return the new label
1739 */
1740 public final TLabel addLabel(final String text, final int x, final int y,
1741 final TAction action) {
1742
1743 return addLabel(text, x, y, "tlabel", action);
1744 }
1745
1746 /**
1747 * Convenience function to add a label to this container/window.
1748 *
1749 * @param text label
1750 * @param x column relative to parent
1751 * @param y row relative to parent
1752 * @param colorKey ColorTheme key color to use for foreground text.
1753 * Default is "tlabel"
1754 * @return the new label
1755 */
1756 public final TLabel addLabel(final String text, final int x, final int y,
1757 final String colorKey) {
1758
1759 return new TLabel(this, text, x, y, colorKey);
1760 }
1761
1762 /**
1763 * Convenience function to add a label to this container/window.
1764 *
1765 * @param text label
1766 * @param x column relative to parent
1767 * @param y row relative to parent
1768 * @param colorKey ColorTheme key color to use for foreground text.
1769 * Default is "tlabel"
1770 * @param action to call when shortcut is pressed
1771 * @return the new label
1772 */
1773 public final TLabel addLabel(final String text, final int x, final int y,
1774 final String colorKey, final TAction action) {
1775
1776 return new TLabel(this, text, x, y, colorKey, action);
1777 }
1778
1779 /**
1780 * Convenience function to add a label to this container/window.
1781 *
1782 * @param text label
1783 * @param x column relative to parent
1784 * @param y row relative to parent
1785 * @param colorKey ColorTheme key color to use for foreground text.
1786 * Default is "tlabel"
1787 * @param useWindowBackground if true, use the window's background color
1788 * @return the new label
1789 */
1790 public final TLabel addLabel(final String text, final int x, final int y,
1791 final String colorKey, final boolean useWindowBackground) {
1792
1793 return new TLabel(this, text, x, y, colorKey, useWindowBackground);
1794 }
1795
1796 /**
1797 * Convenience function to add a label to this container/window.
1798 *
1799 * @param text label
1800 * @param x column relative to parent
1801 * @param y row relative to parent
1802 * @param colorKey ColorTheme key color to use for foreground text.
1803 * Default is "tlabel"
1804 * @param useWindowBackground if true, use the window's background color
1805 * @param action to call when shortcut is pressed
1806 * @return the new label
1807 */
1808 public final TLabel addLabel(final String text, final int x, final int y,
1809 final String colorKey, final boolean useWindowBackground,
1810 final TAction action) {
1811
1812 return new TLabel(this, text, x, y, colorKey, useWindowBackground,
1813 action);
1814 }
1815
1816 /**
1817 * Convenience function to add a button to this container/window.
1818 *
1819 * @param text label on the button
1820 * @param x column relative to parent
1821 * @param y row relative to parent
1822 * @param action action to call when button is pressed
1823 * @return the new button
1824 */
1825 public final TButton addButton(final String text, final int x, final int y,
1826 final TAction action) {
1827
1828 return new TButton(this, text, x, y, action);
1829 }
1830
1831 /**
1832 * Convenience function to add a checkbox to this container/window.
1833 *
1834 * @param x column relative to parent
1835 * @param y row relative to parent
1836 * @param label label to display next to (right of) the checkbox
1837 * @param checked initial check state
1838 * @return the new checkbox
1839 */
1840 public final TCheckBox addCheckBox(final int x, final int y,
1841 final String label, final boolean checked) {
1842
1843 return new TCheckBox(this, x, y, label, checked);
1844 }
1845
1846 /**
1847 * Convenience function to add a combobox to this container/window.
1848 *
1849 * @param x column relative to parent
1850 * @param y row relative to parent
1851 * @param width visible combobox width, including the down-arrow
1852 * @param values the possible values for the box, shown in the drop-down
1853 * @param valuesIndex the initial index in values, or -1 for no default
1854 * value
1855 * @param maxValuesHeight the maximum height of the values drop-down when
1856 * it is visible
1857 * @param updateAction action to call when a new value is selected from
1858 * the list or enter is pressed in the edit field
1859 * @return the new combobox
1860 */
1861 public final TComboBox addComboBox(final int x, final int y,
1862 final int width, final List<String> values, final int valuesIndex,
1863 final int maxValuesHeight, final TAction updateAction) {
1864
1865 return new TComboBox(this, x, y, width, values, valuesIndex,
1866 maxValuesHeight, updateAction);
1867 }
1868
1869 /**
1870 * Convenience function to add a spinner to this container/window.
1871 *
1872 * @param x column relative to parent
1873 * @param y row relative to parent
1874 * @param upAction action to call when the up arrow is clicked or pressed
1875 * @param downAction action to call when the down arrow is clicked or
1876 * pressed
1877 * @return the new spinner
1878 */
1879 public final TSpinner addSpinner(final int x, final int y,
1880 final TAction upAction, final TAction downAction) {
1881
1882 return new TSpinner(this, x, y, upAction, downAction);
1883 }
1884
1885 /**
1886 * Convenience function to add a calendar to this container/window.
1887 *
1888 * @param x column relative to parent
1889 * @param y row relative to parent
1890 * @param updateAction action to call when the user changes the value of
1891 * the calendar
1892 * @return the new calendar
1893 */
1894 public final TCalendar addCalendar(final int x, final int y,
1895 final TAction updateAction) {
1896
1897 return new TCalendar(this, x, y, updateAction);
1898 }
1899
1900 /**
1901 * Convenience function to add a progress bar to this container/window.
1902 *
1903 * @param x column relative to parent
1904 * @param y row relative to parent
1905 * @param width width of progress bar
1906 * @param value initial value of percent complete
1907 * @return the new progress bar
1908 */
1909 public final TProgressBar addProgressBar(final int x, final int y,
1910 final int width, final int value) {
1911
1912 return new TProgressBar(this, x, y, width, value);
1913 }
1914
1915 /**
1916 * Convenience function to add a radio button group to this
1917 * container/window.
1918 *
1919 * @param x column relative to parent
1920 * @param y row relative to parent
1921 * @param label label to display on the group box
1922 * @return the new radio button group
1923 */
1924 public final TRadioGroup addRadioGroup(final int x, final int y,
1925 final String label) {
1926
1927 return new TRadioGroup(this, x, y, label);
1928 }
1929
1930 /**
1931 * Convenience function to add a text field to this container/window.
1932 *
1933 * @param x column relative to parent
1934 * @param y row relative to parent
1935 * @param width visible text width
1936 * @param fixed if true, the text cannot exceed the display width
1937 * @return the new text field
1938 */
1939 public final TField addField(final int x, final int y,
1940 final int width, final boolean fixed) {
1941
1942 return new TField(this, x, y, width, fixed);
1943 }
1944
1945 /**
1946 * Convenience function to add a text field to this container/window.
1947 *
1948 * @param x column relative to parent
1949 * @param y row relative to parent
1950 * @param width visible text width
1951 * @param fixed if true, the text cannot exceed the display width
1952 * @param text initial text, default is empty string
1953 * @return the new text field
1954 */
1955 public final TField addField(final int x, final int y,
1956 final int width, final boolean fixed, final String text) {
1957
1958 return new TField(this, x, y, width, fixed, text);
1959 }
1960
1961 /**
1962 * Convenience function to add a text field to this container/window.
1963 *
1964 * @param x column relative to parent
1965 * @param y row relative to parent
1966 * @param width visible text width
1967 * @param fixed if true, the text cannot exceed the display width
1968 * @param text initial text, default is empty string
1969 * @param enterAction function to call when enter key is pressed
1970 * @param updateAction function to call when the text is updated
1971 * @return the new text field
1972 */
1973 public final TField addField(final int x, final int y,
1974 final int width, final boolean fixed, final String text,
1975 final TAction enterAction, final TAction updateAction) {
1976
1977 return new TField(this, x, y, width, fixed, text, enterAction,
1978 updateAction);
1979 }
1980
1981 /**
1982 * Convenience function to add a scrollable text box to this
1983 * container/window.
1984 *
1985 * @param text text on the screen
1986 * @param x column relative to parent
1987 * @param y row relative to parent
1988 * @param width width of text area
1989 * @param height height of text area
1990 * @param colorKey ColorTheme key color to use for foreground text
1991 * @return the new text box
1992 */
1993 public final TText addText(final String text, final int x,
1994 final int y, final int width, final int height, final String colorKey) {
1995
1996 return new TText(this, text, x, y, width, height, colorKey);
1997 }
1998
1999 /**
2000 * Convenience function to add a scrollable text box to this
2001 * container/window.
2002 *
2003 * @param text text on the screen
2004 * @param x column relative to parent
2005 * @param y row relative to parent
2006 * @param width width of text area
2007 * @param height height of text area
2008 * @return the new text box
2009 */
2010 public final TText addText(final String text, final int x, final int y,
2011 final int width, final int height) {
2012
2013 return new TText(this, text, x, y, width, height, "ttext");
2014 }
2015
2016 /**
2017 * Convenience function to add an editable text area box to this
2018 * container/window.
2019 *
2020 * @param text text on the screen
2021 * @param x column relative to parent
2022 * @param y row relative to parent
2023 * @param width width of text area
2024 * @param height height of text area
2025 * @return the new text box
2026 */
2027 public final TEditorWidget addEditor(final String text, final int x,
2028 final int y, final int width, final int height) {
2029
2030 return new TEditorWidget(this, text, x, y, width, height);
2031 }
2032
2033 /**
2034 * Convenience function to spawn a message box.
2035 *
2036 * @param title window title, will be centered along the top border
2037 * @param caption message to display. Use embedded newlines to get a
2038 * multi-line box.
2039 * @return the new message box
2040 */
2041 public final TMessageBox messageBox(final String title,
2042 final String caption) {
2043
2044 return getApplication().messageBox(title, caption, TMessageBox.Type.OK);
2045 }
2046
2047 /**
2048 * Convenience function to spawn a message box.
2049 *
2050 * @param title window title, will be centered along the top border
2051 * @param caption message to display. Use embedded newlines to get a
2052 * multi-line box.
2053 * @param type one of the TMessageBox.Type constants. Default is
2054 * Type.OK.
2055 * @return the new message box
2056 */
2057 public final TMessageBox messageBox(final String title,
2058 final String caption, final TMessageBox.Type type) {
2059
2060 return getApplication().messageBox(title, caption, type);
2061 }
2062
2063 /**
2064 * Convenience function to spawn an input box.
2065 *
2066 * @param title window title, will be centered along the top border
2067 * @param caption message to display. Use embedded newlines to get a
2068 * multi-line box.
2069 * @return the new input box
2070 */
2071 public final TInputBox inputBox(final String title, final String caption) {
2072
2073 return getApplication().inputBox(title, caption);
2074 }
2075
2076 /**
2077 * Convenience function to spawn an input box.
2078 *
2079 * @param title window title, will be centered along the top border
2080 * @param caption message to display. Use embedded newlines to get a
2081 * multi-line box.
2082 * @param text initial text to seed the field with
2083 * @return the new input box
2084 */
2085 public final TInputBox inputBox(final String title, final String caption,
2086 final String text) {
2087
2088 return getApplication().inputBox(title, caption, text);
2089 }
2090
2091 /**
2092 * Convenience function to spawn an input box.
2093 *
2094 * @param title window title, will be centered along the top border
2095 * @param caption message to display. Use embedded newlines to get a
2096 * multi-line box.
2097 * @param text initial text to seed the field with
2098 * @param type one of the Type constants. Default is Type.OK.
2099 * @return the new input box
2100 */
2101 public final TInputBox inputBox(final String title, final String caption,
2102 final String text, final TInputBox.Type type) {
2103
2104 return getApplication().inputBox(title, caption, text, type);
2105 }
2106
2107 /**
2108 * Convenience function to add a password text field to this
2109 * container/window.
2110 *
2111 * @param x column relative to parent
2112 * @param y row relative to parent
2113 * @param width visible text width
2114 * @param fixed if true, the text cannot exceed the display width
2115 * @return the new text field
2116 */
2117 public final TPasswordField addPasswordField(final int x, final int y,
2118 final int width, final boolean fixed) {
2119
2120 return new TPasswordField(this, x, y, width, fixed);
2121 }
2122
2123 /**
2124 * Convenience function to add a password text field to this
2125 * container/window.
2126 *
2127 * @param x column relative to parent
2128 * @param y row relative to parent
2129 * @param width visible text width
2130 * @param fixed if true, the text cannot exceed the display width
2131 * @param text initial text, default is empty string
2132 * @return the new text field
2133 */
2134 public final TPasswordField addPasswordField(final int x, final int y,
2135 final int width, final boolean fixed, final String text) {
2136
2137 return new TPasswordField(this, x, y, width, fixed, text);
2138 }
2139
2140 /**
2141 * Convenience function to add a password text field to this
2142 * container/window.
2143 *
2144 * @param x column relative to parent
2145 * @param y row relative to parent
2146 * @param width visible text width
2147 * @param fixed if true, the text cannot exceed the display width
2148 * @param text initial text, default is empty string
2149 * @param enterAction function to call when enter key is pressed
2150 * @param updateAction function to call when the text is updated
2151 * @return the new text field
2152 */
2153 public final TPasswordField addPasswordField(final int x, final int y,
2154 final int width, final boolean fixed, final String text,
2155 final TAction enterAction, final TAction updateAction) {
2156
2157 return new TPasswordField(this, x, y, width, fixed, text, enterAction,
2158 updateAction);
2159 }
2160
2161 /**
2162 * Convenience function to add a scrollable tree view to this
2163 * container/window.
2164 *
2165 * @param x column relative to parent
2166 * @param y row relative to parent
2167 * @param width width of tree view
2168 * @param height height of tree view
2169 * @return the new tree view
2170 */
2171 public final TTreeViewWidget addTreeViewWidget(final int x, final int y,
2172 final int width, final int height) {
2173
2174 return new TTreeViewWidget(this, x, y, width, height);
2175 }
2176
2177 /**
2178 * Convenience function to add a scrollable tree view to this
2179 * container/window.
2180 *
2181 * @param x column relative to parent
2182 * @param y row relative to parent
2183 * @param width width of tree view
2184 * @param height height of tree view
2185 * @param action action to perform when an item is selected
2186 * @return the new tree view
2187 */
2188 public final TTreeViewWidget addTreeViewWidget(final int x, final int y,
2189 final int width, final int height, final TAction action) {
2190
2191 return new TTreeViewWidget(this, x, y, width, height, action);
2192 }
2193
2194 /**
2195 * Convenience function to spawn a file open box.
2196 *
2197 * @param path path of selected file
2198 * @return the result of the new file open box
2199 * @throws IOException if a java.io operation throws
2200 */
2201 public final String fileOpenBox(final String path) throws IOException {
2202 return getApplication().fileOpenBox(path);
2203 }
2204
2205 /**
2206 * Convenience function to spawn a file save box.
2207 *
2208 * @param path path of selected file
2209 * @return the result of the new file open box
2210 * @throws IOException if a java.io operation throws
2211 */
2212 public final String fileSaveBox(final String path) throws IOException {
2213 return getApplication().fileOpenBox(path, TFileOpenBox.Type.SAVE);
2214 }
2215
2216 /**
2217 * Convenience function to spawn a file open box.
2218 *
2219 * @param path path of selected file
2220 * @param type one of the Type constants
2221 * @return the result of the new file open box
2222 * @throws IOException if a java.io operation throws
2223 */
2224 public final String fileOpenBox(final String path,
2225 final TFileOpenBox.Type type) throws IOException {
2226
2227 return getApplication().fileOpenBox(path, type);
2228 }
2229
2230 /**
2231 * Convenience function to spawn a file open box.
2232 *
2233 * @param path path of selected file
2234 * @param type one of the Type constants
2235 * @param filter a string that files must match to be displayed
2236 * @return the result of the new file open box
2237 * @throws IOException of a java.io operation throws
2238 */
2239 public final String fileOpenBox(final String path,
2240 final TFileOpenBox.Type type, final String filter) throws IOException {
2241
2242 ArrayList<String> filters = new ArrayList<String>();
2243 filters.add(filter);
2244
2245 return getApplication().fileOpenBox(path, type, filters);
2246 }
2247
2248 /**
2249 * Convenience function to spawn a file open box.
2250 *
2251 * @param path path of selected file
2252 * @param type one of the Type constants
2253 * @param filters a list of strings that files must match to be displayed
2254 * @return the result of the new file open box
2255 * @throws IOException of a java.io operation throws
2256 */
2257 public final String fileOpenBox(final String path,
2258 final TFileOpenBox.Type type,
2259 final List<String> filters) throws IOException {
2260
2261 return getApplication().fileOpenBox(path, type, filters);
2262 }
2263
2264 /**
2265 * Convenience function to add a directory list to this container/window.
2266 *
2267 * @param path directory path, must be a directory
2268 * @param x column relative to parent
2269 * @param y row relative to parent
2270 * @param width width of text area
2271 * @param height height of text area
2272 * @return the new directory list
2273 */
2274 public final TDirectoryList addDirectoryList(final String path, final int x,
2275 final int y, final int width, final int height) {
2276
2277 return new TDirectoryList(this, path, x, y, width, height, null);
2278 }
2279
2280 /**
2281 * Convenience function to add a directory list to this container/window.
2282 *
2283 * @param path directory path, must be a directory
2284 * @param x column relative to parent
2285 * @param y row relative to parent
2286 * @param width width of text area
2287 * @param height height of text area
2288 * @param action action to perform when an item is selected (enter or
2289 * double-click)
2290 * @return the new directory list
2291 */
2292 public final TDirectoryList addDirectoryList(final String path, final int x,
2293 final int y, final int width, final int height, final TAction action) {
2294
2295 return new TDirectoryList(this, path, x, y, width, height, action);
2296 }
2297
2298 /**
2299 * Convenience function to add a directory list to this container/window.
2300 *
2301 * @param path directory path, must be a directory
2302 * @param x column relative to parent
2303 * @param y row relative to parent
2304 * @param width width of text area
2305 * @param height height of text area
2306 * @param action action to perform when an item is selected (enter or
2307 * double-click)
2308 * @param singleClickAction action to perform when an item is selected
2309 * (single-click)
2310 * @return the new directory list
2311 */
2312 public final TDirectoryList addDirectoryList(final String path, final int x,
2313 final int y, final int width, final int height, final TAction action,
2314 final TAction singleClickAction) {
2315
2316 return new TDirectoryList(this, path, x, y, width, height, action,
2317 singleClickAction);
2318 }
2319
2320 /**
2321 * Convenience function to add a directory list to this container/window.
2322 *
2323 * @param path directory path, must be a directory
2324 * @param x column relative to parent
2325 * @param y row relative to parent
2326 * @param width width of text area
2327 * @param height height of text area
2328 * @param action action to perform when an item is selected (enter or
2329 * double-click)
2330 * @param singleClickAction action to perform when an item is selected
2331 * (single-click)
2332 * @param filters a list of strings that files must match to be displayed
2333 * @return the new directory list
2334 */
2335 public final TDirectoryList addDirectoryList(final String path, final int x,
2336 final int y, final int width, final int height, final TAction action,
2337 final TAction singleClickAction, final List<String> filters) {
2338
2339 return new TDirectoryList(this, path, x, y, width, height, action,
2340 singleClickAction, filters);
2341 }
2342
2343 /**
2344 * Convenience function to add a list to this container/window.
2345 *
2346 * @param strings list of strings to show
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 TList addList(final List<String> strings, final int x,
2354 final int y, final int width, final int height) {
2355
2356 return new TList(this, strings, x, y, width, height, null);
2357 }
2358
2359 /**
2360 * Convenience function to add a list to this container/window.
2361 *
2362 * @param strings list of strings to show
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 enterAction action to perform when an item is selected
2368 * @return the new directory list
2369 */
2370 public final TList addList(final List<String> strings, final int x,
2371 final int y, final int width, final int height,
2372 final TAction enterAction) {
2373
2374 return new TList(this, strings, x, y, width, height, enterAction);
2375 }
2376
2377 /**
2378 * Convenience function to add a list to this container/window.
2379 *
2380 * @param strings list of strings to show
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 enterAction action to perform when an item is selected
2386 * @param moveAction action to perform when the user navigates to a new
2387 * item with arrow/page keys
2388 * @return the new directory list
2389 */
2390 public final TList addList(final List<String> strings, final int x,
2391 final int y, final int width, final int height,
2392 final TAction enterAction, final TAction moveAction) {
2393
2394 return new TList(this, strings, x, y, width, height, enterAction,
2395 moveAction);
2396 }
2397
2398 /**
2399 * Convenience function to add a list to this container/window.
2400 *
2401 * @param strings list of strings to show. This is allowed to be null
2402 * and set later with setList() or by subclasses.
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 enterAction action to perform when an item is selected
2408 * @param moveAction action to perform when the user navigates to a new
2409 * item with arrow/page keys
2410 * @param singleClickAction action to perform when the user clicks on an
2411 * item
2412 */
2413 public TList addList(final List<String> strings, final int x,
2414 final int y, final int width, final int height,
2415 final TAction enterAction, final TAction moveAction,
2416 final TAction singleClickAction) {
2417
2418 return new TList(this, strings, x, y, width, height, enterAction,
2419 moveAction, singleClickAction);
2420 }
2421
2422
2423 /**
2424 * Convenience function to add an image to this container/window.
2425 *
2426 * @param x column relative to parent
2427 * @param y row relative to parent
2428 * @param width number of text cells for width of the image
2429 * @param height number of text cells for height of the image
2430 * @param image the image to display
2431 * @param left left column of the image. 0 is the left-most column.
2432 * @param top top row of the image. 0 is the top-most row.
2433 */
2434 public final TImage addImage(final int x, final int y,
2435 final int width, final int height,
2436 final BufferedImage image, final int left, final int top) {
2437
2438 return new TImage(this, x, y, width, height, image, left, top);
2439 }
2440
2441 /**
2442 * Convenience function to add an image to this container/window.
2443 *
2444 * @param x column relative to parent
2445 * @param y row relative to parent
2446 * @param width number of text cells for width of the image
2447 * @param height number of text cells for height of the image
2448 * @param image the image to display
2449 * @param left left column of the image. 0 is the left-most column.
2450 * @param top top row of the image. 0 is the top-most row.
2451 * @param clickAction function to call when mouse is pressed
2452 */
2453 public final TImage addImage(final int x, final int y,
2454 final int width, final int height,
2455 final BufferedImage image, final int left, final int top,
2456 final TAction clickAction) {
2457
2458 return new TImage(this, x, y, width, height, image, left, top,
2459 clickAction);
2460 }
2461
2462 /**
2463 * Convenience function to add an editable 2D data table to this
2464 * container/window.
2465 *
2466 * @param x column relative to parent
2467 * @param y row relative to parent
2468 * @param width width of widget
2469 * @param height height of widget
2470 */
2471 public TTableWidget addTable(final int x, final int y, final int width,
2472 final int height) {
2473
2474 return new TTableWidget(this, x, y, width, height);
2475 }
2476
2477 /**
2478 * Convenience function to add an editable 2D data table to this
2479 * container/window.
2480 *
2481 * @param x column relative to parent
2482 * @param y row relative to parent
2483 * @param width width of widget
2484 * @param height height of widget
2485 * @param gridColumns number of columns in grid
2486 * @param gridRows number of rows in grid
2487 */
2488 public TTableWidget addTable(final int x, final int y, final int width,
2489 final int height, final int gridColumns, final int gridRows) {
2490
2491 return new TTableWidget(this, x, y, width, height, gridColumns,
2492 gridRows);
2493 }
2494
2495 /**
2496 * Convenience function to add a panel to this container/window.
2497 *
2498 * @param x column relative to parent
2499 * @param y row relative to parent
2500 * @param width width of text area
2501 * @param height height of text area
2502 * @return the new panel
2503 */
2504 public final TPanel addPanel(final int x, final int y, final int width,
2505 final int height) {
2506
2507 return new TPanel(this, x, y, width, height);
2508 }
2509
2510 }