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