Fix ClassCastException
[fanfix.git] / src / jexer / TWindow.java
CommitLineData
daa4106c 1/*
48e27807
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
48e27807 5 *
a2018e99 6 * Copyright (C) 2017 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
5dfd1c11
KL
31import java.util.HashSet;
32
42873e30 33import jexer.backend.Screen;
48e27807
KL
34import jexer.bits.Cell;
35import jexer.bits.CellAttributes;
36import jexer.bits.GraphicsChars;
37import jexer.event.TCommandEvent;
38import jexer.event.TKeypressEvent;
39import jexer.event.TMenuEvent;
40import jexer.event.TMouseEvent;
41import jexer.event.TResizeEvent;
928811d8 42import jexer.menu.TMenu;
48e27807
KL
43import static jexer.TCommand.*;
44import static jexer.TKeypress.*;
45
46/**
47 * TWindow is the top-level container and drawing surface for other widgets.
48 */
2b9c27db 49public class TWindow extends TWidget {
48e27807 50
2ce6dab2
KL
51 // ------------------------------------------------------------------------
52 // Public constants -------------------------------------------------------
53 // ------------------------------------------------------------------------
54
48e27807 55 /**
2ce6dab2 56 * Window is resizable (default yes).
48e27807 57 */
2ce6dab2 58 public static final int RESIZABLE = 0x01;
48e27807
KL
59
60 /**
2ce6dab2 61 * Window is modal (default no).
48e27807 62 */
2ce6dab2 63 public static final int MODAL = 0x02;
48e27807
KL
64
65 /**
2ce6dab2 66 * Window is centered (default no).
48e27807 67 */
2ce6dab2
KL
68 public static final int CENTERED = 0x04;
69
78a56d5d
KL
70 /**
71 * Window has no close box (default no). Window can still be closed via
72 * TApplication.closeWindow() and TWindow.close().
73 */
74 public static final int NOCLOSEBOX = 0x08;
75
2ce6dab2
KL
76 // ------------------------------------------------------------------------
77 // Common window attributes -----------------------------------------------
78 // ------------------------------------------------------------------------
79
80 /**
81 * Window flags. Note package private access.
82 */
83 int flags = RESIZABLE;
48e27807
KL
84
85 /**
86 * Window title.
87 */
fca67db0
KL
88 private String title = "";
89
90 /**
91 * Get window title.
92 *
93 * @return window title
94 */
95 public final String getTitle() {
96 return title;
97 }
98
99 /**
100 * Set window title.
101 *
102 * @param title new window title
103 */
104 public final void setTitle(final String title) {
105 this.title = title;
106 }
48e27807 107
2ce6dab2
KL
108 // ------------------------------------------------------------------------
109 // TApplication integration -----------------------------------------------
110 // ------------------------------------------------------------------------
48e27807
KL
111
112 /**
2ce6dab2 113 * Window's parent TApplication.
48e27807 114 */
2ce6dab2 115 private TApplication application;
48e27807
KL
116
117 /**
2ce6dab2
KL
118 * Get this TWindow's parent TApplication.
119 *
120 * @return this TWindow's parent TApplication
48e27807 121 */
2ce6dab2
KL
122 @Override
123 public final TApplication getApplication() {
124 return application;
125 }
48e27807
KL
126
127 /**
2ce6dab2
KL
128 * Get the Screen.
129 *
130 * @return the Screen
48e27807 131 */
2ce6dab2
KL
132 @Override
133 public final Screen getScreen() {
134 return application.getScreen();
135 }
48e27807
KL
136
137 /**
138 * Z order. Lower number means more in-front.
139 */
140 private int z = 0;
141
a06459bd
KL
142 /**
143 * Get Z order. Lower number means more in-front.
144 *
145 * @return Z value. Lower number means more in-front.
146 */
147 public final int getZ() {
148 return z;
149 }
150
151 /**
152 * Set Z order. Lower number means more in-front.
153 *
154 * @param z the new Z value. Lower number means more in-front.
155 */
156 public final void setZ(final int z) {
157 this.z = z;
158 }
159
5dfd1c11
KL
160 /**
161 * Window's keyboard shortcuts. Any key in this set will be passed to
162 * the window directly rather than processed through the menu
163 * accelerators.
164 */
165 private HashSet<TKeypress> keyboardShortcuts = new HashSet<TKeypress>();
166
167 /**
168 * Add a keypress to be overridden for this window.
169 *
170 * @param key the key to start taking control of
171 */
172 protected void addShortcutKeypress(final TKeypress key) {
173 keyboardShortcuts.add(key);
174 }
175
176 /**
177 * Remove a keypress to be overridden for this window.
178 *
179 * @param key the key to stop taking control of
180 */
181 protected void removeShortcutKeypress(final TKeypress key) {
182 keyboardShortcuts.remove(key);
183 }
184
185 /**
186 * Remove all keypresses to be overridden for this window.
187 */
188 protected void clearShortcutKeypresses() {
189 keyboardShortcuts.clear();
190 }
191
192 /**
193 * Determine if a keypress is overridden for this window.
194 *
195 * @param key the key to check
196 * @return true if this window wants to process this key on its own
197 */
198 public boolean isShortcutKeypress(final TKeypress key) {
199 return keyboardShortcuts.contains(key);
200 }
201
2ce6dab2
KL
202 /**
203 * A window may have a status bar associated with it. TApplication will
204 * draw this status bar last, and will also route events to it first
205 * before the window.
206 */
207 protected TStatusBar statusBar = null;
208
209 /**
210 * Get the window's status bar, or null if it does not have one.
211 *
212 * @return the status bar, or null
213 */
214 public TStatusBar getStatusBar() {
215 return statusBar;
216 }
217
218 /**
219 * Set the window's status bar to a new one.
220 *
221 * @param text the status bar text
222 * @return the status bar
223 */
224 public TStatusBar newStatusBar(final String text) {
225 statusBar = new TStatusBar(this, text);
226 return statusBar;
227 }
228
229 // ------------------------------------------------------------------------
230 // Window movement/resizing support ---------------------------------------
231 // ------------------------------------------------------------------------
232
48e27807
KL
233 /**
234 * If true, then the user clicked on the title bar and is moving the
235 * window.
236 */
bd8d51fa 237 protected boolean inWindowMove = false;
48e27807
KL
238
239 /**
240 * If true, then the user clicked on the bottom right corner and is
241 * resizing the window.
242 */
bd8d51fa 243 protected boolean inWindowResize = false;
48e27807
KL
244
245 /**
246 * If true, then the user selected "Size/Move" (or hit Ctrl-F5) and is
247 * resizing/moving the window via the keyboard.
248 */
249 private boolean inKeyboardResize = false;
250
251 /**
252 * If true, this window is maximized.
253 */
254 private boolean maximized = false;
255
256 /**
257 * Remember mouse state.
258 */
928811d8 259 protected TMouseEvent mouse;
48e27807
KL
260
261 // For moving the window. resizing also uses moveWindowMouseX/Y
262 private int moveWindowMouseX;
263 private int moveWindowMouseY;
264 private int oldWindowX;
265 private int oldWindowY;
266
267 // Resizing
268 private int resizeWindowWidth;
269 private int resizeWindowHeight;
270 private int minimumWindowWidth = 10;
271 private int minimumWindowHeight = 2;
272 private int maximumWindowWidth = -1;
273 private int maximumWindowHeight = -1;
274
275 // For maximize/restore
276 private int restoreWindowWidth;
277 private int restoreWindowHeight;
278 private int restoreWindowX;
279 private int restoreWindowY;
280
34a42e78
KL
281 /**
282 * Set the maximum width for this window.
283 *
284 * @param maximumWindowWidth new maximum width
285 */
286 public final void setMaximumWindowWidth(final int maximumWindowWidth) {
71a389c9
KL
287 if ((maximumWindowWidth != -1)
288 && (maximumWindowWidth < minimumWindowWidth + 1)
289 ) {
290 throw new IllegalArgumentException("Maximum window width cannot " +
291 "be smaller than minimum window width + 1");
292 }
34a42e78
KL
293 this.maximumWindowWidth = maximumWindowWidth;
294 }
295
71a389c9
KL
296 /**
297 * Set the minimum width for this window.
298 *
299 * @param minimumWindowWidth new minimum width
300 */
301 public final void setMinimumWindowWidth(final int minimumWindowWidth) {
302 if ((maximumWindowWidth != -1)
303 && (minimumWindowWidth > maximumWindowWidth - 1)
304 ) {
305 throw new IllegalArgumentException("Minimum window width cannot " +
306 "be larger than maximum window width - 1");
307 }
308 this.minimumWindowWidth = minimumWindowWidth;
309 }
310
311 /**
312 * Set the maximum height for this window.
313 *
314 * @param maximumWindowHeight new maximum height
315 */
316 public final void setMaximumWindowHeight(final int maximumWindowHeight) {
317 if ((maximumWindowHeight != -1)
318 && (maximumWindowHeight < minimumWindowHeight + 1)
319 ) {
320 throw new IllegalArgumentException("Maximum window height cannot " +
321 "be smaller than minimum window height + 1");
322 }
323 this.maximumWindowHeight = maximumWindowHeight;
324 }
325
326 /**
327 * Set the minimum height for this window.
328 *
329 * @param minimumWindowHeight new minimum height
330 */
331 public final void setMinimumWindowHeight(final int minimumWindowHeight) {
332 if ((maximumWindowHeight != -1)
333 && (minimumWindowHeight > maximumWindowHeight - 1)
334 ) {
335 throw new IllegalArgumentException("Minimum window height cannot " +
336 "be larger than maximum window height - 1");
337 }
338 this.minimumWindowHeight = minimumWindowHeight;
339 }
340
2ce6dab2
KL
341 /**
342 * Recenter the window on-screen.
343 */
344 public final void center() {
345 if ((flags & CENTERED) != 0) {
346 if (getWidth() < getScreen().getWidth()) {
347 setX((getScreen().getWidth() - getWidth()) / 2);
348 } else {
349 setX(0);
350 }
351 setY(((application.getDesktopBottom()
352 - application.getDesktopTop()) - getHeight()) / 2);
353 if (getY() < 0) {
354 setY(0);
355 }
356 setY(getY() + application.getDesktopTop());
357 }
358 }
359
360 /**
361 * Maximize window.
362 */
92453213
KL
363 public void maximize() {
364 if (maximized) {
365 return;
366 }
367
2ce6dab2
KL
368 restoreWindowWidth = getWidth();
369 restoreWindowHeight = getHeight();
370 restoreWindowX = getX();
371 restoreWindowY = getY();
372 setWidth(getScreen().getWidth());
373 setHeight(application.getDesktopBottom() - 1);
374 setX(0);
375 setY(1);
376 maximized = true;
377 }
378
379 /**
7657ad8c 380 * Restore (unmaximize) window.
2ce6dab2 381 */
92453213
KL
382 public void restore() {
383 if (!maximized) {
384 return;
385 }
386
2ce6dab2
KL
387 setWidth(restoreWindowWidth);
388 setHeight(restoreWindowHeight);
389 setX(restoreWindowX);
390 setY(restoreWindowY);
391 maximized = false;
392 }
393
92453213
KL
394 // ------------------------------------------------------------------------
395 // Window visibility ------------------------------------------------------
396 // ------------------------------------------------------------------------
397
398 /**
399 * Hidden flag. A hidden window will still have its onIdle() called, and
400 * will also have onClose() called at application exit. Note package
401 * private access: TApplication will force hidden false if a modal window
402 * is active.
403 */
404 boolean hidden = false;
405
406 /**
407 * Returns true if this window is hidden.
408 *
409 * @return true if this window is hidden, false if the window is shown
410 */
411 public final boolean isHidden() {
412 return hidden;
413 }
414
415 /**
416 * Returns true if this window is shown.
417 *
418 * @return true if this window is shown, false if the window is hidden
419 */
420 public final boolean isShown() {
421 return !hidden;
422 }
423
424 /**
425 * Hide window. A hidden window will still have its onIdle() called, and
426 * will also have onClose() called at application exit. Hidden windows
427 * will not receive any other events.
428 */
429 public void hide() {
430 application.hideWindow(this);
431 }
432
433 /**
434 * Show window.
435 */
436 public void show() {
437 application.showWindow(this);
438 }
439
440 /**
441 * Activate window (bring to top and receive events).
442 */
443 public void activate() {
444 application.activateWindow(this);
445 }
446
78a56d5d
KL
447 /**
448 * Close window. Note that windows without a close box can still be
449 * closed by calling the close() method.
450 */
451 public void close() {
452 application.closeWindow(this);
453 }
454
2ce6dab2
KL
455 // ------------------------------------------------------------------------
456 // Constructors -----------------------------------------------------------
457 // ------------------------------------------------------------------------
458
48e27807
KL
459 /**
460 * Public constructor. Window will be located at (0, 0).
461 *
462 * @param application TApplication that manages this window
463 * @param title window title, will be centered along the top border
464 * @param width width of window
465 * @param height height of window
466 */
467 public TWindow(final TApplication application, final String title,
468 final int width, final int height) {
469
470 this(application, title, 0, 0, width, height, RESIZABLE);
471 }
472
473 /**
474 * Public constructor. Window will be located at (0, 0).
475 *
476 * @param application TApplication that manages this window
477 * @param title window title, will be centered along the top border
478 * @param width width of window
479 * @param height height of window
480 * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
481 */
482 public TWindow(final TApplication application, final String title,
483 final int width, final int height, final int flags) {
484
485 this(application, title, 0, 0, width, height, flags);
486 }
487
488 /**
489 * Public constructor.
490 *
491 * @param application TApplication that manages this window
492 * @param title window title, will be centered along the top border
493 * @param x column relative to parent
494 * @param y row relative to parent
495 * @param width width of window
496 * @param height height of window
497 */
498 public TWindow(final TApplication application, final String title,
499 final int x, final int y, final int width, final int height) {
500
501 this(application, title, x, y, width, height, RESIZABLE);
502 }
503
504 /**
505 * Public constructor.
506 *
507 * @param application TApplication that manages this window
508 * @param title window title, will be centered along the top border
509 * @param x column relative to parent
510 * @param y row relative to parent
511 * @param width width of window
512 * @param height height of window
513 * @param flags mask of RESIZABLE, CENTERED, or MODAL
514 */
515 public TWindow(final TApplication application, final String title,
516 final int x, final int y, final int width, final int height,
517 final int flags) {
518
fca67db0
KL
519 super();
520
48e27807 521 // I am my own window and parent
fca67db0
KL
522 setupForTWindow(this, x, y + application.getDesktopTop(),
523 width, height);
48e27807
KL
524
525 // Save fields
526 this.title = title;
527 this.application = application;
48e27807
KL
528 this.flags = flags;
529
530 // Minimum width/height are 10 and 2
531 assert (width >= 10);
fca67db0 532 assert (getHeight() >= 2);
48e27807
KL
533
534 // MODAL implies CENTERED
535 if (isModal()) {
536 this.flags |= CENTERED;
537 }
538
539 // Center window if specified
540 center();
541
542 // Add me to the application
543 application.addWindow(this);
544 }
545
2ce6dab2
KL
546 // ------------------------------------------------------------------------
547 // General behavior -------------------------------------------------------
548 // ------------------------------------------------------------------------
48e27807 549
fe0770f9
KL
550 /**
551 * See if this window is undergoing any movement/resize/etc.
552 *
553 * @return true if the window is moving
554 */
555 public boolean inMovements() {
556 if (inWindowResize || inWindowMove || inKeyboardResize) {
557 return true;
558 }
559 return false;
560 }
561
562 /**
563 * Stop any pending movement/resize/etc.
564 */
565 public void stopMovements() {
566 inWindowResize = false;
567 inWindowMove = false;
568 inKeyboardResize = false;
569 }
570
48e27807
KL
571 /**
572 * Returns true if this window is modal.
573 *
574 * @return true if this window is modal
575 */
576 public final boolean isModal() {
577 if ((flags & MODAL) == 0) {
578 return false;
579 }
580 return true;
581 }
582
78a56d5d
KL
583 /**
584 * Returns true if this window has a close box.
585 *
586 * @return true if this window has a close box
587 */
588 public final boolean hasCloseBox() {
589 if ((flags & NOCLOSEBOX) != 0) {
590 return true;
591 }
592 return false;
593 }
594
48e27807
KL
595 /**
596 * Retrieve the background color.
597 *
598 * @return the background color
599 */
30d336cc 600 public final CellAttributes getBackground() {
48e27807
KL
601 if (!isModal()
602 && (inWindowMove || inWindowResize || inKeyboardResize)
603 ) {
7c870d89 604 assert (isActive());
928811d8 605 return getTheme().getColor("twindow.background.windowmove");
48e27807 606 } else if (isModal() && inWindowMove) {
7c870d89 607 assert (isActive());
928811d8 608 return getTheme().getColor("twindow.background.modal");
48e27807 609 } else if (isModal()) {
7c870d89 610 if (isActive()) {
928811d8 611 return getTheme().getColor("twindow.background.modal");
48e27807 612 }
928811d8 613 return getTheme().getColor("twindow.background.modal.inactive");
7c870d89 614 } else if (isActive()) {
48e27807 615 assert (!isModal());
928811d8 616 return getTheme().getColor("twindow.background");
48e27807
KL
617 } else {
618 assert (!isModal());
928811d8 619 return getTheme().getColor("twindow.background.inactive");
48e27807
KL
620 }
621 }
622
623 /**
624 * Retrieve the border color.
625 *
626 * @return the border color
627 */
3649b921 628 public CellAttributes getBorder() {
48e27807
KL
629 if (!isModal()
630 && (inWindowMove || inWindowResize || inKeyboardResize)
631 ) {
7c870d89 632 assert (isActive());
928811d8 633 return getTheme().getColor("twindow.border.windowmove");
48e27807 634 } else if (isModal() && inWindowMove) {
7c870d89 635 assert (isActive());
928811d8 636 return getTheme().getColor("twindow.border.modal.windowmove");
48e27807 637 } else if (isModal()) {
7c870d89 638 if (isActive()) {
928811d8 639 return getTheme().getColor("twindow.border.modal");
48e27807 640 } else {
928811d8 641 return getTheme().getColor("twindow.border.modal.inactive");
48e27807 642 }
7c870d89 643 } else if (isActive()) {
48e27807 644 assert (!isModal());
928811d8 645 return getTheme().getColor("twindow.border");
48e27807
KL
646 } else {
647 assert (!isModal());
928811d8 648 return getTheme().getColor("twindow.border.inactive");
48e27807
KL
649 }
650 }
651
652 /**
653 * Retrieve the border line type.
654 *
655 * @return the border line type
656 */
928811d8 657 private int getBorderType() {
48e27807
KL
658 if (!isModal()
659 && (inWindowMove || inWindowResize || inKeyboardResize)
660 ) {
7c870d89 661 assert (isActive());
48e27807
KL
662 return 1;
663 } else if (isModal() && inWindowMove) {
7c870d89 664 assert (isActive());
48e27807
KL
665 return 1;
666 } else if (isModal()) {
7c870d89 667 if (isActive()) {
48e27807
KL
668 return 2;
669 } else {
670 return 1;
671 }
7c870d89 672 } else if (isActive()) {
48e27807
KL
673 return 2;
674 } else {
675 return 1;
676 }
677 }
678
48e27807
KL
679 /**
680 * Called by TApplication.drawChildren() to render on screen.
681 */
682 @Override
683 public void draw() {
684 // Draw the box and background first.
685 CellAttributes border = getBorder();
686 CellAttributes background = getBackground();
687 int borderType = getBorderType();
688
fca67db0 689 getScreen().drawBox(0, 0, getWidth(), getHeight(), border,
48e27807
KL
690 background, borderType, true);
691
692 // Draw the title
fca67db0 693 int titleLeft = (getWidth() - title.length() - 2) / 2;
48e27807 694 putCharXY(titleLeft, 0, ' ', border);
0d47c546 695 putStringXY(titleLeft + 1, 0, title);
48e27807
KL
696 putCharXY(titleLeft + title.length() + 1, 0, ' ', border);
697
7c870d89 698 if (isActive()) {
48e27807
KL
699
700 // Draw the close button
78a56d5d
KL
701 if ((flags & NOCLOSEBOX) == 0) {
702 putCharXY(2, 0, '[', border);
703 putCharXY(4, 0, ']', border);
704 if (mouseOnClose() && mouse.isMouse1()) {
705 putCharXY(3, 0, GraphicsChars.CP437[0x0F],
706 !isModal()
707 ? getTheme().getColor("twindow.border.windowmove")
708 : getTheme().getColor("twindow.border.modal.windowmove"));
709 } else {
710 putCharXY(3, 0, GraphicsChars.CP437[0xFE],
711 !isModal()
712 ? getTheme().getColor("twindow.border.windowmove")
713 : getTheme().getColor("twindow.border.modal.windowmove"));
714 }
48e27807
KL
715 }
716
717 // Draw the maximize button
718 if (!isModal()) {
719
fca67db0
KL
720 putCharXY(getWidth() - 5, 0, '[', border);
721 putCharXY(getWidth() - 3, 0, ']', border);
7c870d89 722 if (mouseOnMaximize() && mouse.isMouse1()) {
fca67db0 723 putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x0F],
928811d8 724 getTheme().getColor("twindow.border.windowmove"));
48e27807
KL
725 } else {
726 if (maximized) {
fca67db0 727 putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x12],
928811d8 728 getTheme().getColor("twindow.border.windowmove"));
48e27807 729 } else {
fca67db0 730 putCharXY(getWidth() - 4, 0, GraphicsChars.UPARROW,
928811d8 731 getTheme().getColor("twindow.border.windowmove"));
48e27807
KL
732 }
733 }
734
735 // Draw the resize corner
736 if ((flags & RESIZABLE) != 0) {
928811d8
KL
737 putCharXY(getWidth() - 2, getHeight() - 1,
738 GraphicsChars.SINGLE_BAR,
739 getTheme().getColor("twindow.border.windowmove"));
740 putCharXY(getWidth() - 1, getHeight() - 1,
741 GraphicsChars.LRCORNER,
742 getTheme().getColor("twindow.border.windowmove"));
48e27807
KL
743 }
744 }
745 }
746 }
747
2ce6dab2
KL
748 // ------------------------------------------------------------------------
749 // Event handlers ---------------------------------------------------------
750 // ------------------------------------------------------------------------
751
752 /**
753 * Returns true if the mouse is currently on the close button.
754 *
755 * @return true if mouse is currently on the close button
756 */
92453213 757 protected boolean mouseOnClose() {
78a56d5d
KL
758 if ((flags & NOCLOSEBOX) != 0) {
759 return false;
760 }
2ce6dab2
KL
761 if ((mouse != null)
762 && (mouse.getAbsoluteY() == getY())
763 && (mouse.getAbsoluteX() == getX() + 3)
764 ) {
765 return true;
766 }
767 return false;
768 }
769
770 /**
771 * Returns true if the mouse is currently on the maximize/restore button.
772 *
773 * @return true if the mouse is currently on the maximize/restore button
774 */
92453213 775 protected boolean mouseOnMaximize() {
2ce6dab2
KL
776 if ((mouse != null)
777 && !isModal()
778 && (mouse.getAbsoluteY() == getY())
779 && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
780 ) {
781 return true;
782 }
783 return false;
784 }
785
786 /**
787 * Returns true if the mouse is currently on the resizable lower right
788 * corner.
789 *
790 * @return true if the mouse is currently on the resizable lower right
791 * corner
792 */
92453213 793 protected boolean mouseOnResize() {
2ce6dab2
KL
794 if (((flags & RESIZABLE) != 0)
795 && !isModal()
796 && (mouse != null)
797 && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
798 && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
799 || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
800 ) {
801 return true;
802 }
803 return false;
804 }
805
806 /**
807 * Subclasses should override this method to cleanup resources. This is
808 * called by application.closeWindow().
809 */
810 public void onClose() {
811 // Default: do nothing
812 }
813
814 /**
815 * Called by application.switchWindow() when this window gets the
816 * focus, and also by application.addWindow().
817 */
818 public void onFocus() {
819 // Default: do nothing
820 }
821
822 /**
823 * Called by application.switchWindow() when another window gets the
824 * focus.
825 */
826 public void onUnfocus() {
827 // Default: do nothing
828 }
829
92453213
KL
830 /**
831 * Called by application.hideWindow().
832 */
833 public void onHide() {
834 // Default: do nothing
835 }
836
837 /**
838 * Called by application.showWindow().
839 */
840 public void onShow() {
841 // Default: do nothing
842 }
843
48e27807
KL
844 /**
845 * Handle mouse button presses.
846 *
847 * @param mouse mouse button event
848 */
849 @Override
850 public void onMouseDown(final TMouseEvent mouse) {
851 this.mouse = mouse;
48e27807
KL
852
853 inKeyboardResize = false;
854
fca67db0 855 if ((mouse.getAbsoluteY() == getY())
7c870d89 856 && mouse.isMouse1()
fca67db0
KL
857 && (getX() <= mouse.getAbsoluteX())
858 && (mouse.getAbsoluteX() < getX() + getWidth())
48e27807
KL
859 && !mouseOnClose()
860 && !mouseOnMaximize()
861 ) {
862 // Begin moving window
863 inWindowMove = true;
864 moveWindowMouseX = mouse.getAbsoluteX();
865 moveWindowMouseY = mouse.getAbsoluteY();
fca67db0
KL
866 oldWindowX = getX();
867 oldWindowY = getY();
48e27807
KL
868 if (maximized) {
869 maximized = false;
870 }
871 return;
872 }
873 if (mouseOnResize()) {
874 // Begin window resize
875 inWindowResize = true;
876 moveWindowMouseX = mouse.getAbsoluteX();
877 moveWindowMouseY = mouse.getAbsoluteY();
fca67db0
KL
878 resizeWindowWidth = getWidth();
879 resizeWindowHeight = getHeight();
48e27807
KL
880 if (maximized) {
881 maximized = false;
882 }
883 return;
884 }
885
2ce6dab2
KL
886 // Give the shortcut bar a shot at this.
887 if (statusBar != null) {
888 if (statusBar.statusBarMouseDown(mouse)) {
889 return;
890 }
891 }
892
48e27807
KL
893 // I didn't take it, pass it on to my children
894 super.onMouseDown(mouse);
895 }
896
48e27807
KL
897 /**
898 * Handle mouse button releases.
899 *
900 * @param mouse mouse button release event
901 */
902 @Override
903 public void onMouseUp(final TMouseEvent mouse) {
904 this.mouse = mouse;
48e27807 905
7c870d89 906 if ((inWindowMove) && (mouse.isMouse1())) {
48e27807
KL
907 // Stop moving window
908 inWindowMove = false;
909 return;
910 }
911
7c870d89 912 if ((inWindowResize) && (mouse.isMouse1())) {
48e27807
KL
913 // Stop resizing window
914 inWindowResize = false;
915 return;
916 }
917
7c870d89 918 if (mouse.isMouse1() && mouseOnClose()) {
48e27807
KL
919 // Close window
920 application.closeWindow(this);
921 return;
922 }
923
fca67db0 924 if ((mouse.getAbsoluteY() == getY())
7c870d89 925 && mouse.isMouse1()
48e27807
KL
926 && mouseOnMaximize()) {
927 if (maximized) {
928 // Restore
929 restore();
930 } else {
931 // Maximize
932 maximize();
933 }
934 // Pass a resize event to my children
fca67db0
KL
935 onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
936 getWidth(), getHeight()));
48e27807
KL
937 return;
938 }
939
2ce6dab2
KL
940 // Give the shortcut bar a shot at this.
941 if (statusBar != null) {
942 if (statusBar.statusBarMouseUp(mouse)) {
943 return;
944 }
945 }
946
48e27807
KL
947 // I didn't take it, pass it on to my children
948 super.onMouseUp(mouse);
949 }
950
951 /**
952 * Handle mouse movements.
953 *
954 * @param mouse mouse motion event
955 */
956 @Override
957 public void onMouseMotion(final TMouseEvent mouse) {
958 this.mouse = mouse;
48e27807
KL
959
960 if (inWindowMove) {
961 // Move window over
fca67db0
KL
962 setX(oldWindowX + (mouse.getAbsoluteX() - moveWindowMouseX));
963 setY(oldWindowY + (mouse.getAbsoluteY() - moveWindowMouseY));
48e27807 964 // Don't cover up the menu bar
fca67db0
KL
965 if (getY() < application.getDesktopTop()) {
966 setY(application.getDesktopTop());
48e27807 967 }
2ce6dab2
KL
968 // Don't go below the status bar
969 if (getY() >= application.getDesktopBottom()) {
970 setY(application.getDesktopBottom() - 1);
971 }
48e27807
KL
972 return;
973 }
974
975 if (inWindowResize) {
12b55d76
KL
976 // Do not permit resizing below the status line
977 if (mouse.getAbsoluteY() == application.getDesktopBottom()) {
978 inWindowResize = false;
979 return;
980 }
981
48e27807 982 // Move window over
fca67db0
KL
983 setWidth(resizeWindowWidth + (mouse.getAbsoluteX()
984 - moveWindowMouseX));
985 setHeight(resizeWindowHeight + (mouse.getAbsoluteY()
986 - moveWindowMouseY));
987 if (getX() + getWidth() > getScreen().getWidth()) {
988 setWidth(getScreen().getWidth() - getX());
48e27807 989 }
fca67db0
KL
990 if (getY() + getHeight() > application.getDesktopBottom()) {
991 setY(application.getDesktopBottom() - getHeight() + 1);
48e27807
KL
992 }
993 // Don't cover up the menu bar
fca67db0
KL
994 if (getY() < application.getDesktopTop()) {
995 setY(application.getDesktopTop());
48e27807
KL
996 }
997
998 // Keep within min/max bounds
fca67db0
KL
999 if (getWidth() < minimumWindowWidth) {
1000 setWidth(minimumWindowWidth);
48e27807
KL
1001 inWindowResize = false;
1002 }
fca67db0
KL
1003 if (getHeight() < minimumWindowHeight) {
1004 setHeight(minimumWindowHeight);
48e27807
KL
1005 inWindowResize = false;
1006 }
fca67db0
KL
1007 if ((maximumWindowWidth > 0)
1008 && (getWidth() > maximumWindowWidth)
1009 ) {
1010 setWidth(maximumWindowWidth);
48e27807
KL
1011 inWindowResize = false;
1012 }
fca67db0
KL
1013 if ((maximumWindowHeight > 0)
1014 && (getHeight() > maximumWindowHeight)
1015 ) {
1016 setHeight(maximumWindowHeight);
48e27807
KL
1017 inWindowResize = false;
1018 }
1019
1020 // Pass a resize event to my children
fca67db0
KL
1021 onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
1022 getWidth(), getHeight()));
48e27807
KL
1023 return;
1024 }
1025
2ce6dab2
KL
1026 // Give the shortcut bar a shot at this.
1027 if (statusBar != null) {
1028 statusBar.statusBarMouseMotion(mouse);
1029 }
1030
48e27807
KL
1031 // I didn't take it, pass it on to my children
1032 super.onMouseMotion(mouse);
1033 }
1034
1035 /**
1036 * Handle keystrokes.
1037 *
1038 * @param keypress keystroke event
1039 */
1040 @Override
1041 public void onKeypress(final TKeypressEvent keypress) {
1042
1043 if (inKeyboardResize) {
1044
32437017
KL
1045 // ESC or ENTER - Exit size/move
1046 if (keypress.equals(kbEsc) || keypress.equals(kbEnter)) {
48e27807
KL
1047 inKeyboardResize = false;
1048 }
1049
1050 if (keypress.equals(kbLeft)) {
fca67db0
KL
1051 if (getX() > 0) {
1052 setX(getX() - 1);
48e27807
KL
1053 }
1054 }
1055 if (keypress.equals(kbRight)) {
fca67db0
KL
1056 if (getX() < getScreen().getWidth() - 1) {
1057 setX(getX() + 1);
48e27807
KL
1058 }
1059 }
1060 if (keypress.equals(kbDown)) {
fca67db0
KL
1061 if (getY() < application.getDesktopBottom() - 1) {
1062 setY(getY() + 1);
48e27807
KL
1063 }
1064 }
1065 if (keypress.equals(kbUp)) {
fca67db0
KL
1066 if (getY() > 1) {
1067 setY(getY() - 1);
48e27807
KL
1068 }
1069 }
3b0a5f8b
KL
1070
1071 /*
1072 * Only permit keyboard resizing if the window was RESIZABLE.
1073 */
1074 if ((flags & RESIZABLE) != 0) {
1075
1076 if (keypress.equals(kbShiftLeft)) {
1077 if ((getWidth() > minimumWindowWidth)
1078 || (minimumWindowWidth <= 0)
1079 ) {
1080 setWidth(getWidth() - 1);
1081 }
48e27807 1082 }
3b0a5f8b
KL
1083 if (keypress.equals(kbShiftRight)) {
1084 if ((getWidth() < maximumWindowWidth)
1085 || (maximumWindowWidth <= 0)
1086 ) {
1087 setWidth(getWidth() + 1);
1088 }
48e27807 1089 }
3b0a5f8b
KL
1090 if (keypress.equals(kbShiftUp)) {
1091 if ((getHeight() > minimumWindowHeight)
1092 || (minimumWindowHeight <= 0)
1093 ) {
1094 setHeight(getHeight() - 1);
1095 }
48e27807 1096 }
3b0a5f8b
KL
1097 if (keypress.equals(kbShiftDown)) {
1098 if ((getHeight() < maximumWindowHeight)
1099 || (maximumWindowHeight <= 0)
1100 ) {
1101 setHeight(getHeight() + 1);
1102 }
48e27807 1103 }
48e27807 1104
3b0a5f8b
KL
1105 // Pass a resize event to my children
1106 onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
1107 getWidth(), getHeight()));
1108
1109 } // if ((flags & RESIZABLE) != 0)
0d47c546 1110
48e27807
KL
1111 return;
1112 }
1113
2ce6dab2
KL
1114 // Give the shortcut bar a shot at this.
1115 if (statusBar != null) {
1116 if (statusBar.statusBarKeypress(keypress)) {
1117 return;
1118 }
1119 }
1120
48e27807
KL
1121 // These keystrokes will typically not be seen unless a subclass
1122 // overrides onMenu() due to how TApplication dispatches
1123 // accelerators.
1124
92453213 1125 if (!(this instanceof TDesktop)) {
48e27807 1126
92453213
KL
1127 // Ctrl-W - close window
1128 if (keypress.equals(kbCtrlW)) {
78a56d5d
KL
1129 if ((flags & NOCLOSEBOX) == 0) {
1130 application.closeWindow(this);
1131 }
92453213
KL
1132 return;
1133 }
48e27807 1134
92453213
KL
1135 // F6 - behave like Alt-TAB
1136 if (keypress.equals(kbF6)) {
1137 application.switchWindow(true);
1138 return;
1139 }
48e27807 1140
92453213
KL
1141 // Shift-F6 - behave like Shift-Alt-TAB
1142 if (keypress.equals(kbShiftF6)) {
1143 application.switchWindow(false);
1144 return;
48e27807 1145 }
48e27807 1146
92453213
KL
1147 // F5 - zoom
1148 if (keypress.equals(kbF5)) {
1149 if (maximized) {
1150 restore();
1151 } else {
1152 maximize();
1153 }
1154 }
1155
1156 // Ctrl-F5 - size/move
1157 if (keypress.equals(kbCtrlF5)) {
1158 inKeyboardResize = !inKeyboardResize;
1159 }
1160
1161 } // if (!(this instanceof TDesktop))
48e27807
KL
1162
1163 // I didn't take it, pass it on to my children
1164 super.onKeypress(keypress);
1165 }
1166
1167 /**
1168 * Handle posted command events.
1169 *
1170 * @param command command event
1171 */
1172 @Override
1173 public void onCommand(final TCommandEvent command) {
1174
1175 // These commands will typically not be seen unless a subclass
1176 // overrides onMenu() due to how TApplication dispatches
1177 // accelerators.
1178
92453213 1179 if (!(this instanceof TDesktop)) {
48e27807 1180
92453213 1181 if (command.equals(cmWindowClose)) {
78a56d5d
KL
1182 if ((flags & NOCLOSEBOX) == 0) {
1183 application.closeWindow(this);
1184 }
92453213
KL
1185 return;
1186 }
48e27807 1187
92453213
KL
1188 if (command.equals(cmWindowNext)) {
1189 application.switchWindow(true);
1190 return;
1191 }
48e27807 1192
92453213
KL
1193 if (command.equals(cmWindowPrevious)) {
1194 application.switchWindow(false);
1195 return;
1196 }
48e27807 1197
92453213
KL
1198 if (command.equals(cmWindowMove)) {
1199 inKeyboardResize = true;
1200 return;
1201 }
1202
1203 if (command.equals(cmWindowZoom)) {
1204 if (maximized) {
1205 restore();
1206 } else {
1207 maximize();
1208 }
48e27807 1209 }
92453213
KL
1210
1211 } // if (!(this instanceof TDesktop))
48e27807
KL
1212
1213 // I didn't take it, pass it on to my children
1214 super.onCommand(command);
1215 }
1216
1217 /**
1218 * Handle posted menu events.
1219 *
1220 * @param menu menu event
1221 */
1222 @Override
1223 public void onMenu(final TMenuEvent menu) {
48e27807 1224
92453213 1225 if (!(this instanceof TDesktop)) {
48e27807 1226
92453213 1227 if (menu.getId() == TMenu.MID_WINDOW_CLOSE) {
78a56d5d
KL
1228 if ((flags & NOCLOSEBOX) == 0) {
1229 application.closeWindow(this);
1230 }
92453213
KL
1231 return;
1232 }
48e27807 1233
92453213
KL
1234 if (menu.getId() == TMenu.MID_WINDOW_NEXT) {
1235 application.switchWindow(true);
1236 return;
1237 }
48e27807 1238
92453213
KL
1239 if (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) {
1240 application.switchWindow(false);
1241 return;
48e27807 1242 }
92453213
KL
1243
1244 if (menu.getId() == TMenu.MID_WINDOW_MOVE) {
1245 inKeyboardResize = true;
1246 return;
1247 }
1248
1249 if (menu.getId() == TMenu.MID_WINDOW_ZOOM) {
1250 if (maximized) {
1251 restore();
1252 } else {
1253 maximize();
1254 }
1255 return;
1256 }
1257
1258 } // if (!(this instanceof TDesktop))
48e27807
KL
1259
1260 // I didn't take it, pass it on to my children
1261 super.onMenu(menu);
1262 }
1263
1264 // ------------------------------------------------------------------------
1265 // Passthru for Screen functions ------------------------------------------
1266 // ------------------------------------------------------------------------
1267
1268 /**
1269 * Get the attributes at one location.
1270 *
1271 * @param x column coordinate. 0 is the left-most column.
1272 * @param y row coordinate. 0 is the top-most row.
1273 * @return attributes at (x, y)
1274 */
1275 public final CellAttributes getAttrXY(final int x, final int y) {
1276 return getScreen().getAttrXY(x, y);
1277 }
1278
1279 /**
1280 * Set the attributes at one location.
1281 *
1282 * @param x column coordinate. 0 is the left-most column.
1283 * @param y row coordinate. 0 is the top-most row.
1284 * @param attr attributes to use (bold, foreColor, backColor)
1285 */
1286 public final void putAttrXY(final int x, final int y,
1287 final CellAttributes attr) {
1288
1289 getScreen().putAttrXY(x, y, attr);
1290 }
1291
1292 /**
1293 * Set the attributes at one location.
1294 *
1295 * @param x column coordinate. 0 is the left-most column.
1296 * @param y row coordinate. 0 is the top-most row.
1297 * @param attr attributes to use (bold, foreColor, backColor)
1298 * @param clip if true, honor clipping/offset
1299 */
1300 public final void putAttrXY(final int x, final int y,
1301 final CellAttributes attr, final boolean clip) {
1302
1303 getScreen().putAttrXY(x, y, attr, clip);
1304 }
1305
1306 /**
1307 * Fill the entire screen with one character with attributes.
1308 *
1309 * @param ch character to draw
1310 * @param attr attributes to use (bold, foreColor, backColor)
1311 */
1312 public final void putAll(final char ch, final CellAttributes attr) {
1313 getScreen().putAll(ch, attr);
1314 }
1315
1316 /**
1317 * Render one character with attributes.
1318 *
1319 * @param x column coordinate. 0 is the left-most column.
1320 * @param y row coordinate. 0 is the top-most row.
1321 * @param ch character + attributes to draw
1322 */
1323 public final void putCharXY(final int x, final int y, final Cell ch) {
1324 getScreen().putCharXY(x, y, ch);
1325 }
1326
1327 /**
1328 * Render one character with attributes.
1329 *
1330 * @param x column coordinate. 0 is the left-most column.
1331 * @param y row coordinate. 0 is the top-most row.
1332 * @param ch character to draw
1333 * @param attr attributes to use (bold, foreColor, backColor)
1334 */
1335 public final void putCharXY(final int x, final int y, final char ch,
1336 final CellAttributes attr) {
1337
1338 getScreen().putCharXY(x, y, ch, attr);
1339 }
1340
1341 /**
1342 * Render one character without changing the underlying attributes.
1343 *
1344 * @param x column coordinate. 0 is the left-most column.
1345 * @param y row coordinate. 0 is the top-most row.
1346 * @param ch character to draw
1347 */
1348 public final void putCharXY(final int x, final int y, final char ch) {
1349 getScreen().putCharXY(x, y, ch);
1350 }
1351
1352 /**
1353 * Render a string. Does not wrap if the string exceeds the line.
1354 *
1355 * @param x column coordinate. 0 is the left-most column.
1356 * @param y row coordinate. 0 is the top-most row.
1357 * @param str string to draw
1358 * @param attr attributes to use (bold, foreColor, backColor)
1359 */
0d47c546 1360 public final void putStringXY(final int x, final int y, final String str,
48e27807
KL
1361 final CellAttributes attr) {
1362
0d47c546 1363 getScreen().putStringXY(x, y, str, attr);
48e27807
KL
1364 }
1365
1366 /**
1367 * Render a string without changing the underlying attribute. Does not
1368 * wrap if the string exceeds the line.
1369 *
1370 * @param x column coordinate. 0 is the left-most column.
1371 * @param y row coordinate. 0 is the top-most row.
1372 * @param str string to draw
1373 */
0d47c546
KL
1374 public final void putStringXY(final int x, final int y, final String str) {
1375 getScreen().putStringXY(x, y, str);
48e27807
KL
1376 }
1377
1378 /**
1379 * Draw a vertical line from (x, y) to (x, y + n).
1380 *
1381 * @param x column coordinate. 0 is the left-most column.
1382 * @param y row coordinate. 0 is the top-most row.
1383 * @param n number of characters to draw
1384 * @param ch character to draw
1385 * @param attr attributes to use (bold, foreColor, backColor)
1386 */
1387 public final void vLineXY(final int x, final int y, final int n,
1388 final char ch, final CellAttributes attr) {
1389
1390 getScreen().vLineXY(x, y, n, ch, attr);
1391 }
1392
1393 /**
1394 * Draw a horizontal line from (x, y) to (x + n, y).
1395 *
1396 * @param x column coordinate. 0 is the left-most column.
1397 * @param y row coordinate. 0 is the top-most row.
1398 * @param n number of characters to draw
1399 * @param ch character to draw
1400 * @param attr attributes to use (bold, foreColor, backColor)
1401 */
1402 public final void hLineXY(final int x, final int y, final int n,
1403 final char ch, final CellAttributes attr) {
1404
1405 getScreen().hLineXY(x, y, n, ch, attr);
1406 }
1407
48e27807 1408}