telnet daemon working
[fanfix.git] / src / jexer / TWidget.java
CommitLineData
48e27807
KL
1/**
2 * Jexer - Java Text User Interface
3 *
4 * License: LGPLv3 or later
5 *
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
9 *
10 * Copyright (C) 2015 Kevin Lamonte
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * 02110-1301 USA
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
30 */
31package jexer;
32
33import java.util.List;
34import java.util.LinkedList;
35
928811d8 36import jexer.bits.ColorTheme;
48e27807
KL
37import jexer.event.TCommandEvent;
38import jexer.event.TInputEvent;
39import jexer.event.TKeypressEvent;
40import jexer.event.TMenuEvent;
41import jexer.event.TMouseEvent;
42import jexer.event.TResizeEvent;
43import jexer.io.Screen;
928811d8 44import jexer.menu.TMenu;
48e27807
KL
45import static jexer.TKeypress.*;
46
47/**
48 * TWidget is the base class of all objects that can be drawn on screen or
49 * handle user input events.
50 */
2b9c27db 51public abstract class TWidget implements Comparable<TWidget> {
48e27807
KL
52
53 /**
54 * Every widget has a parent widget that it may be "contained" in. For
55 * example, a TWindow might contain several TTextFields, or a TComboBox
56 * may contain a TScrollBar.
57 */
fca67db0
KL
58 private TWidget parent = null;
59
928811d8
KL
60 /**
61 * Get parent widget.
62 *
63 * @return parent widget
64 */
65 public final TWidget getParent() {
66 return parent;
67 }
68
fca67db0
KL
69 /**
70 * Backdoor access for TWindow's constructor. ONLY TWindow USES THIS.
71 *
72 * @param window the top-level window
73 * @param x column relative to parent
74 * @param y row relative to parent
75 * @param width width of window
76 * @param height height of window
77 */
78 protected final void setupForTWindow(final TWindow window,
79 final int x, final int y, final int width, final int height) {
80
81 this.parent = window;
82 this.window = window;
83 this.x = x;
84 this.y = y;
85 this.width = width;
86 this.height = height;
87 }
48e27807 88
928811d8
KL
89 /**
90 * Get this TWidget's parent TApplication.
91 *
92 * @return the parent TApplication
93 */
94 public TApplication getApplication() {
95 return window.getApplication();
96 }
97
98 /**
99 * Get the Screen.
100 *
101 * @return the Screen
102 */
103 public Screen getScreen() {
104 return window.getScreen();
105 }
106
48e27807
KL
107 /**
108 * Child widgets that this widget contains.
109 */
110 private List<TWidget> children;
111
928811d8
KL
112 /**
113 * Get the list of child widgets that this widget contains.
114 *
115 * @return the list of child widgets
116 */
117 public List<TWidget> getChildren() {
118 return children;
119 }
120
48e27807
KL
121 /**
122 * The currently active child widget that will receive keypress events.
123 */
124 private TWidget activeChild = null;
125
126 /**
127 * If true, this widget will receive events.
128 */
fca67db0
KL
129 private boolean active = false;
130
131 /**
132 * Get active flag.
133 *
134 * @return if true, this widget will receive events
135 */
7c870d89 136 public final boolean isActive() {
fca67db0
KL
137 return active;
138 }
139
140 /**
141 * Set active flag.
142 *
143 * @param active if true, this widget will receive events
144 */
928811d8 145 public final void setActive(final boolean active) {
fca67db0
KL
146 this.active = active;
147 }
48e27807
KL
148
149 /**
150 * The window that this widget draws to.
151 */
fca67db0 152 private TWindow window = null;
48e27807 153
30d336cc
KL
154 /**
155 * Get the window this widget is on.
156 *
157 * @return the window
158 */
159 public final TWindow getWindow() {
160 return window;
161 }
162
48e27807
KL
163 /**
164 * Absolute X position of the top-left corner.
165 */
fca67db0
KL
166 private int x = 0;
167
168 /**
169 * Get X position.
170 *
171 * @return absolute X position of the top-left corner
172 */
173 public final int getX() {
174 return x;
175 }
176
177 /**
178 * Set X position.
179 *
180 * @param x absolute X position of the top-left corner
181 */
182 public final void setX(final int x) {
183 this.x = x;
184 }
48e27807
KL
185
186 /**
187 * Absolute Y position of the top-left corner.
188 */
fca67db0
KL
189 private int y = 0;
190
191 /**
192 * Get Y position.
193 *
194 * @return absolute Y position of the top-left corner
195 */
196 public final int getY() {
197 return y;
198 }
199
200 /**
201 * Set Y position.
202 *
203 * @param y absolute Y position of the top-left corner
204 */
205 public final void setY(final int y) {
206 this.y = y;
207 }
48e27807
KL
208
209 /**
210 * Width.
211 */
fca67db0
KL
212 private int width = 0;
213
214 /**
215 * Get the width.
216 *
217 * @return widget width
218 */
219 public final int getWidth() {
220 return this.width;
221 }
222
223 /**
224 * Change the width.
225 *
226 * @param width new widget width
227 */
228 public final void setWidth(final int width) {
229 this.width = width;
230 }
48e27807
KL
231
232 /**
233 * Height.
234 */
fca67db0
KL
235 private int height = 0;
236
237 /**
238 * Get the height.
239 *
240 * @return widget height
241 */
242 public final int getHeight() {
243 return this.height;
244 }
245
246 /**
247 * Change the height.
248 *
249 * @param height new widget height
250 */
251 public final void setHeight(final int height) {
252 this.height = height;
253 }
48e27807
KL
254
255 /**
256 * My tab order inside a window or containing widget.
257 */
258 private int tabOrder = 0;
259
260 /**
261 * If true, this widget can be tabbed to or receive events.
262 */
263 private boolean enabled = true;
264
265 /**
266 * Get enabled flag.
267 *
268 * @return if true, this widget can be tabbed to or receive events
269 */
7c870d89 270 public final boolean isEnabled() {
48e27807
KL
271 return enabled;
272 }
273
274 /**
275 * Set enabled flag.
276 *
277 * @param enabled if true, this widget can be tabbed to or receive events
278 */
279 public final void setEnabled(final boolean enabled) {
280 this.enabled = enabled;
fca67db0 281 if (!enabled) {
48e27807
KL
282 active = false;
283 // See if there are any active siblings to switch to
284 boolean foundSibling = false;
fca67db0
KL
285 if (parent != null) {
286 for (TWidget w: parent.children) {
287 if ((w.enabled)
288 && !(this instanceof THScroller)
289 && !(this instanceof TVScroller)
48e27807
KL
290 ) {
291 parent.activate(w);
292 foundSibling = true;
293 break;
294 }
295 }
296 if (!foundSibling) {
297 parent.activeChild = null;
298 }
299 }
300 }
48e27807
KL
301 }
302
303 /**
304 * If true, this widget has a cursor.
305 */
7c870d89 306 private boolean cursorVisible = false;
48e27807 307
7272e49f
KL
308 /**
309 * Set visible cursor flag.
310 *
7c870d89 311 * @param cursorVisible if true, this widget has a cursor
7272e49f 312 */
7c870d89
KL
313 public final void setCursorVisible(final boolean cursorVisible) {
314 this.cursorVisible = cursorVisible;
7272e49f
KL
315 }
316
a06459bd
KL
317 /**
318 * See if this widget has a visible cursor.
319 *
320 * @return if true, this widget has a visible cursor
321 */
7c870d89
KL
322 public final boolean isCursorVisible() {
323 return cursorVisible;
a06459bd
KL
324 }
325
48e27807
KL
326 /**
327 * Cursor column position in relative coordinates.
328 */
329 private int cursorX = 0;
330
7272e49f
KL
331 /**
332 * Get cursor X value.
333 *
334 * @return cursor column position in relative coordinates
335 */
336 public final int getCursorX() {
337 return cursorX;
338 }
339
340 /**
341 * Set cursor X value.
342 *
343 * @param cursorX column position in relative coordinates
344 */
345 public final void setCursorX(final int cursorX) {
346 this.cursorX = cursorX;
347 }
348
48e27807
KL
349 /**
350 * Cursor row position in relative coordinates.
351 */
352 private int cursorY = 0;
353
7272e49f
KL
354 /**
355 * Get cursor Y value.
356 *
357 * @return cursor row position in relative coordinates
358 */
359 public final int getCursorY() {
360 return cursorY;
361 }
362
363 /**
364 * Set cursor Y value.
365 *
366 * @param cursorY row position in relative coordinates
367 */
368 public final void setCursorY(final int cursorY) {
369 this.cursorY = cursorY;
370 }
371
48e27807 372 /**
2b9c27db 373 * Comparison operator sorts on tabOrder for TWidgets and z for TWindows.
48e27807 374 *
2b9c27db
KL
375 * @param that another TWidget or TWindow instance
376 * @return difference between this.tabOrder and that.tabOrder, or
377 * difference between this.z and that.z
378 */
379 @Override
380 public final int compareTo(final TWidget that) {
381 if ((this instanceof TWindow)
7272e49f 382 && (that instanceof TWindow)
2b9c27db
KL
383 ) {
384 return (((TWindow) this).getZ() - ((TWindow) that).getZ());
385 }
48e27807
KL
386 return (this.tabOrder - that.tabOrder);
387 }
388
389 /**
390 * See if this widget should render with the active color.
391 *
392 * @return true if this widget is active and all of its parents are
393 * active.
394 */
7c870d89 395 public final boolean isAbsoluteActive() {
48e27807
KL
396 if (parent == this) {
397 return active;
398 }
7c870d89 399 return (active && parent.isAbsoluteActive());
48e27807
KL
400 }
401
402 /**
403 * Returns the cursor X position.
404 *
405 * @return absolute screen column number for the cursor's X position
406 */
407 public final int getCursorAbsoluteX() {
7c870d89 408 assert (cursorVisible);
48e27807
KL
409 return getAbsoluteX() + cursorX;
410 }
411
412 /**
413 * Returns the cursor Y position.
414 *
415 * @return absolute screen row number for the cursor's Y position
416 */
417 public final int getCursorAbsoluteY() {
7c870d89 418 assert (cursorVisible);
48e27807
KL
419 return getAbsoluteY() + cursorY;
420 }
421
422 /**
423 * Compute my absolute X position as the sum of my X plus all my parent's
424 * X's.
425 *
426 * @return absolute screen column number for my X position
427 */
428 public final int getAbsoluteX() {
429 assert (parent != null);
430 if (parent == this) {
431 return x;
432 }
433 if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
434 // Widgets on a TWindow have (0,0) as their top-left, but this is
435 // actually the TWindow's (1,1).
436 return parent.getAbsoluteX() + x + 1;
437 }
438 return parent.getAbsoluteX() + x;
439 }
440
441 /**
442 * Compute my absolute Y position as the sum of my Y plus all my parent's
443 * Y's.
444 *
445 * @return absolute screen row number for my Y position
446 */
447 public final int getAbsoluteY() {
448 assert (parent != null);
449 if (parent == this) {
450 return y;
451 }
452 if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
453 // Widgets on a TWindow have (0,0) as their top-left, but this is
454 // actually the TWindow's (1,1).
455 return parent.getAbsoluteY() + y + 1;
456 }
457 return parent.getAbsoluteY() + y;
458 }
459
928811d8
KL
460 /**
461 * Get the global color theme.
462 *
463 * @return the ColorTheme
464 */
465 public final ColorTheme getTheme() {
466 return window.getApplication().getTheme();
467 }
468
48e27807
KL
469 /**
470 * Draw my specific widget. When called, the screen rectangle I draw
471 * into is already setup (offset and clipping).
472 */
473 public void draw() {
474 // Default widget draws nothing.
475 }
476
477 /**
478 * Called by parent to render to TWindow.
479 */
480 public final void drawChildren() {
481 // Set my clipping rectangle
482 assert (window != null);
92554d64
KL
483 assert (getScreen() != null);
484 Screen screen = getScreen();
48e27807
KL
485
486 screen.setClipRight(width);
487 screen.setClipBottom(height);
488
92554d64
KL
489 int absoluteRightEdge = window.getAbsoluteX() + window.getWidth();
490 int absoluteBottomEdge = window.getAbsoluteY() + window.getHeight();
48e27807
KL
491 if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
492 absoluteRightEdge -= 1;
493 }
494 if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
495 absoluteBottomEdge -= 1;
496 }
497 int myRightEdge = getAbsoluteX() + width;
498 int myBottomEdge = getAbsoluteY() + height;
499 if (getAbsoluteX() > absoluteRightEdge) {
500 // I am offscreen
501 screen.setClipRight(0);
502 } else if (myRightEdge > absoluteRightEdge) {
503 screen.setClipRight(screen.getClipRight()
92554d64 504 - (myRightEdge - absoluteRightEdge));
48e27807
KL
505 }
506 if (getAbsoluteY() > absoluteBottomEdge) {
507 // I am offscreen
508 screen.setClipBottom(0);
509 } else if (myBottomEdge > absoluteBottomEdge) {
510 screen.setClipBottom(screen.getClipBottom()
92554d64 511 - (myBottomEdge - absoluteBottomEdge));
48e27807
KL
512 }
513
514 // Set my offset
515 screen.setOffsetX(getAbsoluteX());
516 screen.setOffsetY(getAbsoluteY());
517
518 // Draw me
519 draw();
520
521 // Continue down the chain
522 for (TWidget widget: children) {
523 widget.drawChildren();
524 }
525 }
526
527 /**
fca67db0 528 * Default constructor for subclasses.
48e27807
KL
529 */
530 protected TWidget() {
531 children = new LinkedList<TWidget>();
532 }
533
534 /**
535 * Protected constructor.
536 *
537 * @param parent parent widget
538 */
539 protected TWidget(final TWidget parent) {
30d336cc
KL
540 this(parent, true);
541 }
542
a83fea2b
KL
543 /**
544 * Protected constructor.
545 *
546 * @param parent parent widget
547 * @param x column relative to parent
548 * @param y row relative to parent
549 * @param width width of widget
550 * @param height height of widget
551 */
552 protected TWidget(final TWidget parent, final int x, final int y,
553 final int width, final int height) {
554
555 this(parent, true, x, y, width, height);
556 }
557
30d336cc
KL
558 /**
559 * Protected constructor used by subclasses that are disabled by default.
560 *
561 * @param parent parent widget
562 * @param enabled if true assume enabled
563 */
564 protected TWidget(final TWidget parent, final boolean enabled) {
565 this.enabled = enabled;
48e27807
KL
566 this.parent = parent;
567 this.window = parent.window;
fca67db0 568 children = new LinkedList<TWidget>();
48e27807
KL
569 parent.addChild(this);
570 }
571
a83fea2b
KL
572 /**
573 * Protected constructor used by subclasses that are disabled by default.
574 *
575 * @param parent parent widget
576 * @param enabled if true assume enabled
577 * @param x column relative to parent
578 * @param y row relative to parent
579 * @param width width of widget
580 * @param height height of widget
581 */
582 protected TWidget(final TWidget parent, final boolean enabled,
583 final int x, final int y, final int width, final int height) {
584
585 this.enabled = enabled;
586 this.parent = parent;
587 this.window = parent.window;
588 children = new LinkedList<TWidget>();
589 parent.addChild(this);
590
591 this.x = x;
592 this.y = y;
593 this.width = width;
594 this.height = height;
595 }
596
48e27807
KL
597 /**
598 * Add a child widget to my list of children. We set its tabOrder to 0
599 * and increment the tabOrder of all other children.
600 *
601 * @param child TWidget to add
602 */
603 private void addChild(final TWidget child) {
604 children.add(child);
605
606 if ((child.enabled)
607 && !(child instanceof THScroller)
608 && !(child instanceof TVScroller)
609 ) {
610 for (TWidget widget: children) {
611 widget.active = false;
612 }
613 child.active = true;
614 activeChild = child;
615 }
616 for (int i = 0; i < children.size(); i++) {
617 children.get(i).tabOrder = i;
618 }
619 }
620
621 /**
622 * Switch the active child.
623 *
624 * @param child TWidget to activate
625 */
626 public final void activate(final TWidget child) {
627 assert (child.enabled);
628 if ((child instanceof THScroller)
629 || (child instanceof TVScroller)
630 ) {
631 return;
632 }
633
634 if (child != activeChild) {
635 if (activeChild != null) {
636 activeChild.active = false;
637 }
638 child.active = true;
639 activeChild = child;
640 }
641 }
642
643 /**
644 * Switch the active child.
645 *
646 * @param tabOrder tabOrder of the child to activate. If that child
647 * isn't enabled, then the next enabled child will be activated.
648 */
649 public final void activate(final int tabOrder) {
650 if (activeChild == null) {
651 return;
652 }
653 TWidget child = null;
654 for (TWidget widget: children) {
655 if ((widget.enabled)
656 && !(widget instanceof THScroller)
657 && !(widget instanceof TVScroller)
658 && (widget.tabOrder >= tabOrder)
659 ) {
660 child = widget;
661 break;
662 }
663 }
664 if ((child != null) && (child != activeChild)) {
665 activeChild.active = false;
666 assert (child.enabled);
667 child.active = true;
668 activeChild = child;
669 }
670 }
671
672 /**
673 * Switch the active widget with the next in the tab order.
674 *
675 * @param forward if true, then switch to the next enabled widget in the
676 * list, otherwise switch to the previous enabled widget in the list
677 */
678 public final void switchWidget(final boolean forward) {
679
680 // Only switch if there are multiple enabled widgets
681 if ((children.size() < 2) || (activeChild == null)) {
682 return;
683 }
684
685 int tabOrder = activeChild.tabOrder;
686 do {
687 if (forward) {
688 tabOrder++;
689 } else {
690 tabOrder--;
691 }
692 if (tabOrder < 0) {
693
694 // If at the end, pass the switch to my parent.
695 if ((!forward) && (parent != this)) {
696 parent.switchWidget(forward);
697 return;
698 }
699
700 tabOrder = children.size() - 1;
701 } else if (tabOrder == children.size()) {
702 // If at the end, pass the switch to my parent.
703 if ((forward) && (parent != this)) {
704 parent.switchWidget(forward);
705 return;
706 }
707
708 tabOrder = 0;
709 }
710 if (activeChild.tabOrder == tabOrder) {
711 // We wrapped around
712 break;
713 }
714 } while ((!children.get(tabOrder).enabled)
715 && !(children.get(tabOrder) instanceof THScroller)
716 && !(children.get(tabOrder) instanceof TVScroller));
717
718 assert (children.get(tabOrder).enabled);
719
720 activeChild.active = false;
721 children.get(tabOrder).active = true;
722 activeChild = children.get(tabOrder);
48e27807
KL
723 }
724
725 /**
726 * Returns my active widget.
727 *
728 * @return widget that is active, or this if no children
729 */
928811d8 730 public TWidget getActiveChild() {
48e27807
KL
731 if ((this instanceof THScroller)
732 || (this instanceof TVScroller)
733 ) {
734 return parent;
735 }
736
737 for (TWidget widget: children) {
738 if (widget.active) {
739 return widget.getActiveChild();
740 }
741 }
742 // No active children, return me
743 return this;
744 }
745
746 /**
747 * Method that subclasses can override to handle keystrokes.
748 *
749 * @param keypress keystroke event
750 */
751 public void onKeypress(final TKeypressEvent keypress) {
752
753 if ((children.size() == 0)
fca67db0 754 // TODO
48e27807 755 // || (cast(TTreeView)this)
a83fea2b 756 || (this instanceof TText)
48e27807
KL
757 ) {
758
759 // Defaults:
760 // tab / shift-tab - switch to next/previous widget
761 // right-arrow or down-arrow: same as tab
762 // left-arrow or up-arrow: same as shift-tab
763 if ((keypress.equals(kbTab))
764 || (keypress.equals(kbRight))
765 || (keypress.equals(kbDown))
766 ) {
767 parent.switchWidget(true);
768 return;
769 } else if ((keypress.equals(kbShiftTab))
770 || (keypress.equals(kbBackTab))
771 || (keypress.equals(kbLeft))
772 || (keypress.equals(kbUp))
773 ) {
774 parent.switchWidget(false);
775 return;
776 }
777 }
778
779 // If I have any buttons on me AND this is an Alt-key that matches
780 // its mnemonic, send it an Enter keystroke
781 for (TWidget widget: children) {
92554d64
KL
782 if (widget instanceof TButton) {
783 TButton button = (TButton) widget;
7c870d89
KL
784 if (button.isEnabled()
785 && !keypress.getKey().isFnKey()
786 && keypress.getKey().isAlt()
787 && !keypress.getKey().isCtrl()
92554d64 788 && (Character.toLowerCase(button.getMnemonic().getShortcut())
7c870d89 789 == Character.toLowerCase(keypress.getKey().getChar()))
92554d64
KL
790 ) {
791
792 widget.handleEvent(new TKeypressEvent(kbEnter));
48e27807
KL
793 return;
794 }
795 }
48e27807
KL
796 }
797
798 // Dispatch the keypress to an active widget
799 for (TWidget widget: children) {
800 if (widget.active) {
48e27807
KL
801 widget.handleEvent(keypress);
802 return;
803 }
804 }
805 }
806
807 /**
808 * Method that subclasses can override to handle mouse button presses.
809 *
810 * @param mouse mouse button event
811 */
812 public void onMouseDown(final TMouseEvent mouse) {
813 // Default: do nothing, pass to children instead
814 for (TWidget widget: children) {
815 if (widget.mouseWouldHit(mouse)) {
816 // Dispatch to this child, also activate it
817 activate(widget);
818
819 // Set x and y relative to the child's coordinates
820 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
821 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
822 widget.handleEvent(mouse);
823 return;
824 }
825 }
826 }
827
828 /**
829 * Method that subclasses can override to handle mouse button releases.
830 *
831 * @param mouse mouse button event
832 */
833 public void onMouseUp(final TMouseEvent mouse) {
834 // Default: do nothing, pass to children instead
835 for (TWidget widget: children) {
836 if (widget.mouseWouldHit(mouse)) {
837 // Dispatch to this child, also activate it
838 activate(widget);
839
840 // Set x and y relative to the child's coordinates
841 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
842 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
843 widget.handleEvent(mouse);
844 return;
845 }
846 }
847 }
848
849 /**
850 * Method that subclasses can override to handle mouse movements.
851 *
852 * @param mouse mouse motion event
853 */
854 public void onMouseMotion(final TMouseEvent mouse) {
855 // Default: do nothing, pass it on to ALL of my children. This way
856 // the children can see the mouse "leaving" their area.
857 for (TWidget widget: children) {
858 // Set x and y relative to the child's coordinates
859 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
860 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
861 widget.handleEvent(mouse);
862 }
863 }
864
865 /**
866 * Method that subclasses can override to handle window/screen resize
867 * events.
868 *
869 * @param resize resize event
870 */
871 public void onResize(final TResizeEvent resize) {
872 // Default: do nothing, pass to children instead
873 for (TWidget widget: children) {
874 widget.onResize(resize);
875 }
876 }
877
878 /**
879 * Method that subclasses can override to handle posted command events.
880 *
881 * @param command command event
882 */
883 public void onCommand(final TCommandEvent command) {
884 // Default: do nothing, pass to children instead
885 for (TWidget widget: children) {
886 widget.onCommand(command);
887 }
888 }
889
890 /**
891 * Method that subclasses can override to handle menu or posted menu
892 * events.
893 *
894 * @param menu menu event
895 */
896 public void onMenu(final TMenuEvent menu) {
897 // Default: do nothing, pass to children instead
898 for (TWidget widget: children) {
899 widget.onMenu(menu);
900 }
901 }
902
903 /**
904 * Method that subclasses can override to do processing when the UI is
905 * idle.
906 */
907 public void onIdle() {
908 // Default: do nothing, pass to children instead
909 for (TWidget widget: children) {
910 widget.onIdle();
911 }
912 }
913
914 /**
915 * Consume event. Subclasses that want to intercept all events in one go
916 * can override this method.
917 *
918 * @param event keyboard, mouse, resize, command, or menu event
919 */
920 public void handleEvent(final TInputEvent event) {
921 // System.err.printf("TWidget (%s) event: %s\n", this.getClass().getName(),
922 // event);
923
924 if (!enabled) {
925 // Discard event
926 // System.err.println(" -- discard --");
927 return;
928 }
929
930 if (event instanceof TKeypressEvent) {
931 onKeypress((TKeypressEvent) event);
932 } else if (event instanceof TMouseEvent) {
933
934 TMouseEvent mouse = (TMouseEvent) event;
935
936 switch (mouse.getType()) {
937
938 case MOUSE_DOWN:
939 onMouseDown(mouse);
940 break;
941
942 case MOUSE_UP:
943 onMouseUp(mouse);
944 break;
945
946 case MOUSE_MOTION:
947 onMouseMotion(mouse);
948 break;
949
950 default:
951 throw new IllegalArgumentException("Invalid mouse event type: "
952 + mouse.getType());
953 }
954 } else if (event instanceof TResizeEvent) {
955 onResize((TResizeEvent) event);
956 } else if (event instanceof TCommandEvent) {
957 onCommand((TCommandEvent) event);
958 } else if (event instanceof TMenuEvent) {
959 onMenu((TMenuEvent) event);
960 }
961
962 // Do nothing else
963 return;
964 }
965
966 /**
967 * Check if a mouse press/release event coordinate is contained in this
968 * widget.
969 *
970 * @param mouse a mouse-based event
971 * @return whether or not a mouse click would be sent to this widget
972 */
973 public final boolean mouseWouldHit(final TMouseEvent mouse) {
974
975 if (!enabled) {
976 return false;
977 }
978
979 if ((mouse.getAbsoluteX() >= getAbsoluteX())
980 && (mouse.getAbsoluteX() < getAbsoluteX() + width)
981 && (mouse.getAbsoluteY() >= getAbsoluteY())
982 && (mouse.getAbsoluteY() < getAbsoluteY() + height)
983 ) {
984 return true;
985 }
986 return false;
987 }
988
30d336cc
KL
989 /**
990 * Convenience function to add a label to this container/window.
991 *
992 * @param text label
993 * @param x column relative to parent
994 * @param y row relative to parent
995 * @return the new label
996 */
997 public final TLabel addLabel(final String text, final int x, final int y) {
998 return addLabel(text, x, y, "tlabel");
999 }
1000
1001 /**
1002 * Convenience function to add a label to this container/window.
1003 *
1004 * @param text label
1005 * @param x column relative to parent
1006 * @param y row relative to parent
1007 * @param colorKey ColorTheme key color to use for foreground text.
1008 * Default is "tlabel"
1009 * @return the new label
1010 */
1011 public final TLabel addLabel(final String text, final int x, final int y,
1012 final String colorKey) {
1013
1014 return new TLabel(this, text, x, y, colorKey);
1015 }
1016
1017 /**
1018 * Convenience function to add a button to this container/window.
1019 *
1020 * @param text label on the button
1021 * @param x column relative to parent
1022 * @param y row relative to parent
1023 * @param action to call when button is pressed
1024 * @return the new button
1025 */
1026 public final TButton addButton(final String text, final int x, final int y,
1027 final TAction action) {
1028
1029 return new TButton(this, text, x, y, action);
1030 }
1031
7272e49f
KL
1032 /**
1033 * Convenience function to add a checkbox to this container/window.
1034 *
1035 * @param x column relative to parent
1036 * @param y row relative to parent
1037 * @param label label to display next to (right of) the checkbox
1038 * @param checked initial check state
1039 * @return the new checkbox
1040 */
1041 public final TCheckbox addCheckbox(final int x, final int y,
1042 final String label, final boolean checked) {
1043
1044 return new TCheckbox(this, x, y, label, checked);
1045 }
30d336cc 1046
d502a0e9
KL
1047 /**
1048 * Convenience function to add a progress bar to this container/window.
1049 *
1050 * @param x column relative to parent
1051 * @param y row relative to parent
1052 * @param width width of progress bar
1053 * @param value initial value of percent complete
00d2622b 1054 * @return the new progress bar
d502a0e9
KL
1055 */
1056 public final TProgressBar addProgressBar(final int x, final int y,
1057 final int width, final int value) {
1058
1059 return new TProgressBar(this, x, y, width, value);
1060 }
1061
00d2622b
KL
1062 /**
1063 * Convenience function to add a radio button group to this
1064 * container/window.
1065 *
1066 * @param x column relative to parent
1067 * @param y row relative to parent
1068 * @param label label to display on the group box
1069 * @return the new radio button group
1070 */
1071 public final TRadioGroup addRadioGroup(final int x, final int y,
1072 final String label) {
1073
1074 return new TRadioGroup(this, x, y, label);
1075 }
1076
128e5be1
KL
1077 /**
1078 * Convenience function to add a text field to this container/window.
1079 *
1080 * @param x column relative to parent
1081 * @param y row relative to parent
1082 * @param width visible text width
1083 * @param fixed if true, the text cannot exceed the display width
1084 * @return the new text field
1085 */
1086 public final TField addField(final int x, final int y,
1087 final int width, final boolean fixed) {
1088
1089 return new TField(this, x, y, width, fixed);
1090 }
1091
1092 /**
1093 * Convenience function to add a text field to this container/window.
1094 *
1095 * @param x column relative to parent
1096 * @param y row relative to parent
1097 * @param width visible text width
1098 * @param fixed if true, the text cannot exceed the display width
1099 * @param text initial text, default is empty string
1100 * @return the new text field
1101 */
1102 public final TField addField(final int x, final int y,
1103 final int width, final boolean fixed, final String text) {
1104
1105 return new TField(this, x, y, width, fixed, text);
1106 }
1107
1108 /**
1109 * Convenience function to add a text field to this container/window.
1110 *
1111 * @param x column relative to parent
1112 * @param y row relative to parent
1113 * @param width visible text width
1114 * @param fixed if true, the text cannot exceed the display width
1115 * @param text initial text, default is empty string
1116 * @param enterAction function to call when enter key is pressed
1117 * @param updateAction function to call when the text is updated
1118 * @return the new text field
1119 */
1120 public final TField addField(final int x, final int y,
1121 final int width, final boolean fixed, final String text,
1122 final TAction enterAction, final TAction updateAction) {
1123
1124 return new TField(this, x, y, width, fixed, text, enterAction,
1125 updateAction);
1126 }
00d2622b 1127
cc99cba8
KL
1128 /**
1129 * Convenience function to add a scrollable text box to this
1130 * container/window.
1131 *
1132 * @param text text on the screen
1133 * @param x column relative to parent
1134 * @param y row relative to parent
1135 * @param width width of text area
1136 * @param height height of text area
1137 * @param colorKey ColorTheme key color to use for foreground text
1138 * @return the new text box
1139 */
c6940ed9 1140 public final TText addText(final String text, final int x,
cc99cba8
KL
1141 final int y, final int width, final int height, final String colorKey) {
1142
1143 return new TText(this, text, x, y, width, height, colorKey);
1144 }
1145
1146 /**
1147 * Convenience function to add a scrollable text box to this
1148 * container/window.
1149 *
1150 * @param text text on the screen
1151 * @param x column relative to parent
1152 * @param y row relative to parent
1153 * @param width width of text area
1154 * @param height height of text area
1155 * @return the new text box
1156 */
c6940ed9 1157 public final TText addText(final String text, final int x, final int y,
cc99cba8
KL
1158 final int width, final int height) {
1159
1160 return new TText(this, text, x, y, width, height, "ttext");
1161 }
1162
c6940ed9
KL
1163 /**
1164 * Convenience function to spawn a message box.
1165 *
1166 * @param title window title, will be centered along the top border
1167 * @param caption message to display. Use embedded newlines to get a
1168 * multi-line box.
1169 * @return the new message box
1170 */
1171 public final TMessageBox messageBox(final String title,
1172 final String caption) {
1173
1174 return getApplication().messageBox(title, caption, TMessageBox.Type.OK);
1175 }
1176
1177 /**
1178 * Convenience function to spawn a message box.
1179 *
1180 * @param title window title, will be centered along the top border
1181 * @param caption message to display. Use embedded newlines to get a
1182 * multi-line box.
1183 * @param type one of the TMessageBox.Type constants. Default is
1184 * Type.OK.
1185 * @return the new message box
1186 */
1187 public final TMessageBox messageBox(final String title,
1188 final String caption, final TMessageBox.Type type) {
1189
1190 return getApplication().messageBox(title, caption, type);
1191 }
1192
1193 /**
1194 * Convenience function to spawn an input box.
1195 *
1196 * @param title window title, will be centered along the top border
1197 * @param caption message to display. Use embedded newlines to get a
1198 * multi-line box.
1199 * @return the new input box
1200 */
1201 public final TInputBox inputBox(final String title, final String caption) {
1202
1203 return getApplication().inputBox(title, caption);
1204 }
1205
1206 /**
1207 * Convenience function to spawn an input box.
1208 *
1209 * @param title window title, will be centered along the top border
1210 * @param caption message to display. Use embedded newlines to get a
1211 * multi-line box.
1212 * @param text initial text to seed the field with
1213 * @return the new input box
1214 */
1215 public final TInputBox inputBox(final String title, final String caption,
1216 final String text) {
1217
1218 return getApplication().inputBox(title, caption, text);
1219 }
cc99cba8 1220
87a17f3c
KL
1221 /**
1222 * Convenience function to add a password text field to this
1223 * container/window.
1224 *
1225 * @param x column relative to parent
1226 * @param y row relative to parent
1227 * @param width visible text width
1228 * @param fixed if true, the text cannot exceed the display width
1229 * @return the new text field
1230 */
1231 public final TPasswordField addPasswordField(final int x, final int y,
1232 final int width, final boolean fixed) {
1233
1234 return new TPasswordField(this, x, y, width, fixed);
1235 }
1236
1237 /**
1238 * Convenience function to add a password text field to this
1239 * container/window.
1240 *
1241 * @param x column relative to parent
1242 * @param y row relative to parent
1243 * @param width visible text width
1244 * @param fixed if true, the text cannot exceed the display width
1245 * @param text initial text, default is empty string
1246 * @return the new text field
1247 */
1248 public final TPasswordField addPasswordField(final int x, final int y,
1249 final int width, final boolean fixed, final String text) {
1250
1251 return new TPasswordField(this, x, y, width, fixed, text);
1252 }
1253
1254 /**
1255 * Convenience function to add a password text field to this
1256 * container/window.
1257 *
1258 * @param x column relative to parent
1259 * @param y row relative to parent
1260 * @param width visible text width
1261 * @param fixed if true, the text cannot exceed the display width
1262 * @param text initial text, default is empty string
1263 * @param enterAction function to call when enter key is pressed
1264 * @param updateAction function to call when the text is updated
1265 * @return the new text field
1266 */
1267 public final TPasswordField addPasswordField(final int x, final int y,
1268 final int width, final boolean fixed, final String text,
1269 final TAction enterAction, final TAction updateAction) {
1270
1271 return new TPasswordField(this, x, y, width, fixed, text, enterAction,
1272 updateAction);
1273 }
1274
48e27807 1275}