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