X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTWindow.java;h=4d14d0eee2debcf23b03e2df314ea41721c38c8c;hb=HEAD;hp=8749eb86cd1b46f4d41db781400d688cc4aba9de;hpb=3b0a5f8b6fd7ed8791f13f422535d581b35f9adb;p=fanfix.git diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index 8749eb8..4d14d0e 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (C) 2017 Kevin Lamonte + * Copyright (C) 2019 Kevin Lamonte * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,16 +29,17 @@ package jexer; import java.util.HashSet; +import java.util.Set; -import jexer.bits.Cell; +import jexer.backend.Screen; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; +import jexer.bits.StringUtils; import jexer.event.TCommandEvent; import jexer.event.TKeypressEvent; import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; -import jexer.io.Screen; import jexer.menu.TMenu; import static jexer.TCommand.*; import static jexer.TKeypress.*; @@ -49,7 +50,7 @@ import static jexer.TKeypress.*; public class TWindow extends TWidget { // ------------------------------------------------------------------------ - // Public constants ------------------------------------------------------- + // Constants -------------------------------------------------------------- // ------------------------------------------------------------------------ /** @@ -73,162 +74,58 @@ public class TWindow extends TWidget { */ public static final int NOCLOSEBOX = 0x08; - // ------------------------------------------------------------------------ - // Common window attributes ----------------------------------------------- - // ------------------------------------------------------------------------ - /** - * Window flags. Note package private access. + * Window has no maximize box (default no). */ - int flags = RESIZABLE; + public static final int NOZOOMBOX = 0x10; /** - * Window title. + * Window is placed at absolute position (no smart placement) (default + * no). */ - private String title = ""; + public static final int ABSOLUTEXY = 0x20; /** - * Get window title. - * - * @return window title + * Hitting the closebox with the mouse calls TApplication.hideWindow() + * rather than TApplication.closeWindow() (default no). */ - public final String getTitle() { - return title; - } + public static final int HIDEONCLOSE = 0x40; /** - * Set window title. - * - * @param title new window title + * Menus cannot be used when this window is active (default no). */ - public final void setTitle(final String title) { - this.title = title; - } + public static final int OVERRIDEMENU = 0x80; // ------------------------------------------------------------------------ - // TApplication integration ----------------------------------------------- + // Variables -------------------------------------------------------------- // ------------------------------------------------------------------------ /** - * Window's parent TApplication. + * Window flags. Note package private access. */ - private TApplication application; + int flags = RESIZABLE; /** - * Get this TWindow's parent TApplication. - * - * @return this TWindow's parent TApplication + * Window title. */ - @Override - public final TApplication getApplication() { - return application; - } + private String title = ""; /** - * Get the Screen. - * - * @return the Screen + * Window's parent TApplication. */ - @Override - public final Screen getScreen() { - return application.getScreen(); - } + private TApplication application; /** * Z order. Lower number means more in-front. */ private int z = 0; - /** - * Get Z order. Lower number means more in-front. - * - * @return Z value. Lower number means more in-front. - */ - public final int getZ() { - return z; - } - - /** - * Set Z order. Lower number means more in-front. - * - * @param z the new Z value. Lower number means more in-front. - */ - public final void setZ(final int z) { - this.z = z; - } - /** * Window's keyboard shortcuts. Any key in this set will be passed to * the window directly rather than processed through the menu * accelerators. */ - private HashSet keyboardShortcuts = new HashSet(); - - /** - * Add a keypress to be overridden for this window. - * - * @param key the key to start taking control of - */ - protected void addShortcutKeypress(final TKeypress key) { - keyboardShortcuts.add(key); - } - - /** - * Remove a keypress to be overridden for this window. - * - * @param key the key to stop taking control of - */ - protected void removeShortcutKeypress(final TKeypress key) { - keyboardShortcuts.remove(key); - } - - /** - * Remove all keypresses to be overridden for this window. - */ - protected void clearShortcutKeypresses() { - keyboardShortcuts.clear(); - } - - /** - * Determine if a keypress is overridden for this window. - * - * @param key the key to check - * @return true if this window wants to process this key on its own - */ - public boolean isShortcutKeypress(final TKeypress key) { - return keyboardShortcuts.contains(key); - } - - /** - * A window may have a status bar associated with it. TApplication will - * draw this status bar last, and will also route events to it first - * before the window. - */ - protected TStatusBar statusBar = null; - - /** - * Get the window's status bar, or null if it does not have one. - * - * @return the status bar, or null - */ - public TStatusBar getStatusBar() { - return statusBar; - } - - /** - * Set the window's status bar to a new one. - * - * @param text the status bar text - * @return the status bar - */ - public TStatusBar newStatusBar(final String text) { - statusBar = new TStatusBar(this, text); - return statusBar; - } - - // ------------------------------------------------------------------------ - // Window movement/resizing support --------------------------------------- - // ------------------------------------------------------------------------ + private Set keyboardShortcuts = new HashSet(); /** * If true, then the user clicked on the title bar and is moving the @@ -246,7 +143,7 @@ public class TWindow extends TWidget { * If true, then the user selected "Size/Move" (or hit Ctrl-F5) and is * resizing/moving the window via the keyboard. */ - private boolean inKeyboardResize = false; + protected boolean inKeyboardResize = false; /** * If true, this window is maximized. @@ -278,72 +175,6 @@ public class TWindow extends TWidget { private int restoreWindowX; private int restoreWindowY; - /** - * Set the maximum width for this window. - * - * @param maximumWindowWidth new maximum width - */ - public final void setMaximumWindowWidth(final int maximumWindowWidth) { - this.maximumWindowWidth = maximumWindowWidth; - } - - /** - * Recenter the window on-screen. - */ - public final void center() { - if ((flags & CENTERED) != 0) { - if (getWidth() < getScreen().getWidth()) { - setX((getScreen().getWidth() - getWidth()) / 2); - } else { - setX(0); - } - setY(((application.getDesktopBottom() - - application.getDesktopTop()) - getHeight()) / 2); - if (getY() < 0) { - setY(0); - } - setY(getY() + application.getDesktopTop()); - } - } - - /** - * Maximize window. - */ - public void maximize() { - if (maximized) { - return; - } - - restoreWindowWidth = getWidth(); - restoreWindowHeight = getHeight(); - restoreWindowX = getX(); - restoreWindowY = getY(); - setWidth(getScreen().getWidth()); - setHeight(application.getDesktopBottom() - 1); - setX(0); - setY(1); - maximized = true; - } - - /** - * Restore (unmaximize) window. - */ - public void restore() { - if (!maximized) { - return; - } - - setWidth(restoreWindowWidth); - setHeight(restoreWindowHeight); - setX(restoreWindowX); - setY(restoreWindowY); - maximized = false; - } - - // ------------------------------------------------------------------------ - // Window visibility ------------------------------------------------------ - // ------------------------------------------------------------------------ - /** * Hidden flag. A hidden window will still have its onIdle() called, and * will also have onClose() called at application exit. Note package @@ -353,53 +184,25 @@ public class TWindow extends TWidget { boolean hidden = false; /** - * Returns true if this window is hidden. - * - * @return true if this window is hidden, false if the window is shown - */ - public final boolean isHidden() { - return hidden; - } - - /** - * Returns true if this window is shown. - * - * @return true if this window is shown, false if the window is hidden - */ - public final boolean isShown() { - return !hidden; - } - - /** - * Hide window. A hidden window will still have its onIdle() called, and - * will also have onClose() called at application exit. Hidden windows - * will not receive any other events. - */ - public void hide() { - application.hideWindow(this); - } - - /** - * Show window. + * A window may have a status bar associated with it. TApplication will + * draw this status bar last, and will also route events to it first + * before the window. */ - public void show() { - application.showWindow(this); - } + protected TStatusBar statusBar = null; /** - * Activate window (bring to top and receive events). + * A window may request that TApplication NOT draw the mouse cursor over + * it by setting this to true. This is currently only used within Jexer + * by TTerminalWindow so that only the bottom-most instance of nested + * Jexer's draws the mouse within its application window. But perhaps + * other applications can use it, so public getter/setter is provided. */ - public void activate() { - application.activateWindow(this); - } + private boolean hideMouse = false; /** - * Close window. Note that windows without a close box can still be - * closed by calling the close() method. + * The help topic for this window. */ - public void close() { - application.closeWindow(this); - } + protected String helpTopic = "Help"; // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- @@ -489,218 +292,40 @@ public class TWindow extends TWidget { center(); // Add me to the application - application.addWindow(this); + application.addWindowToApplication(this); } // ------------------------------------------------------------------------ - // General behavior ------------------------------------------------------- + // Event handlers --------------------------------------------------------- // ------------------------------------------------------------------------ /** - * Returns true if this window is modal. + * Returns true if the mouse is currently on the close button. * - * @return true if this window is modal + * @return true if mouse is currently on the close button */ - public final boolean isModal() { - if ((flags & MODAL) == 0) { + protected boolean mouseOnClose() { + if ((flags & NOCLOSEBOX) != 0) { return false; } - return true; - } - - /** - * Returns true if this window has a close box. - * - * @return true if this window has a close box - */ - public final boolean hasCloseBox() { - if ((flags & NOCLOSEBOX) != 0) { + if ((mouse != null) + && (mouse.getAbsoluteY() == getY()) + && (mouse.getAbsoluteX() == getX() + 3) + ) { return true; } return false; } /** - * Retrieve the background color. - * - * @return the background color - */ - public final CellAttributes getBackground() { - if (!isModal() - && (inWindowMove || inWindowResize || inKeyboardResize) - ) { - assert (isActive()); - return getTheme().getColor("twindow.background.windowmove"); - } else if (isModal() && inWindowMove) { - assert (isActive()); - return getTheme().getColor("twindow.background.modal"); - } else if (isModal()) { - if (isActive()) { - return getTheme().getColor("twindow.background.modal"); - } - return getTheme().getColor("twindow.background.modal.inactive"); - } else if (isActive()) { - assert (!isModal()); - return getTheme().getColor("twindow.background"); - } else { - assert (!isModal()); - return getTheme().getColor("twindow.background.inactive"); - } - } - - /** - * Retrieve the border color. - * - * @return the border color - */ - public CellAttributes getBorder() { - if (!isModal() - && (inWindowMove || inWindowResize || inKeyboardResize) - ) { - assert (isActive()); - return getTheme().getColor("twindow.border.windowmove"); - } else if (isModal() && inWindowMove) { - assert (isActive()); - return getTheme().getColor("twindow.border.modal.windowmove"); - } else if (isModal()) { - if (isActive()) { - return getTheme().getColor("twindow.border.modal"); - } else { - return getTheme().getColor("twindow.border.modal.inactive"); - } - } else if (isActive()) { - assert (!isModal()); - return getTheme().getColor("twindow.border"); - } else { - assert (!isModal()); - return getTheme().getColor("twindow.border.inactive"); - } - } - - /** - * Retrieve the border line type. - * - * @return the border line type - */ - private int getBorderType() { - if (!isModal() - && (inWindowMove || inWindowResize || inKeyboardResize) - ) { - assert (isActive()); - return 1; - } else if (isModal() && inWindowMove) { - assert (isActive()); - return 1; - } else if (isModal()) { - if (isActive()) { - return 2; - } else { - return 1; - } - } else if (isActive()) { - return 2; - } else { - return 1; - } - } - - /** - * Called by TApplication.drawChildren() to render on screen. - */ - @Override - public void draw() { - // Draw the box and background first. - CellAttributes border = getBorder(); - CellAttributes background = getBackground(); - int borderType = getBorderType(); - - getScreen().drawBox(0, 0, getWidth(), getHeight(), border, - background, borderType, true); - - // Draw the title - int titleLeft = (getWidth() - title.length() - 2) / 2; - putCharXY(titleLeft, 0, ' ', border); - putStringXY(titleLeft + 1, 0, title); - putCharXY(titleLeft + title.length() + 1, 0, ' ', border); - - if (isActive()) { - - // Draw the close button - if ((flags & NOCLOSEBOX) == 0) { - putCharXY(2, 0, '[', border); - putCharXY(4, 0, ']', border); - if (mouseOnClose() && mouse.isMouse1()) { - putCharXY(3, 0, GraphicsChars.CP437[0x0F], - !isModal() - ? getTheme().getColor("twindow.border.windowmove") - : getTheme().getColor("twindow.border.modal.windowmove")); - } else { - putCharXY(3, 0, GraphicsChars.CP437[0xFE], - !isModal() - ? getTheme().getColor("twindow.border.windowmove") - : getTheme().getColor("twindow.border.modal.windowmove")); - } - } - - // Draw the maximize button - if (!isModal()) { - - putCharXY(getWidth() - 5, 0, '[', border); - putCharXY(getWidth() - 3, 0, ']', border); - if (mouseOnMaximize() && mouse.isMouse1()) { - putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x0F], - getTheme().getColor("twindow.border.windowmove")); - } else { - if (maximized) { - putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x12], - getTheme().getColor("twindow.border.windowmove")); - } else { - putCharXY(getWidth() - 4, 0, GraphicsChars.UPARROW, - getTheme().getColor("twindow.border.windowmove")); - } - } - - // Draw the resize corner - if ((flags & RESIZABLE) != 0) { - putCharXY(getWidth() - 2, getHeight() - 1, - GraphicsChars.SINGLE_BAR, - getTheme().getColor("twindow.border.windowmove")); - putCharXY(getWidth() - 1, getHeight() - 1, - GraphicsChars.LRCORNER, - getTheme().getColor("twindow.border.windowmove")); - } - } - } - } - - // ------------------------------------------------------------------------ - // Event handlers --------------------------------------------------------- - // ------------------------------------------------------------------------ - - /** - * Returns true if the mouse is currently on the close button. - * - * @return true if mouse is currently on the close button - */ - protected boolean mouseOnClose() { - if ((flags & NOCLOSEBOX) != 0) { - return false; - } - if ((mouse != null) - && (mouse.getAbsoluteY() == getY()) - && (mouse.getAbsoluteX() == getX() + 3) - ) { - return true; - } - return false; - } - - /** - * Returns true if the mouse is currently on the maximize/restore button. + * Returns true if the mouse is currently on the maximize/restore button. * * @return true if the mouse is currently on the maximize/restore button */ protected boolean mouseOnMaximize() { + if ((flags & NOZOOMBOX) != 0) { + return false; + } if ((mouse != null) && !isModal() && (mouse.getAbsoluteY() == getY()) @@ -731,19 +356,32 @@ public class TWindow extends TWidget { return false; } + /** + * Subclasses should override this method to perform any user prompting + * before they are offscreen. Note that unlike other windowing toolkits, + * windows can NOT use this function in some manner to avoid being + * closed. This is called by application.closeWindow(). + */ + protected void onPreClose() { + // Default: do nothing. + } + /** * Subclasses should override this method to cleanup resources. This is * called by application.closeWindow(). */ - public void onClose() { - // Default: do nothing + protected void onClose() { + // Default: perform widget-specific cleanup. + for (TWidget w: getChildren()) { + w.close(); + } } /** * Called by application.switchWindow() when this window gets the * focus, and also by application.addWindow(). */ - public void onFocus() { + protected void onFocus() { // Default: do nothing } @@ -751,21 +389,21 @@ public class TWindow extends TWidget { * Called by application.switchWindow() when another window gets the * focus. */ - public void onUnfocus() { + protected void onUnfocus() { // Default: do nothing } /** * Called by application.hideWindow(). */ - public void onHide() { + protected void onHide() { // Default: do nothing } /** * Called by application.showWindow(). */ - public void onShow() { + protected void onShow() { // Default: do nothing } @@ -779,6 +417,8 @@ public class TWindow extends TWidget { this.mouse = mouse; inKeyboardResize = false; + inWindowMove = false; + inWindowResize = false; if ((mouse.getAbsoluteY() == getY()) && mouse.isMouse1() @@ -844,8 +484,13 @@ public class TWindow extends TWidget { } if (mouse.isMouse1() && mouseOnClose()) { - // Close window - application.closeWindow(this); + if ((flags & HIDEONCLOSE) == 0) { + // Close window + application.closeWindow(this); + } else { + // Hide window + application.hideWindow(this); + } return; } @@ -920,23 +565,22 @@ public class TWindow extends TWidget { // Keep within min/max bounds if (getWidth() < minimumWindowWidth) { setWidth(minimumWindowWidth); - inWindowResize = false; } if (getHeight() < minimumWindowHeight) { setHeight(minimumWindowHeight); - inWindowResize = false; } if ((maximumWindowWidth > 0) && (getWidth() > maximumWindowWidth) ) { setWidth(maximumWindowWidth); - inWindowResize = false; } if ((maximumWindowHeight > 0) && (getHeight() > maximumWindowHeight) ) { setHeight(maximumWindowHeight); - inWindowResize = false; + } + if (getHeight() + getY() >= getApplication().getDesktopBottom()) { + setHeight(getApplication().getDesktopBottom() - getY()); } // Pass a resize event to my children @@ -962,6 +606,15 @@ public class TWindow extends TWidget { @Override public void onKeypress(final TKeypressEvent keypress) { + if (inWindowMove || inWindowResize) { + // ESC or ENTER - Exit size/move + if (keypress.equals(kbEsc) || keypress.equals(kbEnter)) { + inWindowMove = false; + inWindowResize = false; + return; + } + } + if (inKeyboardResize) { // ESC or ENTER - Exit size/move @@ -1049,7 +702,13 @@ public class TWindow extends TWidget { // Ctrl-W - close window if (keypress.equals(kbCtrlW)) { if ((flags & NOCLOSEBOX) == 0) { - application.closeWindow(this); + if ((flags & HIDEONCLOSE) == 0) { + // Close window + application.closeWindow(this); + } else { + // Hide window + application.hideWindow(this); + } } return; } @@ -1067,7 +726,7 @@ public class TWindow extends TWidget { } // F5 - zoom - if (keypress.equals(kbF5)) { + if (keypress.equals(kbF5) && ((flags & NOZOOMBOX) == 0)) { if (maximized) { restore(); } else { @@ -1102,7 +761,13 @@ public class TWindow extends TWidget { if (command.equals(cmWindowClose)) { if ((flags & NOCLOSEBOX) == 0) { - application.closeWindow(this); + if ((flags & HIDEONCLOSE) == 0) { + // Close window + application.closeWindow(this); + } else { + // Hide window + application.hideWindow(this); + } } return; } @@ -1122,7 +787,7 @@ public class TWindow extends TWidget { return; } - if (command.equals(cmWindowZoom)) { + if (command.equals(cmWindowZoom) && ((flags & NOZOOMBOX) == 0)) { if (maximized) { restore(); } else { @@ -1148,7 +813,13 @@ public class TWindow extends TWidget { if (menu.getId() == TMenu.MID_WINDOW_CLOSE) { if ((flags & NOCLOSEBOX) == 0) { - application.closeWindow(this); + if ((flags & HIDEONCLOSE) == 0) { + // Close window + application.closeWindow(this); + } else { + // Hide window + application.hideWindow(this); + } } return; } @@ -1168,7 +839,9 @@ public class TWindow extends TWidget { return; } - if (menu.getId() == TMenu.MID_WINDOW_ZOOM) { + if ((menu.getId() == TMenu.MID_WINDOW_ZOOM) + && ((flags & NOZOOMBOX) == 0) + ) { if (maximized) { restore(); } else { @@ -1183,149 +856,608 @@ public class TWindow extends TWidget { super.onMenu(menu); } - // ------------------------------------------------------------------------ - // Passthru for Screen functions ------------------------------------------ - // ------------------------------------------------------------------------ - - /** - * Get the attributes at one location. - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @return attributes at (x, y) - */ - public final CellAttributes getAttrXY(final int x, final int y) { - return getScreen().getAttrXY(x, y); - } - /** - * Set the attributes at one location. + * Method that subclasses can override to handle window/screen resize + * events. * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param attr attributes to use (bold, foreColor, backColor) + * @param resize resize event */ - public final void putAttrXY(final int x, final int y, - final CellAttributes attr) { + @Override + public void onResize(final TResizeEvent resize) { + if (resize.getType() == TResizeEvent.Type.WIDGET) { + if (getChildren().size() == 1) { + TWidget child = getChildren().get(0); + if ((child instanceof TSplitPane) + || (child instanceof TPanel) + ) { + if (this instanceof TDesktop) { + child.onResize(new TResizeEvent( + TResizeEvent.Type.WIDGET, + resize.getWidth(), resize.getHeight())); + } else { + child.onResize(new TResizeEvent( + TResizeEvent.Type.WIDGET, + resize.getWidth() - 2, resize.getHeight() - 2)); + } + } + return; + } + } - getScreen().putAttrXY(x, y, attr); + // Pass on to TWidget. + super.onResize(resize); } - /** - * Set the attributes at one location. - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param attr attributes to use (bold, foreColor, backColor) - * @param clip if true, honor clipping/offset - */ - public final void putAttrXY(final int x, final int y, - final CellAttributes attr, final boolean clip) { - - getScreen().putAttrXY(x, y, attr, clip); - } + // ------------------------------------------------------------------------ + // TWidget ---------------------------------------------------------------- + // ------------------------------------------------------------------------ /** - * Fill the entire screen with one character with attributes. + * Get this TWindow's parent TApplication. * - * @param ch character to draw - * @param attr attributes to use (bold, foreColor, backColor) + * @return this TWindow's parent TApplication */ - public final void putAll(final char ch, final CellAttributes attr) { - getScreen().putAll(ch, attr); + @Override + public final TApplication getApplication() { + return application; } /** - * Render one character with attributes. + * Get the Screen. * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param ch character + attributes to draw + * @return the Screen */ - public final void putCharXY(final int x, final int y, final Cell ch) { - getScreen().putCharXY(x, y, ch); + @Override + public final Screen getScreen() { + return application.getScreen(); } /** - * Render one character with attributes. - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param ch character to draw - * @param attr attributes to use (bold, foreColor, backColor) + * Called by TApplication.drawChildren() to render on screen. */ - public final void putCharXY(final int x, final int y, final char ch, - final CellAttributes attr) { - - getScreen().putCharXY(x, y, ch, attr); - } + @Override + public void draw() { + // Draw the box and background first. + CellAttributes border = getBorder(); + CellAttributes background = getBackground(); + int borderType = getBorderType(); - /** - * Render one character without changing the underlying attributes. - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param ch character to draw - */ - public final void putCharXY(final int x, final int y, final char ch) { - getScreen().putCharXY(x, y, ch); - } + drawBox(0, 0, getWidth(), getHeight(), border, background, borderType, + true); - /** - * Render a string. Does not wrap if the string exceeds the line. - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param str string to draw - * @param attr attributes to use (bold, foreColor, backColor) - */ - public final void putStringXY(final int x, final int y, final String str, - final CellAttributes attr) { + // Draw the title + int titleLength = StringUtils.width(title); + int titleLeft = (getWidth() - titleLength - 2) / 2; + putCharXY(titleLeft, 0, ' ', border); + putStringXY(titleLeft + 1, 0, title, border); + putCharXY(titleLeft + titleLength + 1, 0, ' ', border); - getScreen().putStringXY(x, y, str, attr); - } + if (isActive()) { - /** - * Render a string without changing the underlying attribute. Does not - * wrap if the string exceeds the line. - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param str string to draw - */ - public final void putStringXY(final int x, final int y, final String str) { - getScreen().putStringXY(x, y, str); - } + // Draw the close button + if ((flags & NOCLOSEBOX) == 0) { + putCharXY(2, 0, '[', border); + putCharXY(4, 0, ']', border); + if (mouseOnClose() && mouse.isMouse1()) { + putCharXY(3, 0, GraphicsChars.CP437[0x0F], + getBorderControls()); + } else { + putCharXY(3, 0, GraphicsChars.CP437[0xFE], + getBorderControls()); + } + } - /** - * Draw a vertical line from (x, y) to (x, y + n). - * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param n number of characters to draw - * @param ch character to draw - * @param attr attributes to use (bold, foreColor, backColor) - */ - public final void vLineXY(final int x, final int y, final int n, - final char ch, final CellAttributes attr) { + // Draw the maximize button + if (!isModal() && ((flags & NOZOOMBOX) == 0)) { - getScreen().vLineXY(x, y, n, ch, attr); - } + putCharXY(getWidth() - 5, 0, '[', border); + putCharXY(getWidth() - 3, 0, ']', border); + if (mouseOnMaximize() && mouse.isMouse1()) { + putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x0F], + getBorderControls()); + } else { + if (maximized) { + putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x12], + getBorderControls()); + } else { + putCharXY(getWidth() - 4, 0, GraphicsChars.UPARROW, + getBorderControls()); + } + } + + // Draw the resize corner + if ((flags & RESIZABLE) != 0) { + putCharXY(getWidth() - 2, getHeight() - 1, + GraphicsChars.SINGLE_BAR, getBorderControls()); + putCharXY(getWidth() - 1, getHeight() - 1, + GraphicsChars.LRCORNER, getBorderControls()); + } + } + } + } + + // ------------------------------------------------------------------------ + // TWindow ---------------------------------------------------------------- + // ------------------------------------------------------------------------ + + /** + * Get window title. + * + * @return window title + */ + public final String getTitle() { + return title; + } + + /** + * Set window title. + * + * @param title new window title + */ + public final void setTitle(final String title) { + this.title = title; + } + + /** + * Get Z order. Lower number means more in-front. + * + * @return Z value. Lower number means more in-front. + */ + public final int getZ() { + return z; + } + + /** + * Set Z order. Lower number means more in-front. + * + * @param z the new Z value. Lower number means more in-front. + */ + public final void setZ(final int z) { + this.z = z; + } + + /** + * Add a keypress to be overridden for this window. + * + * @param key the key to start taking control of + */ + protected void addShortcutKeypress(final TKeypress key) { + keyboardShortcuts.add(key); + } + + /** + * Remove a keypress to be overridden for this window. + * + * @param key the key to stop taking control of + */ + protected void removeShortcutKeypress(final TKeypress key) { + keyboardShortcuts.remove(key); + } + + /** + * Remove all keypresses to be overridden for this window. + */ + protected void clearShortcutKeypresses() { + keyboardShortcuts.clear(); + } + + /** + * Determine if a keypress is overridden for this window. + * + * @param key the key to check + * @return true if this window wants to process this key on its own + */ + public boolean isShortcutKeypress(final TKeypress key) { + return keyboardShortcuts.contains(key); + } + + /** + * Get the window's status bar, or null if it does not have one. + * + * @return the status bar, or null + */ + public TStatusBar getStatusBar() { + return statusBar; + } /** - * Draw a horizontal line from (x, y) to (x + n, y). + * Set the window's status bar to a new one. + * + * @param text the status bar text + * @return the status bar + */ + public TStatusBar newStatusBar(final String text) { + statusBar = new TStatusBar(this, text); + return statusBar; + } + + /** + * Set the maximum width for this window. + * + * @param maximumWindowWidth new maximum width + */ + public final void setMaximumWindowWidth(final int maximumWindowWidth) { + if ((maximumWindowWidth != -1) + && (maximumWindowWidth < minimumWindowWidth + 1) + ) { + throw new IllegalArgumentException("Maximum window width cannot " + + "be smaller than minimum window width + 1"); + } + this.maximumWindowWidth = maximumWindowWidth; + } + + /** + * Set the minimum width for this window. + * + * @param minimumWindowWidth new minimum width + */ + public final void setMinimumWindowWidth(final int minimumWindowWidth) { + if ((maximumWindowWidth != -1) + && (minimumWindowWidth > maximumWindowWidth - 1) + ) { + throw new IllegalArgumentException("Minimum window width cannot " + + "be larger than maximum window width - 1"); + } + this.minimumWindowWidth = minimumWindowWidth; + } + + /** + * Set the maximum height for this window. * - * @param x column coordinate. 0 is the left-most column. - * @param y row coordinate. 0 is the top-most row. - * @param n number of characters to draw - * @param ch character to draw - * @param attr attributes to use (bold, foreColor, backColor) + * @param maximumWindowHeight new maximum height */ - public final void hLineXY(final int x, final int y, final int n, - final char ch, final CellAttributes attr) { + public final void setMaximumWindowHeight(final int maximumWindowHeight) { + if ((maximumWindowHeight != -1) + && (maximumWindowHeight < minimumWindowHeight + 1) + ) { + throw new IllegalArgumentException("Maximum window height cannot " + + "be smaller than minimum window height + 1"); + } + this.maximumWindowHeight = maximumWindowHeight; + } - getScreen().hLineXY(x, y, n, ch, attr); + /** + * Set the minimum height for this window. + * + * @param minimumWindowHeight new minimum height + */ + public final void setMinimumWindowHeight(final int minimumWindowHeight) { + if ((maximumWindowHeight != -1) + && (minimumWindowHeight > maximumWindowHeight - 1) + ) { + throw new IllegalArgumentException("Minimum window height cannot " + + "be larger than maximum window height - 1"); + } + this.minimumWindowHeight = minimumWindowHeight; } + /** + * Recenter the window on-screen. + */ + public final void center() { + if ((flags & CENTERED) != 0) { + if (getWidth() < getScreen().getWidth()) { + setX((getScreen().getWidth() - getWidth()) / 2); + } else { + setX(0); + } + setY(((application.getDesktopBottom() + - application.getDesktopTop()) - getHeight()) / 2); + if (getY() < 0) { + setY(0); + } + setY(getY() + application.getDesktopTop()); + } + } + + /** + * Maximize window. + */ + public void maximize() { + if (maximized) { + return; + } + + restoreWindowWidth = getWidth(); + restoreWindowHeight = getHeight(); + restoreWindowX = getX(); + restoreWindowY = getY(); + setWidth(getScreen().getWidth()); + setHeight(application.getDesktopBottom() - application.getDesktopTop()); + setX(0); + setY(application.getDesktopTop()); + maximized = true; + + onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(), + getHeight())); + } + + /** + * Restore (unmaximize) window. + */ + public void restore() { + if (!maximized) { + return; + } + + setWidth(restoreWindowWidth); + setHeight(restoreWindowHeight); + setX(restoreWindowX); + setY(restoreWindowY); + maximized = false; + + onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(), + getHeight())); + } + + /** + * Returns true if this window is hidden. + * + * @return true if this window is hidden, false if the window is shown + */ + public final boolean isHidden() { + return hidden; + } + + /** + * Returns true if this window is shown. + * + * @return true if this window is shown, false if the window is hidden + */ + public final boolean isShown() { + return !hidden; + } + + /** + * Hide window. A hidden window will still have its onIdle() called, and + * will also have onClose() called at application exit. Hidden windows + * will not receive any other events. + */ + public void hide() { + application.hideWindow(this); + } + + /** + * Show window. + */ + public void show() { + application.showWindow(this); + } + + /** + * Activate window (bring to top and receive events). + */ + @Override + public void activate() { + application.activateWindow(this); + } + + /** + * Close window. Note that windows without a close box can still be + * closed by calling the close() method. + */ + @Override + public void close() { + application.closeWindow(this); + } + + /** + * See if this window is undergoing any movement/resize/etc. + * + * @return true if the window is moving + */ + public boolean inMovements() { + if (inWindowResize || inWindowMove || inKeyboardResize) { + return true; + } + return false; + } + + /** + * Stop any pending movement/resize/etc. + */ + public void stopMovements() { + inWindowResize = false; + inWindowMove = false; + inKeyboardResize = false; + } + + /** + * Returns true if this window is modal. + * + * @return true if this window is modal + */ + public final boolean isModal() { + if ((flags & MODAL) == 0) { + return false; + } + return true; + } + + /** + * Returns true if this window has a close box. + * + * @return true if this window has a close box + */ + public final boolean hasCloseBox() { + if ((flags & NOCLOSEBOX) != 0) { + return true; + } + return false; + } + + /** + * Returns true if this window has a maximize/zoom box. + * + * @return true if this window has a maximize/zoom box + */ + public final boolean hasZoomBox() { + if ((flags & NOZOOMBOX) != 0) { + return true; + } + return false; + } + + /** + * Returns true if this window does not want menus to work while it is + * visible. + * + * @return true if this window does not want menus to work while it is + * visible + */ + public final boolean hasOverriddenMenu() { + if ((flags & OVERRIDEMENU) != 0) { + return true; + } + return false; + } + + /** + * Retrieve the background color. + * + * @return the background color + */ + public CellAttributes getBackground() { + if (!isModal() + && (inWindowMove || inWindowResize || inKeyboardResize) + ) { + assert (isActive()); + return getTheme().getColor("twindow.background.windowmove"); + } else if (isModal() && inWindowMove) { + assert (isActive()); + return getTheme().getColor("twindow.background.modal"); + } else if (isModal()) { + if (isActive()) { + return getTheme().getColor("twindow.background.modal"); + } + return getTheme().getColor("twindow.background.modal.inactive"); + } else if (isActive()) { + assert (!isModal()); + return getTheme().getColor("twindow.background"); + } else { + assert (!isModal()); + return getTheme().getColor("twindow.background.inactive"); + } + } + + /** + * Retrieve the border color. + * + * @return the border color + */ + public CellAttributes getBorder() { + if (!isModal() + && (inWindowMove || inWindowResize || inKeyboardResize) + ) { + if (!isActive()) { + // The user's terminal never passed a mouse up event, and now + // another window is active but we never finished a drag. + inWindowMove = false; + inWindowResize = false; + inKeyboardResize = false; + return getTheme().getColor("twindow.border.inactive"); + } + + return getTheme().getColor("twindow.border.windowmove"); + } else if (isModal() && inWindowMove) { + assert (isActive()); + return getTheme().getColor("twindow.border.modal.windowmove"); + } else if (isModal()) { + if (isActive()) { + return getTheme().getColor("twindow.border.modal"); + } else { + return getTheme().getColor("twindow.border.modal.inactive"); + } + } else if (isActive()) { + assert (!isModal()); + return getTheme().getColor("twindow.border"); + } else { + assert (!isModal()); + return getTheme().getColor("twindow.border.inactive"); + } + } + + /** + * Retrieve the color used by the window movement/sizing controls. + * + * @return the color used by the zoom box, resize bar, and close box + */ + public CellAttributes getBorderControls() { + if (isModal()) { + return getTheme().getColor("twindow.border.modal.windowmove"); + } + return getTheme().getColor("twindow.border.windowmove"); + } + + /** + * Retrieve the border line type. + * + * @return the border line type + */ + private int getBorderType() { + if (!isModal() + && (inWindowMove || inWindowResize || inKeyboardResize) + ) { + assert (isActive()); + return 1; + } else if (isModal() && inWindowMove) { + assert (isActive()); + return 1; + } else if (isModal()) { + if (isActive()) { + return 2; + } else { + return 1; + } + } else if (isActive()) { + return 2; + } else { + return 1; + } + } + + /** + * Returns true if this window does not want the application-wide mouse + * cursor drawn over it. + * + * @return true if this window does not want the application-wide mouse + * cursor drawn over it + */ + public boolean hasHiddenMouse() { + return hideMouse; + } + + /** + * Set request to prevent the application-wide mouse cursor from being + * drawn over this window. + * + * @param hideMouse if true, this window does not want the + * application-wide mouse cursor drawn over it + */ + public final void setHiddenMouse(final boolean hideMouse) { + this.hideMouse = hideMouse; + } + + /** + * Get this window's help topic to load. + * + * @return the topic name + */ + public String getHelpTopic() { + return helpTopic; + } + + /** + * Generate a human-readable string for this window. + * + * @return a human-readable string + */ + @Override + public String toString() { + return String.format("%s(%8x) \'%s\' Z %d position (%d, %d) " + + "geometry %dx%d hidden %s modal %s", + getClass().getName(), hashCode(), title, getZ(), + getX(), getY(), getWidth(), getHeight(), hidden, isModal()); + } }