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