more TEditor stubs
[nikiroo-utils.git] / src / jexer / TWindow.java
index 10f4e59d5df5d946a5d7032475a590552bbdf7ac..32907a8a9c9046ec4329182623b0137c72391086 100644 (file)
@@ -3,7 +3,7 @@
  *
  * The MIT License (MIT)
  *
- * Copyright (C) 2016 Kevin Lamonte
+ * Copyright (C) 2017 Kevin Lamonte
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -28,6 +28,9 @@
  */
 package jexer;
 
+import java.util.HashSet;
+
+import jexer.backend.Screen;
 import jexer.bits.Cell;
 import jexer.bits.CellAttributes;
 import jexer.bits.GraphicsChars;
@@ -36,7 +39,6 @@ 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.*;
@@ -46,30 +48,39 @@ import static jexer.TKeypress.*;
  */
 public class TWindow extends TWidget {
 
+    // ------------------------------------------------------------------------
+    // Public constants -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
-     * Window's parent TApplication.
+     * Window is resizable (default yes).
      */
-    private TApplication application;
+    public static final int RESIZABLE   = 0x01;
 
     /**
-     * Get this TWindow's parent TApplication.
-     *
-     * @return this TWindow's parent TApplication
+     * Window is modal (default no).
      */
-    @Override
-    public final TApplication getApplication() {
-        return application;
-    }
+    public static final int MODAL       = 0x02;
 
     /**
-     * Get the Screen.
-     *
-     * @return the Screen
+     * Window is centered (default no).
      */
-    @Override
-    public final Screen getScreen() {
-        return application.getScreen();
-    }
+    public static final int CENTERED    = 0x04;
+
+    /**
+     * Window has no close box (default no).  Window can still be closed via
+     * TApplication.closeWindow() and TWindow.close().
+     */
+    public static final int NOCLOSEBOX  = 0x08;
+
+    // ------------------------------------------------------------------------
+    // Common window attributes -----------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Window flags.  Note package private access.
+     */
+    int flags = RESIZABLE;
 
     /**
      * Window title.
@@ -94,25 +105,34 @@ public class TWindow extends TWidget {
         this.title = title;
     }
 
-    /**
-     * Window is resizable (default yes).
-     */
-    public static final int RESIZABLE   = 0x01;
+    // ------------------------------------------------------------------------
+    // TApplication integration -----------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
-     * Window is modal (default no).
+     * Window's parent TApplication.
      */
-    public static final int MODAL       = 0x02;
+    private TApplication application;
 
     /**
-     * Window is centered (default no).
+     * Get this TWindow's parent TApplication.
+     *
+     * @return this TWindow's parent TApplication
      */
-    public static final int CENTERED    = 0x04;
+    @Override
+    public final TApplication getApplication() {
+        return application;
+    }
 
     /**
-     * Window flags.
+     * Get the Screen.
+     *
+     * @return the Screen
      */
-    private int flags = RESIZABLE;
+    @Override
+    public final Screen getScreen() {
+        return application.getScreen();
+    }
 
     /**
      * Z order.  Lower number means more in-front.
@@ -137,6 +157,79 @@ public class TWindow extends TWidget {
         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<TKeypress> keyboardShortcuts = new HashSet<TKeypress>();
+
+    /**
+     * 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 ---------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * If true, then the user clicked on the title bar and is moving the
      * window.
@@ -194,6 +287,124 @@ public class TWindow extends TWidget {
         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
+     * private access: TApplication will force hidden false if a modal window
+     * is active.
+     */
+    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.
+     */
+    public void show() {
+        application.showWindow(this);
+    }
+
+    /**
+     * Activate window (bring to top and receive events).
+     */
+    public void activate() {
+        application.activateWindow(this);
+    }
+
+    /**
+     * Close window.  Note that windows without a close box can still be
+     * closed by calling the close() method.
+     */
+    public void close() {
+        application.closeWindow(this);
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Public constructor.  Window will be located at (0, 0).
      *
@@ -281,24 +492,9 @@ public class TWindow extends TWidget {
         application.addWindow(this);
     }
 
-    /**
-     * 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());
-        }
-    }
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Returns true if this window is modal.
@@ -313,51 +509,12 @@ public class TWindow extends TWidget {
     }
 
     /**
-     * Returns true if the mouse is currently on the close button.
+     * Returns true if this window has a close box.
      *
-     * @return true if mouse is currently on the close button
+     * @return true if this window has a close box
      */
-    private boolean mouseOnClose() {
-        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.
-     *
-     * @return true if the mouse is currently on the maximize/restore button
-     */
-    private boolean mouseOnMaximize() {
-        if ((mouse != null)
-            && !isModal()
-            && (mouse.getAbsoluteY() == getY())
-            && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
-        ) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the mouse is currently on the resizable lower right
-     * corner.
-     *
-     * @return true if the mouse is currently on the resizable lower right
-     * corner
-     */
-    private boolean mouseOnResize() {
-        if (((flags & RESIZABLE) != 0)
-            && !isModal()
-            && (mouse != null)
-            && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
-            && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
-                || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
-        ) {
+    public final boolean hasCloseBox() {
+        if ((flags & NOCLOSEBOX) != 0) {
             return true;
         }
         return false;
@@ -447,30 +604,6 @@ public class TWindow extends TWidget {
         }
     }
 
-    /**
-     * Subclasses should override this method to cleanup resources.  This is
-     * called by application.closeWindow().
-     */
-    public void onClose() {
-        // Default: do nothing
-    }
-
-    /**
-     * Called by application.switchWindow() when this window gets the
-     * focus, and also by application.addWindow().
-     */
-    public void onFocus() {
-        // Default: do nothing
-    }
-
-    /**
-     * Called by application.switchWindow() when another window gets the
-     * focus.
-     */
-    public void onUnfocus() {
-        // Default: do nothing
-    }
-
     /**
      * Called by TApplication.drawChildren() to render on screen.
      */
@@ -493,18 +626,20 @@ public class TWindow extends TWidget {
         if (isActive()) {
 
             // Draw the close button
-            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"));
+            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
@@ -538,6 +673,102 @@ public class TWindow extends TWidget {
         }
     }
 
+    // ------------------------------------------------------------------------
+    // 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.
+     *
+     * @return true if the mouse is currently on the maximize/restore button
+     */
+    protected boolean mouseOnMaximize() {
+        if ((mouse != null)
+            && !isModal()
+            && (mouse.getAbsoluteY() == getY())
+            && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the resizable lower right
+     * corner.
+     *
+     * @return true if the mouse is currently on the resizable lower right
+     * corner
+     */
+    protected boolean mouseOnResize() {
+        if (((flags & RESIZABLE) != 0)
+            && !isModal()
+            && (mouse != null)
+            && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
+            && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
+                || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Subclasses should override this method to cleanup resources.  This is
+     * called by application.closeWindow().
+     */
+    public void onClose() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.switchWindow() when this window gets the
+     * focus, and also by application.addWindow().
+     */
+    public void onFocus() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.switchWindow() when another window gets the
+     * focus.
+     */
+    public void onUnfocus() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.hideWindow().
+     */
+    public void onHide() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.showWindow().
+     */
+    public void onShow() {
+        // Default: do nothing
+    }
+
     /**
      * Handle mouse button presses.
      *
@@ -580,36 +811,17 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarMouseDown(mouse)) {
+                return;
+            }
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseDown(mouse);
     }
 
-    /**
-     * Maximize window.
-     */
-    private void maximize() {
-        restoreWindowWidth = getWidth();
-        restoreWindowHeight = getHeight();
-        restoreWindowX = getX();
-        restoreWindowY = getY();
-        setWidth(getScreen().getWidth());
-        setHeight(application.getDesktopBottom() - 1);
-        setX(0);
-        setY(1);
-        maximized = true;
-    }
-
-    /**
-     * Restote (unmaximize) window.
-     */
-    private void restore() {
-        setWidth(restoreWindowWidth);
-        setHeight(restoreWindowHeight);
-        setX(restoreWindowX);
-        setY(restoreWindowY);
-        maximized = false;
-    }
-
     /**
      * Handle mouse button releases.
      *
@@ -653,6 +865,13 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarMouseUp(mouse)) {
+                return;
+            }
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseUp(mouse);
     }
@@ -674,10 +893,20 @@ public class TWindow extends TWidget {
             if (getY() < application.getDesktopTop()) {
                 setY(application.getDesktopTop());
             }
+            // Don't go below the status bar
+            if (getY() >= application.getDesktopBottom()) {
+                setY(application.getDesktopBottom() - 1);
+            }
             return;
         }
 
         if (inWindowResize) {
+            // Do not permit resizing below the status line
+            if (mouse.getAbsoluteY() == application.getDesktopBottom()) {
+                inWindowResize = false;
+                return;
+            }
+
             // Move window over
             setWidth(resizeWindowWidth + (mouse.getAbsoluteX()
                     - moveWindowMouseX));
@@ -722,6 +951,11 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            statusBar.statusBarMouseMotion(mouse);
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseMotion(mouse);
     }
@@ -736,8 +970,8 @@ public class TWindow extends TWidget {
 
         if (inKeyboardResize) {
 
-            // ESC - Exit size/move
-            if (keypress.equals(kbEsc)) {
+            // ESC or ENTER - Exit size/move
+            if (keypress.equals(kbEsc) || keypress.equals(kbEnter)) {
                 inKeyboardResize = false;
             }
 
@@ -761,77 +995,98 @@ public class TWindow extends TWidget {
                     setY(getY() - 1);
                 }
             }
-            if (keypress.equals(kbShiftLeft)) {
-                if ((getWidth() > minimumWindowWidth)
-                    || (minimumWindowWidth <= 0)
-                ) {
-                    setWidth(getWidth() - 1);
+
+            /*
+             * Only permit keyboard resizing if the window was RESIZABLE.
+             */
+            if ((flags & RESIZABLE) != 0) {
+
+                if (keypress.equals(kbShiftLeft)) {
+                    if ((getWidth() > minimumWindowWidth)
+                        || (minimumWindowWidth <= 0)
+                    ) {
+                        setWidth(getWidth() - 1);
+                    }
                 }
-            }
-            if (keypress.equals(kbShiftRight)) {
-                if ((getWidth() < maximumWindowWidth)
-                    || (maximumWindowWidth <= 0)
-                ) {
-                    setWidth(getWidth() + 1);
+                if (keypress.equals(kbShiftRight)) {
+                    if ((getWidth() < maximumWindowWidth)
+                        || (maximumWindowWidth <= 0)
+                    ) {
+                        setWidth(getWidth() + 1);
+                    }
                 }
-            }
-            if (keypress.equals(kbShiftUp)) {
-                if ((getHeight() > minimumWindowHeight)
-                    || (minimumWindowHeight <= 0)
-                ) {
-                    setHeight(getHeight() - 1);
+                if (keypress.equals(kbShiftUp)) {
+                    if ((getHeight() > minimumWindowHeight)
+                        || (minimumWindowHeight <= 0)
+                    ) {
+                        setHeight(getHeight() - 1);
+                    }
                 }
-            }
-            if (keypress.equals(kbShiftDown)) {
-                if ((getHeight() < maximumWindowHeight)
-                    || (maximumWindowHeight <= 0)
-                ) {
-                    setHeight(getHeight() + 1);
+                if (keypress.equals(kbShiftDown)) {
+                    if ((getHeight() < maximumWindowHeight)
+                        || (maximumWindowHeight <= 0)
+                    ) {
+                        setHeight(getHeight() + 1);
+                    }
                 }
-            }
 
-            // Pass a resize event to my children
-            onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
-                    getWidth(), getHeight()));
+                // Pass a resize event to my children
+                onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                        getWidth(), getHeight()));
+
+            } // if ((flags & RESIZABLE) != 0)
 
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarKeypress(keypress)) {
+                return;
+            }
+        }
+
         // These keystrokes will typically not be seen unless a subclass
         // overrides onMenu() due to how TApplication dispatches
         // accelerators.
 
-        // Ctrl-W - close window
-        if (keypress.equals(kbCtrlW)) {
-            application.closeWindow(this);
-            return;
-        }
+        if (!(this instanceof TDesktop)) {
 
-        // F6 - behave like Alt-TAB
-        if (keypress.equals(kbF6)) {
-            application.switchWindow(true);
-            return;
-        }
+            // Ctrl-W - close window
+            if (keypress.equals(kbCtrlW)) {
+                if ((flags & NOCLOSEBOX) == 0) {
+                    application.closeWindow(this);
+                }
+                return;
+            }
 
-        // Shift-F6 - behave like Shift-Alt-TAB
-        if (keypress.equals(kbShiftF6)) {
-            application.switchWindow(false);
-            return;
-        }
+            // F6 - behave like Alt-TAB
+            if (keypress.equals(kbF6)) {
+                application.switchWindow(true);
+                return;
+            }
 
-        // F5 - zoom
-        if (keypress.equals(kbF5)) {
-            if (maximized) {
-                restore();
-            } else {
-                maximize();
+            // Shift-F6 - behave like Shift-Alt-TAB
+            if (keypress.equals(kbShiftF6)) {
+                application.switchWindow(false);
+                return;
             }
-        }
 
-        // Ctrl-F5 - size/move
-        if (keypress.equals(kbCtrlF5)) {
-            inKeyboardResize = !inKeyboardResize;
-        }
+            // F5 - zoom
+            if (keypress.equals(kbF5)) {
+                if (maximized) {
+                    restore();
+                } else {
+                    maximize();
+                }
+            }
+
+            // Ctrl-F5 - size/move
+            if (keypress.equals(kbCtrlF5)) {
+                inKeyboardResize = !inKeyboardResize;
+            }
+
+        } // if (!(this instanceof TDesktop))
 
         // I didn't take it, pass it on to my children
         super.onKeypress(keypress);
@@ -849,33 +1104,39 @@ public class TWindow extends TWidget {
         // overrides onMenu() due to how TApplication dispatches
         // accelerators.
 
-        if (command.equals(cmWindowClose)) {
-            application.closeWindow(this);
-            return;
-        }
+        if (!(this instanceof TDesktop)) {
 
-        if (command.equals(cmWindowNext)) {
-            application.switchWindow(true);
-            return;
-        }
+            if (command.equals(cmWindowClose)) {
+                if ((flags & NOCLOSEBOX) == 0) {
+                    application.closeWindow(this);
+                }
+                return;
+            }
 
-        if (command.equals(cmWindowPrevious)) {
-            application.switchWindow(false);
-            return;
-        }
+            if (command.equals(cmWindowNext)) {
+                application.switchWindow(true);
+                return;
+            }
 
-        if (command.equals(cmWindowMove)) {
-            inKeyboardResize = true;
-            return;
-        }
+            if (command.equals(cmWindowPrevious)) {
+                application.switchWindow(false);
+                return;
+            }
 
-        if (command.equals(cmWindowZoom)) {
-            if (maximized) {
-                restore();
-            } else {
-                maximize();
+            if (command.equals(cmWindowMove)) {
+                inKeyboardResize = true;
+                return;
             }
-        }
+
+            if (command.equals(cmWindowZoom)) {
+                if (maximized) {
+                    restore();
+                } else {
+                    maximize();
+                }
+            }
+
+        } // if (!(this instanceof TDesktop))
 
         // I didn't take it, pass it on to my children
         super.onCommand(command);
@@ -888,34 +1149,41 @@ public class TWindow extends TWidget {
      */
     @Override
     public void onMenu(final TMenuEvent menu) {
-        if (menu.getId() == TMenu.MID_WINDOW_CLOSE) {
-            application.closeWindow(this);
-            return;
-        }
 
-        if (menu.getId() == TMenu.MID_WINDOW_NEXT) {
-            application.switchWindow(true);
-            return;
-        }
+        if (!(this instanceof TDesktop)) {
 
-        if (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) {
-            application.switchWindow(false);
-            return;
-        }
+            if (menu.getId() == TMenu.MID_WINDOW_CLOSE) {
+                if ((flags & NOCLOSEBOX) == 0) {
+                    application.closeWindow(this);
+                }
+                return;
+            }
 
-        if (menu.getId() == TMenu.MID_WINDOW_MOVE) {
-            inKeyboardResize = true;
-            return;
-        }
+            if (menu.getId() == TMenu.MID_WINDOW_NEXT) {
+                application.switchWindow(true);
+                return;
+            }
 
-        if (menu.getId() == TMenu.MID_WINDOW_ZOOM) {
-            if (maximized) {
-                restore();
-            } else {
-                maximize();
+            if (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) {
+                application.switchWindow(false);
+                return;
             }
-            return;
-        }
+
+            if (menu.getId() == TMenu.MID_WINDOW_MOVE) {
+                inKeyboardResize = true;
+                return;
+            }
+
+            if (menu.getId() == TMenu.MID_WINDOW_ZOOM) {
+                if (maximized) {
+                    restore();
+                } else {
+                    maximize();
+                }
+                return;
+            }
+
+        } // if (!(this instanceof TDesktop))
 
         // I didn't take it, pass it on to my children
         super.onMenu(menu);