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