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