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