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