Common Scrollable interface
[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 */
92453213
KL
306 public void maximize() {
307 if (maximized) {
308 return;
309 }
310
2ce6dab2
KL
311 restoreWindowWidth = getWidth();
312 restoreWindowHeight = getHeight();
313 restoreWindowX = getX();
314 restoreWindowY = getY();
315 setWidth(getScreen().getWidth());
316 setHeight(application.getDesktopBottom() - 1);
317 setX(0);
318 setY(1);
319 maximized = true;
320 }
321
322 /**
7657ad8c 323 * Restore (unmaximize) window.
2ce6dab2 324 */
92453213
KL
325 public void restore() {
326 if (!maximized) {
327 return;
328 }
329
2ce6dab2
KL
330 setWidth(restoreWindowWidth);
331 setHeight(restoreWindowHeight);
332 setX(restoreWindowX);
333 setY(restoreWindowY);
334 maximized = false;
335 }
336
92453213
KL
337 // ------------------------------------------------------------------------
338 // Window visibility ------------------------------------------------------
339 // ------------------------------------------------------------------------
340
341 /**
342 * Hidden flag. A hidden window will still have its onIdle() called, and
343 * will also have onClose() called at application exit. Note package
344 * private access: TApplication will force hidden false if a modal window
345 * is active.
346 */
347 boolean hidden = false;
348
349 /**
350 * Returns true if this window is hidden.
351 *
352 * @return true if this window is hidden, false if the window is shown
353 */
354 public final boolean isHidden() {
355 return hidden;
356 }
357
358 /**
359 * Returns true if this window is shown.
360 *
361 * @return true if this window is shown, false if the window is hidden
362 */
363 public final boolean isShown() {
364 return !hidden;
365 }
366
367 /**
368 * Hide window. A hidden window will still have its onIdle() called, and
369 * will also have onClose() called at application exit. Hidden windows
370 * will not receive any other events.
371 */
372 public void hide() {
373 application.hideWindow(this);
374 }
375
376 /**
377 * Show window.
378 */
379 public void show() {
380 application.showWindow(this);
381 }
382
383 /**
384 * Activate window (bring to top and receive events).
385 */
386 public void activate() {
387 application.activateWindow(this);
388 }
389
2ce6dab2
KL
390 // ------------------------------------------------------------------------
391 // Constructors -----------------------------------------------------------
392 // ------------------------------------------------------------------------
393
48e27807
KL
394 /**
395 * Public constructor. Window will be located at (0, 0).
396 *
397 * @param application TApplication that manages this window
398 * @param title window title, will be centered along the top border
399 * @param width width of window
400 * @param height height of window
401 */
402 public TWindow(final TApplication application, final String title,
403 final int width, final int height) {
404
405 this(application, title, 0, 0, width, height, RESIZABLE);
406 }
407
408 /**
409 * Public constructor. Window will be located at (0, 0).
410 *
411 * @param application TApplication that manages this window
412 * @param title window title, will be centered along the top border
413 * @param width width of window
414 * @param height height of window
415 * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
416 */
417 public TWindow(final TApplication application, final String title,
418 final int width, final int height, final int flags) {
419
420 this(application, title, 0, 0, width, height, flags);
421 }
422
423 /**
424 * Public constructor.
425 *
426 * @param application TApplication that manages this window
427 * @param title window title, will be centered along the top border
428 * @param x column relative to parent
429 * @param y row relative to parent
430 * @param width width of window
431 * @param height height of window
432 */
433 public TWindow(final TApplication application, final String title,
434 final int x, final int y, final int width, final int height) {
435
436 this(application, title, x, y, width, height, RESIZABLE);
437 }
438
439 /**
440 * Public constructor.
441 *
442 * @param application TApplication that manages this window
443 * @param title window title, will be centered along the top border
444 * @param x column relative to parent
445 * @param y row relative to parent
446 * @param width width of window
447 * @param height height of window
448 * @param flags mask of RESIZABLE, CENTERED, or MODAL
449 */
450 public TWindow(final TApplication application, final String title,
451 final int x, final int y, final int width, final int height,
452 final int flags) {
453
fca67db0
KL
454 super();
455
48e27807 456 // I am my own window and parent
fca67db0
KL
457 setupForTWindow(this, x, y + application.getDesktopTop(),
458 width, height);
48e27807
KL
459
460 // Save fields
461 this.title = title;
462 this.application = application;
48e27807
KL
463 this.flags = flags;
464
465 // Minimum width/height are 10 and 2
466 assert (width >= 10);
fca67db0 467 assert (getHeight() >= 2);
48e27807
KL
468
469 // MODAL implies CENTERED
470 if (isModal()) {
471 this.flags |= CENTERED;
472 }
473
474 // Center window if specified
475 center();
476
477 // Add me to the application
478 application.addWindow(this);
479 }
480
2ce6dab2
KL
481 // ------------------------------------------------------------------------
482 // General behavior -------------------------------------------------------
483 // ------------------------------------------------------------------------
48e27807
KL
484
485 /**
486 * Returns true if this window is modal.
487 *
488 * @return true if this window is modal
489 */
490 public final boolean isModal() {
491 if ((flags & MODAL) == 0) {
492 return false;
493 }
494 return true;
495 }
496
48e27807
KL
497 /**
498 * Retrieve the background color.
499 *
500 * @return the background color
501 */
30d336cc 502 public final CellAttributes getBackground() {
48e27807
KL
503 if (!isModal()
504 && (inWindowMove || inWindowResize || inKeyboardResize)
505 ) {
7c870d89 506 assert (isActive());
928811d8 507 return getTheme().getColor("twindow.background.windowmove");
48e27807 508 } else if (isModal() && inWindowMove) {
7c870d89 509 assert (isActive());
928811d8 510 return getTheme().getColor("twindow.background.modal");
48e27807 511 } else if (isModal()) {
7c870d89 512 if (isActive()) {
928811d8 513 return getTheme().getColor("twindow.background.modal");
48e27807 514 }
928811d8 515 return getTheme().getColor("twindow.background.modal.inactive");
7c870d89 516 } else if (isActive()) {
48e27807 517 assert (!isModal());
928811d8 518 return getTheme().getColor("twindow.background");
48e27807
KL
519 } else {
520 assert (!isModal());
928811d8 521 return getTheme().getColor("twindow.background.inactive");
48e27807
KL
522 }
523 }
524
525 /**
526 * Retrieve the border color.
527 *
528 * @return the border color
529 */
3649b921 530 public CellAttributes getBorder() {
48e27807
KL
531 if (!isModal()
532 && (inWindowMove || inWindowResize || inKeyboardResize)
533 ) {
7c870d89 534 assert (isActive());
928811d8 535 return getTheme().getColor("twindow.border.windowmove");
48e27807 536 } else if (isModal() && inWindowMove) {
7c870d89 537 assert (isActive());
928811d8 538 return getTheme().getColor("twindow.border.modal.windowmove");
48e27807 539 } else if (isModal()) {
7c870d89 540 if (isActive()) {
928811d8 541 return getTheme().getColor("twindow.border.modal");
48e27807 542 } else {
928811d8 543 return getTheme().getColor("twindow.border.modal.inactive");
48e27807 544 }
7c870d89 545 } else if (isActive()) {
48e27807 546 assert (!isModal());
928811d8 547 return getTheme().getColor("twindow.border");
48e27807
KL
548 } else {
549 assert (!isModal());
928811d8 550 return getTheme().getColor("twindow.border.inactive");
48e27807
KL
551 }
552 }
553
554 /**
555 * Retrieve the border line type.
556 *
557 * @return the border line type
558 */
928811d8 559 private int getBorderType() {
48e27807
KL
560 if (!isModal()
561 && (inWindowMove || inWindowResize || inKeyboardResize)
562 ) {
7c870d89 563 assert (isActive());
48e27807
KL
564 return 1;
565 } else if (isModal() && inWindowMove) {
7c870d89 566 assert (isActive());
48e27807
KL
567 return 1;
568 } else if (isModal()) {
7c870d89 569 if (isActive()) {
48e27807
KL
570 return 2;
571 } else {
572 return 1;
573 }
7c870d89 574 } else if (isActive()) {
48e27807
KL
575 return 2;
576 } else {
577 return 1;
578 }
579 }
580
48e27807
KL
581 /**
582 * Called by TApplication.drawChildren() to render on screen.
583 */
584 @Override
585 public void draw() {
586 // Draw the box and background first.
587 CellAttributes border = getBorder();
588 CellAttributes background = getBackground();
589 int borderType = getBorderType();
590
fca67db0 591 getScreen().drawBox(0, 0, getWidth(), getHeight(), border,
48e27807
KL
592 background, borderType, true);
593
594 // Draw the title
fca67db0 595 int titleLeft = (getWidth() - title.length() - 2) / 2;
48e27807 596 putCharXY(titleLeft, 0, ' ', border);
0d47c546 597 putStringXY(titleLeft + 1, 0, title);
48e27807
KL
598 putCharXY(titleLeft + title.length() + 1, 0, ' ', border);
599
7c870d89 600 if (isActive()) {
48e27807
KL
601
602 // Draw the close button
603 putCharXY(2, 0, '[', border);
604 putCharXY(4, 0, ']', border);
7c870d89 605 if (mouseOnClose() && mouse.isMouse1()) {
48e27807
KL
606 putCharXY(3, 0, GraphicsChars.CP437[0x0F],
607 !isModal()
928811d8
KL
608 ? getTheme().getColor("twindow.border.windowmove")
609 : getTheme().getColor("twindow.border.modal.windowmove"));
48e27807
KL
610 } else {
611 putCharXY(3, 0, GraphicsChars.CP437[0xFE],
612 !isModal()
928811d8
KL
613 ? getTheme().getColor("twindow.border.windowmove")
614 : getTheme().getColor("twindow.border.modal.windowmove"));
48e27807
KL
615 }
616
617 // Draw the maximize button
618 if (!isModal()) {
619
fca67db0
KL
620 putCharXY(getWidth() - 5, 0, '[', border);
621 putCharXY(getWidth() - 3, 0, ']', border);
7c870d89 622 if (mouseOnMaximize() && mouse.isMouse1()) {
fca67db0 623 putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x0F],
928811d8 624 getTheme().getColor("twindow.border.windowmove"));
48e27807
KL
625 } else {
626 if (maximized) {
fca67db0 627 putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x12],
928811d8 628 getTheme().getColor("twindow.border.windowmove"));
48e27807 629 } else {
fca67db0 630 putCharXY(getWidth() - 4, 0, GraphicsChars.UPARROW,
928811d8 631 getTheme().getColor("twindow.border.windowmove"));
48e27807
KL
632 }
633 }
634
635 // Draw the resize corner
636 if ((flags & RESIZABLE) != 0) {
928811d8
KL
637 putCharXY(getWidth() - 2, getHeight() - 1,
638 GraphicsChars.SINGLE_BAR,
639 getTheme().getColor("twindow.border.windowmove"));
640 putCharXY(getWidth() - 1, getHeight() - 1,
641 GraphicsChars.LRCORNER,
642 getTheme().getColor("twindow.border.windowmove"));
48e27807
KL
643 }
644 }
645 }
646 }
647
2ce6dab2
KL
648 // ------------------------------------------------------------------------
649 // Event handlers ---------------------------------------------------------
650 // ------------------------------------------------------------------------
651
652 /**
653 * Returns true if the mouse is currently on the close button.
654 *
655 * @return true if mouse is currently on the close button
656 */
92453213 657 protected boolean mouseOnClose() {
2ce6dab2
KL
658 if ((mouse != null)
659 && (mouse.getAbsoluteY() == getY())
660 && (mouse.getAbsoluteX() == getX() + 3)
661 ) {
662 return true;
663 }
664 return false;
665 }
666
667 /**
668 * Returns true if the mouse is currently on the maximize/restore button.
669 *
670 * @return true if the mouse is currently on the maximize/restore button
671 */
92453213 672 protected boolean mouseOnMaximize() {
2ce6dab2
KL
673 if ((mouse != null)
674 && !isModal()
675 && (mouse.getAbsoluteY() == getY())
676 && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
677 ) {
678 return true;
679 }
680 return false;
681 }
682
683 /**
684 * Returns true if the mouse is currently on the resizable lower right
685 * corner.
686 *
687 * @return true if the mouse is currently on the resizable lower right
688 * corner
689 */
92453213 690 protected boolean mouseOnResize() {
2ce6dab2
KL
691 if (((flags & RESIZABLE) != 0)
692 && !isModal()
693 && (mouse != null)
694 && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
695 && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
696 || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
697 ) {
698 return true;
699 }
700 return false;
701 }
702
703 /**
704 * Subclasses should override this method to cleanup resources. This is
705 * called by application.closeWindow().
706 */
707 public void onClose() {
708 // Default: do nothing
709 }
710
711 /**
712 * Called by application.switchWindow() when this window gets the
713 * focus, and also by application.addWindow().
714 */
715 public void onFocus() {
716 // Default: do nothing
717 }
718
719 /**
720 * Called by application.switchWindow() when another window gets the
721 * focus.
722 */
723 public void onUnfocus() {
724 // Default: do nothing
725 }
726
92453213
KL
727 /**
728 * Called by application.hideWindow().
729 */
730 public void onHide() {
731 // Default: do nothing
732 }
733
734 /**
735 * Called by application.showWindow().
736 */
737 public void onShow() {
738 // Default: do nothing
739 }
740
48e27807
KL
741 /**
742 * Handle mouse button presses.
743 *
744 * @param mouse mouse button event
745 */
746 @Override
747 public void onMouseDown(final TMouseEvent mouse) {
748 this.mouse = mouse;
48e27807
KL
749
750 inKeyboardResize = false;
751
fca67db0 752 if ((mouse.getAbsoluteY() == getY())
7c870d89 753 && mouse.isMouse1()
fca67db0
KL
754 && (getX() <= mouse.getAbsoluteX())
755 && (mouse.getAbsoluteX() < getX() + getWidth())
48e27807
KL
756 && !mouseOnClose()
757 && !mouseOnMaximize()
758 ) {
759 // Begin moving window
760 inWindowMove = true;
761 moveWindowMouseX = mouse.getAbsoluteX();
762 moveWindowMouseY = mouse.getAbsoluteY();
fca67db0
KL
763 oldWindowX = getX();
764 oldWindowY = getY();
48e27807
KL
765 if (maximized) {
766 maximized = false;
767 }
768 return;
769 }
770 if (mouseOnResize()) {
771 // Begin window resize
772 inWindowResize = true;
773 moveWindowMouseX = mouse.getAbsoluteX();
774 moveWindowMouseY = mouse.getAbsoluteY();
fca67db0
KL
775 resizeWindowWidth = getWidth();
776 resizeWindowHeight = getHeight();
48e27807
KL
777 if (maximized) {
778 maximized = false;
779 }
780 return;
781 }
782
2ce6dab2
KL
783 // Give the shortcut bar a shot at this.
784 if (statusBar != null) {
785 if (statusBar.statusBarMouseDown(mouse)) {
786 return;
787 }
788 }
789
48e27807
KL
790 // I didn't take it, pass it on to my children
791 super.onMouseDown(mouse);
792 }
793
48e27807
KL
794 /**
795 * Handle mouse button releases.
796 *
797 * @param mouse mouse button release event
798 */
799 @Override
800 public void onMouseUp(final TMouseEvent mouse) {
801 this.mouse = mouse;
48e27807 802
7c870d89 803 if ((inWindowMove) && (mouse.isMouse1())) {
48e27807
KL
804 // Stop moving window
805 inWindowMove = false;
806 return;
807 }
808
7c870d89 809 if ((inWindowResize) && (mouse.isMouse1())) {
48e27807
KL
810 // Stop resizing window
811 inWindowResize = false;
812 return;
813 }
814
7c870d89 815 if (mouse.isMouse1() && mouseOnClose()) {
48e27807
KL
816 // Close window
817 application.closeWindow(this);
818 return;
819 }
820
fca67db0 821 if ((mouse.getAbsoluteY() == getY())
7c870d89 822 && mouse.isMouse1()
48e27807
KL
823 && mouseOnMaximize()) {
824 if (maximized) {
825 // Restore
826 restore();
827 } else {
828 // Maximize
829 maximize();
830 }
831 // Pass a resize event to my children
fca67db0
KL
832 onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
833 getWidth(), getHeight()));
48e27807
KL
834 return;
835 }
836
2ce6dab2
KL
837 // Give the shortcut bar a shot at this.
838 if (statusBar != null) {
839 if (statusBar.statusBarMouseUp(mouse)) {
840 return;
841 }
842 }
843
48e27807
KL
844 // I didn't take it, pass it on to my children
845 super.onMouseUp(mouse);
846 }
847
848 /**
849 * Handle mouse movements.
850 *
851 * @param mouse mouse motion event
852 */
853 @Override
854 public void onMouseMotion(final TMouseEvent mouse) {
855 this.mouse = mouse;
48e27807
KL
856
857 if (inWindowMove) {
858 // Move window over
fca67db0
KL
859 setX(oldWindowX + (mouse.getAbsoluteX() - moveWindowMouseX));
860 setY(oldWindowY + (mouse.getAbsoluteY() - moveWindowMouseY));
48e27807 861 // Don't cover up the menu bar
fca67db0
KL
862 if (getY() < application.getDesktopTop()) {
863 setY(application.getDesktopTop());
48e27807 864 }
2ce6dab2
KL
865 // Don't go below the status bar
866 if (getY() >= application.getDesktopBottom()) {
867 setY(application.getDesktopBottom() - 1);
868 }
48e27807
KL
869 return;
870 }
871
872 if (inWindowResize) {
873 // Move window over
fca67db0
KL
874 setWidth(resizeWindowWidth + (mouse.getAbsoluteX()
875 - moveWindowMouseX));
876 setHeight(resizeWindowHeight + (mouse.getAbsoluteY()
877 - moveWindowMouseY));
878 if (getX() + getWidth() > getScreen().getWidth()) {
879 setWidth(getScreen().getWidth() - getX());
48e27807 880 }
fca67db0
KL
881 if (getY() + getHeight() > application.getDesktopBottom()) {
882 setY(application.getDesktopBottom() - getHeight() + 1);
48e27807
KL
883 }
884 // Don't cover up the menu bar
fca67db0
KL
885 if (getY() < application.getDesktopTop()) {
886 setY(application.getDesktopTop());
48e27807
KL
887 }
888
889 // Keep within min/max bounds
fca67db0
KL
890 if (getWidth() < minimumWindowWidth) {
891 setWidth(minimumWindowWidth);
48e27807
KL
892 inWindowResize = false;
893 }
fca67db0
KL
894 if (getHeight() < minimumWindowHeight) {
895 setHeight(minimumWindowHeight);
48e27807
KL
896 inWindowResize = false;
897 }
fca67db0
KL
898 if ((maximumWindowWidth > 0)
899 && (getWidth() > maximumWindowWidth)
900 ) {
901 setWidth(maximumWindowWidth);
48e27807
KL
902 inWindowResize = false;
903 }
fca67db0
KL
904 if ((maximumWindowHeight > 0)
905 && (getHeight() > maximumWindowHeight)
906 ) {
907 setHeight(maximumWindowHeight);
48e27807
KL
908 inWindowResize = false;
909 }
910
911 // Pass a resize event to my children
fca67db0
KL
912 onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
913 getWidth(), getHeight()));
48e27807
KL
914 return;
915 }
916
2ce6dab2
KL
917 // Give the shortcut bar a shot at this.
918 if (statusBar != null) {
919 statusBar.statusBarMouseMotion(mouse);
920 }
921
48e27807
KL
922 // I didn't take it, pass it on to my children
923 super.onMouseMotion(mouse);
924 }
925
926 /**
927 * Handle keystrokes.
928 *
929 * @param keypress keystroke event
930 */
931 @Override
932 public void onKeypress(final TKeypressEvent keypress) {
933
934 if (inKeyboardResize) {
935
32437017
KL
936 // ESC or ENTER - Exit size/move
937 if (keypress.equals(kbEsc) || keypress.equals(kbEnter)) {
48e27807
KL
938 inKeyboardResize = false;
939 }
940
941 if (keypress.equals(kbLeft)) {
fca67db0
KL
942 if (getX() > 0) {
943 setX(getX() - 1);
48e27807
KL
944 }
945 }
946 if (keypress.equals(kbRight)) {
fca67db0
KL
947 if (getX() < getScreen().getWidth() - 1) {
948 setX(getX() + 1);
48e27807
KL
949 }
950 }
951 if (keypress.equals(kbDown)) {
fca67db0
KL
952 if (getY() < application.getDesktopBottom() - 1) {
953 setY(getY() + 1);
48e27807
KL
954 }
955 }
956 if (keypress.equals(kbUp)) {
fca67db0
KL
957 if (getY() > 1) {
958 setY(getY() - 1);
48e27807
KL
959 }
960 }
961 if (keypress.equals(kbShiftLeft)) {
a83fea2b
KL
962 if ((getWidth() > minimumWindowWidth)
963 || (minimumWindowWidth <= 0)
964 ) {
fca67db0 965 setWidth(getWidth() - 1);
48e27807
KL
966 }
967 }
968 if (keypress.equals(kbShiftRight)) {
a83fea2b
KL
969 if ((getWidth() < maximumWindowWidth)
970 || (maximumWindowWidth <= 0)
971 ) {
fca67db0 972 setWidth(getWidth() + 1);
48e27807
KL
973 }
974 }
975 if (keypress.equals(kbShiftUp)) {
a83fea2b
KL
976 if ((getHeight() > minimumWindowHeight)
977 || (minimumWindowHeight <= 0)
978 ) {
fca67db0 979 setHeight(getHeight() - 1);
48e27807
KL
980 }
981 }
982 if (keypress.equals(kbShiftDown)) {
a83fea2b
KL
983 if ((getHeight() < maximumWindowHeight)
984 || (maximumWindowHeight <= 0)
985 ) {
fca67db0 986 setHeight(getHeight() + 1);
48e27807
KL
987 }
988 }
989
0d47c546
KL
990 // Pass a resize event to my children
991 onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
992 getWidth(), getHeight()));
993
48e27807
KL
994 return;
995 }
996
2ce6dab2
KL
997 // Give the shortcut bar a shot at this.
998 if (statusBar != null) {
999 if (statusBar.statusBarKeypress(keypress)) {
1000 return;
1001 }
1002 }
1003
48e27807
KL
1004 // These keystrokes will typically not be seen unless a subclass
1005 // overrides onMenu() due to how TApplication dispatches
1006 // accelerators.
1007
92453213 1008 if (!(this instanceof TDesktop)) {
48e27807 1009
92453213
KL
1010 // Ctrl-W - close window
1011 if (keypress.equals(kbCtrlW)) {
1012 application.closeWindow(this);
1013 return;
1014 }
48e27807 1015
92453213
KL
1016 // F6 - behave like Alt-TAB
1017 if (keypress.equals(kbF6)) {
1018 application.switchWindow(true);
1019 return;
1020 }
48e27807 1021
92453213
KL
1022 // Shift-F6 - behave like Shift-Alt-TAB
1023 if (keypress.equals(kbShiftF6)) {
1024 application.switchWindow(false);
1025 return;
48e27807 1026 }
48e27807 1027
92453213
KL
1028 // F5 - zoom
1029 if (keypress.equals(kbF5)) {
1030 if (maximized) {
1031 restore();
1032 } else {
1033 maximize();
1034 }
1035 }
1036
1037 // Ctrl-F5 - size/move
1038 if (keypress.equals(kbCtrlF5)) {
1039 inKeyboardResize = !inKeyboardResize;
1040 }
1041
1042 } // if (!(this instanceof TDesktop))
48e27807
KL
1043
1044 // I didn't take it, pass it on to my children
1045 super.onKeypress(keypress);
1046 }
1047
1048 /**
1049 * Handle posted command events.
1050 *
1051 * @param command command event
1052 */
1053 @Override
1054 public void onCommand(final TCommandEvent command) {
1055
1056 // These commands will typically not be seen unless a subclass
1057 // overrides onMenu() due to how TApplication dispatches
1058 // accelerators.
1059
92453213 1060 if (!(this instanceof TDesktop)) {
48e27807 1061
92453213
KL
1062 if (command.equals(cmWindowClose)) {
1063 application.closeWindow(this);
1064 return;
1065 }
48e27807 1066
92453213
KL
1067 if (command.equals(cmWindowNext)) {
1068 application.switchWindow(true);
1069 return;
1070 }
48e27807 1071
92453213
KL
1072 if (command.equals(cmWindowPrevious)) {
1073 application.switchWindow(false);
1074 return;
1075 }
48e27807 1076
92453213
KL
1077 if (command.equals(cmWindowMove)) {
1078 inKeyboardResize = true;
1079 return;
1080 }
1081
1082 if (command.equals(cmWindowZoom)) {
1083 if (maximized) {
1084 restore();
1085 } else {
1086 maximize();
1087 }
48e27807 1088 }
92453213
KL
1089
1090 } // if (!(this instanceof TDesktop))
48e27807
KL
1091
1092 // I didn't take it, pass it on to my children
1093 super.onCommand(command);
1094 }
1095
1096 /**
1097 * Handle posted menu events.
1098 *
1099 * @param menu menu event
1100 */
1101 @Override
1102 public void onMenu(final TMenuEvent menu) {
48e27807 1103
92453213 1104 if (!(this instanceof TDesktop)) {
48e27807 1105
92453213
KL
1106 if (menu.getId() == TMenu.MID_WINDOW_CLOSE) {
1107 application.closeWindow(this);
1108 return;
1109 }
48e27807 1110
92453213
KL
1111 if (menu.getId() == TMenu.MID_WINDOW_NEXT) {
1112 application.switchWindow(true);
1113 return;
1114 }
48e27807 1115
92453213
KL
1116 if (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) {
1117 application.switchWindow(false);
1118 return;
48e27807 1119 }
92453213
KL
1120
1121 if (menu.getId() == TMenu.MID_WINDOW_MOVE) {
1122 inKeyboardResize = true;
1123 return;
1124 }
1125
1126 if (menu.getId() == TMenu.MID_WINDOW_ZOOM) {
1127 if (maximized) {
1128 restore();
1129 } else {
1130 maximize();
1131 }
1132 return;
1133 }
1134
1135 } // if (!(this instanceof TDesktop))
48e27807
KL
1136
1137 // I didn't take it, pass it on to my children
1138 super.onMenu(menu);
1139 }
1140
1141 // ------------------------------------------------------------------------
1142 // Passthru for Screen functions ------------------------------------------
1143 // ------------------------------------------------------------------------
1144
1145 /**
1146 * Get the attributes at one location.
1147 *
1148 * @param x column coordinate. 0 is the left-most column.
1149 * @param y row coordinate. 0 is the top-most row.
1150 * @return attributes at (x, y)
1151 */
1152 public final CellAttributes getAttrXY(final int x, final int y) {
1153 return getScreen().getAttrXY(x, y);
1154 }
1155
1156 /**
1157 * Set the attributes at one location.
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 attr attributes to use (bold, foreColor, backColor)
1162 */
1163 public final void putAttrXY(final int x, final int y,
1164 final CellAttributes attr) {
1165
1166 getScreen().putAttrXY(x, y, attr);
1167 }
1168
1169 /**
1170 * Set the attributes at one location.
1171 *
1172 * @param x column coordinate. 0 is the left-most column.
1173 * @param y row coordinate. 0 is the top-most row.
1174 * @param attr attributes to use (bold, foreColor, backColor)
1175 * @param clip if true, honor clipping/offset
1176 */
1177 public final void putAttrXY(final int x, final int y,
1178 final CellAttributes attr, final boolean clip) {
1179
1180 getScreen().putAttrXY(x, y, attr, clip);
1181 }
1182
1183 /**
1184 * Fill the entire screen with one character with attributes.
1185 *
1186 * @param ch character to draw
1187 * @param attr attributes to use (bold, foreColor, backColor)
1188 */
1189 public final void putAll(final char ch, final CellAttributes attr) {
1190 getScreen().putAll(ch, attr);
1191 }
1192
1193 /**
1194 * Render one character with attributes.
1195 *
1196 * @param x column coordinate. 0 is the left-most column.
1197 * @param y row coordinate. 0 is the top-most row.
1198 * @param ch character + attributes to draw
1199 */
1200 public final void putCharXY(final int x, final int y, final Cell ch) {
1201 getScreen().putCharXY(x, y, ch);
1202 }
1203
1204 /**
1205 * Render one character with attributes.
1206 *
1207 * @param x column coordinate. 0 is the left-most column.
1208 * @param y row coordinate. 0 is the top-most row.
1209 * @param ch character to draw
1210 * @param attr attributes to use (bold, foreColor, backColor)
1211 */
1212 public final void putCharXY(final int x, final int y, final char ch,
1213 final CellAttributes attr) {
1214
1215 getScreen().putCharXY(x, y, ch, attr);
1216 }
1217
1218 /**
1219 * Render one character without changing the underlying attributes.
1220 *
1221 * @param x column coordinate. 0 is the left-most column.
1222 * @param y row coordinate. 0 is the top-most row.
1223 * @param ch character to draw
1224 */
1225 public final void putCharXY(final int x, final int y, final char ch) {
1226 getScreen().putCharXY(x, y, ch);
1227 }
1228
1229 /**
1230 * Render a string. Does not wrap if the string exceeds the line.
1231 *
1232 * @param x column coordinate. 0 is the left-most column.
1233 * @param y row coordinate. 0 is the top-most row.
1234 * @param str string to draw
1235 * @param attr attributes to use (bold, foreColor, backColor)
1236 */
0d47c546 1237 public final void putStringXY(final int x, final int y, final String str,
48e27807
KL
1238 final CellAttributes attr) {
1239
0d47c546 1240 getScreen().putStringXY(x, y, str, attr);
48e27807
KL
1241 }
1242
1243 /**
1244 * Render a string without changing the underlying attribute. Does not
1245 * wrap if the string exceeds the line.
1246 *
1247 * @param x column coordinate. 0 is the left-most column.
1248 * @param y row coordinate. 0 is the top-most row.
1249 * @param str string to draw
1250 */
0d47c546
KL
1251 public final void putStringXY(final int x, final int y, final String str) {
1252 getScreen().putStringXY(x, y, str);
48e27807
KL
1253 }
1254
1255 /**
1256 * Draw a vertical line from (x, y) to (x, y + n).
1257 *
1258 * @param x column coordinate. 0 is the left-most column.
1259 * @param y row coordinate. 0 is the top-most row.
1260 * @param n number of characters to draw
1261 * @param ch character to draw
1262 * @param attr attributes to use (bold, foreColor, backColor)
1263 */
1264 public final void vLineXY(final int x, final int y, final int n,
1265 final char ch, final CellAttributes attr) {
1266
1267 getScreen().vLineXY(x, y, n, ch, attr);
1268 }
1269
1270 /**
1271 * Draw a horizontal line from (x, y) to (x + n, y).
1272 *
1273 * @param x column coordinate. 0 is the left-most column.
1274 * @param y row coordinate. 0 is the top-most row.
1275 * @param n number of characters to draw
1276 * @param ch character to draw
1277 * @param attr attributes to use (bold, foreColor, backColor)
1278 */
1279 public final void hLineXY(final int x, final int y, final int n,
1280 final char ch, final CellAttributes attr) {
1281
1282 getScreen().hLineXY(x, y, n, ch, attr);
1283 }
1284
1285
1286}