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