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