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