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