JEXER_SRC = $(SRC_DIR)/jexer/TApplication.java \
$(SRC_DIR)/jexer/TCommand.java \
$(SRC_DIR)/jexer/TKeypress.java \
+ $(SRC_DIR)/jexer/THScroller.java \
+ $(SRC_DIR)/jexer/TVScroller.java \
+ $(SRC_DIR)/jexer/TWidget.java \
$(SRC_DIR)/jexer/bits/GraphicsChars.java \
$(SRC_DIR)/jexer/bits/Color.java \
$(SRC_DIR)/jexer/bits/CellAttributes.java \
JEXER_BIN = $(TARGET_DIR)/jexer/TApplication.class \
$(TARGET_DIR)/jexer/TCommand.class \
$(TARGET_DIR)/jexer/TKeypress.class \
+ $(TARGET_DIR)/jexer/THScroller.class \
+ $(TARGET_DIR)/jexer/TVScroller.class \
+ $(TARGET_DIR)/jexer/TWidget.class \
$(TARGET_DIR)/jexer/bits/GraphicsChars.class \
$(TARGET_DIR)/jexer/bits/Color.class \
$(TARGET_DIR)/jexer/bits/CellAttributes.class \
import jexer.event.TResizeEvent;
import jexer.backend.Backend;
import jexer.backend.ECMA48Backend;
+import jexer.io.Screen;
import static jexer.TCommand.*;
import static jexer.TKeypress.*;
*/
private Backend backend;
+ /**
+ * Get the Screen.
+ *
+ * @return the Screen
+ */
+ public final Screen getScreen() {
+ return backend.getScreen();
+ }
+
/**
* Actual mouse coordinate X.
*/
/**
* When true, exit the application.
*/
- public boolean quit = false;
+ private boolean quit = false;
/**
* When true, repaint the entire screen.
*/
- public boolean repaint = true;
+ private boolean repaint = true;
+
+ /**
+ * Request full repaint on next screen refresh.
+ */
+ public void setRepaint() {
+ repaint = true;
+ }
/**
* When true, just flush updates from the screen.
*/
- public boolean flush = false;
+ private boolean flush = false;
/**
* Y coordinate of the top edge of the desktop. For now this is a
* constant. Someday it would be nice to have a multi-line menu or
* toolbars.
*/
- public static final int desktopTop = 1;
+ private static final int desktopTop = 1;
+
+ /**
+ * Get Y coordinate of the top edge of the desktop.
+ *
+ * @return Y coordinate of the top edge of the desktop
+ */
+ public final int getDesktopTop() {
+ return desktopTop;
+ }
/**
* Y coordinate of the bottom edge of the desktop.
*/
- public int desktopBottom;
+ private int desktopBottom;
+
+ /**
+ * Get Y coordinate of the bottom edge of the desktop.
+ *
+ * @return Y coordinate of the bottom edge of the desktop
+ */
+ public final int getDesktopBottom() {
+ return desktopBottom;
+ }
/**
* Public constructor.
return 250;
}
+ /**
+ * Close window. Note that the window's destructor is NOT called by this
+ * method, instead the GC is assumed to do the cleanup.
+ *
+ * @param window the window to remove
+ */
+ public final void closeWindow(final TWindow window) {
+ /*
+ TODO
+
+ uint z = window.z;
+ window.z = -1;
+ windows.sort;
+ windows = windows[1 .. $];
+ TWindow activeWindow = null;
+ foreach (w; windows) {
+ if (w.z > z) {
+ w.z--;
+ if (w.z == 0) {
+ w.active = true;
+ assert(activeWindow is null);
+ activeWindow = w;
+ } else {
+ w.active = false;
+ }
+ }
+ }
+
+ // Perform window cleanup
+ window.onClose();
+
+ // Refresh screen
+ repaint = true;
+
+ // Check if we are closing a TMessageBox or similar
+ if (secondaryEventReceiver !is null) {
+ assert(secondaryEventFiber !is null);
+
+ // Do not send events to the secondaryEventReceiver anymore, the
+ // window is closed.
+ secondaryEventReceiver = null;
+
+ // Special case: if this is called while executing on a
+ // secondaryEventFiber, call it so that widgetEventHandler() can
+ // terminate.
+ if (secondaryEventFiber.state == Fiber.State.HOLD) {
+ secondaryEventFiber.call();
+ }
+ secondaryEventFiber = null;
+
+ // Unfreeze the logic in handleEvent()
+ if (primaryEventFiber.state == Fiber.State.HOLD) {
+ primaryEventFiber.call();
+ }
+ }
+ */
+ }
+
+ /**
+ * Switch to the next window.
+ *
+ * @param forward if true, then switch to the next window in the list,
+ * otherwise switch to the previous window in the list
+ */
+ public final void switchWindow(final boolean forward) {
+ /*
+ TODO
+
+ // Only switch if there are multiple windows
+ if (windows.length < 2) {
+ return;
+ }
+
+ // Swap z/active between active window and the next in the
+ // list
+ ptrdiff_t activeWindowI = -1;
+ for (auto i = 0; i < windows.length; i++) {
+ if (windows[i].active) {
+ activeWindowI = i;
+ break;
+ }
+ }
+ assert(activeWindowI >= 0);
+
+ // Do not switch if a window is modal
+ if (windows[activeWindowI].isModal()) {
+ return;
+ }
+
+ size_t nextWindowI;
+ if (forward) {
+ nextWindowI = (activeWindowI + 1) % windows.length;
+ } else {
+ if (activeWindowI == 0) {
+ nextWindowI = windows.length - 1;
+ } else {
+ nextWindowI = activeWindowI - 1;
+ }
+ }
+ windows[activeWindowI].active = false;
+ windows[activeWindowI].z = windows[nextWindowI].z;
+ windows[nextWindowI].z = 0;
+ windows[nextWindowI].active = true;
+
+ // Refresh
+ repaint = true;
+ */
+ }
+
+ /**
+ * Add a window to my window list and make it active.
+ *
+ * @param window new window to add
+ */
+ public final void addWindow(final TWindow window) {
+ /*
+ TODO
+ // Do not allow a modal window to spawn a non-modal window
+ if ((windows.length > 0) && (windows[0].isModal())) {
+ assert(window.isModal());
+ }
+ foreach (w; windows) {
+ w.active = false;
+ w.z++;
+ }
+ windows ~= window;
+ window.active = true;
+ window.z = 0;
+ */
+ }
+
+
}
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+/**
+ * THScroller implements a simple horizontal scroll bar.
+ */
+public class THScroller extends TWidget {
+
+}
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import jexer.bits.MnemonicString;
+
+/**
+ * TMenu is a top-level collection of TMenuItems.
+ */
+public class TMenu extends TWindow {
+
+ /**
+ * If true, this is a sub-menu.
+ */
+ private boolean isSubMenu = false;
+
+ /**
+ * The shortcut and title.
+ */
+ private MnemonicString mnemonic;
+
+ // Reserved menu item IDs
+ public static final int MID_UNUSED = -1;
+
+ // File menu
+ public static final int MID_EXIT = 1;
+ public static final int MID_QUIT = MID_EXIT;
+ public static final int MID_OPEN_FILE = 2;
+ public static final int MID_SHELL = 3;
+
+ // Edit menu
+ public static final int MID_CUT = 10;
+ public static final int MID_COPY = 11;
+ public static final int MID_PASTE = 12;
+ public static final int MID_CLEAR = 13;
+
+ // Window menu
+ public static final int MID_TILE = 20;
+ public static final int MID_CASCADE = 21;
+ public static final int MID_CLOSE_ALL = 22;
+ public static final int MID_WINDOW_MOVE = 23;
+ public static final int MID_WINDOW_ZOOM = 24;
+ public static final int MID_WINDOW_NEXT = 25;
+ public static final int MID_WINDOW_PREVIOUS = 26;
+ public static final int MID_WINDOW_CLOSE = 27;
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent application
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param label mnemonic menu title. Label must contain a keyboard
+ * shortcut (mnemonic), denoted by prefixing a letter with "&",
+ * e.g. "&File"
+ */
+ public TMenu(final TApplication parent, final int x, final int y,
+ final String label) {
+
+ super(parent, label, x, y, parent.getScreen().getWidth(),
+ parent.getScreen().getHeight());
+
+ // My parent constructor added me as a window, get rid of that
+ parent.closeWindow(this);
+
+ // Setup the menu shortcut
+ mnemonic = new MnemonicString(title);
+ this.title = mnemonic.getRawLabel();
+ assert (mnemonic.getShortcutIdx() >= 0);
+
+ // Recompute width and height to reflect an empty menu
+ width = this.title.length() + 4;
+ height = 2;
+
+ this.active = false;
+ }
+
+}
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+/**
+ * TVScroller implements a simple vertical scroll bar.
+ */
+public class TVScroller extends TWidget {
+
+}
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import java.util.List;
+import java.util.LinkedList;
+
+import jexer.event.TCommandEvent;
+import jexer.event.TInputEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMenuEvent;
+import jexer.event.TMouseEvent;
+import jexer.event.TResizeEvent;
+import jexer.io.Screen;
+import static jexer.TKeypress.*;
+
+/**
+ * TWidget is the base class of all objects that can be drawn on screen or
+ * handle user input events.
+ */
+public abstract class TWidget {
+
+ /**
+ * Every widget has a parent widget that it may be "contained" in. For
+ * example, a TWindow might contain several TTextFields, or a TComboBox
+ * may contain a TScrollBar.
+ */
+ protected TWidget parent = null;
+
+ /**
+ * Child widgets that this widget contains.
+ */
+ private List<TWidget> children;
+
+ /**
+ * The currently active child widget that will receive keypress events.
+ */
+ private TWidget activeChild = null;
+
+ /**
+ * If true, this widget will receive events.
+ */
+ protected boolean active = false;
+
+ /**
+ * The window that this widget draws to.
+ */
+ protected TWindow window = null;
+
+ /**
+ * Absolute X position of the top-left corner.
+ */
+ protected int x = 0;
+
+ /**
+ * Absolute Y position of the top-left corner.
+ */
+ protected int y = 0;
+
+ /**
+ * Width.
+ */
+ protected int width = 0;
+
+ /**
+ * Height.
+ */
+ protected int height = 0;
+
+ /**
+ * My tab order inside a window or containing widget.
+ */
+ private int tabOrder = 0;
+
+ /**
+ * If true, this widget can be tabbed to or receive events.
+ */
+ private boolean enabled = true;
+
+ /**
+ * Get enabled flag.
+ *
+ * @return if true, this widget can be tabbed to or receive events
+ */
+ public final boolean getEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Set enabled flag.
+ *
+ * @param enabled if true, this widget can be tabbed to or receive events
+ */
+ public final void setEnabled(final boolean enabled) {
+ this.enabled = enabled;
+ /*
+
+ // TODO: get this working after scrollers are going again
+
+ if (enabled == false) {
+ active = false;
+ // See if there are any active siblings to switch to
+ boolean foundSibling = false;
+ if (parent !is null) {
+ foreach (w; parent.children) {
+ if ((w.enabled) &&
+ (!cast(THScroller)this) &&
+ (!cast(TVScroller)this)
+ ) {
+ parent.activate(w);
+ foundSibling = true;
+ break;
+ }
+ }
+ if (!foundSibling) {
+ parent.activeChild = null;
+ }
+ }
+ }
+ */
+ }
+
+ /**
+ * If true, this widget has a cursor.
+ */
+ private boolean hasCursor = false;
+
+ /**
+ * Cursor column position in relative coordinates.
+ */
+ private int cursorX = 0;
+
+ /**
+ * Cursor row position in relative coordinates.
+ */
+ private int cursorY = 0;
+
+ /**
+ * Comparison operator sorts on tabOrder.
+ *
+ * @param that another TWidget instance
+ * @return difference between this.tabOrder and that.tabOrder
+ */
+ public final int compare(final TWidget that) {
+ return (this.tabOrder - that.tabOrder);
+ }
+
+ /**
+ * See if this widget should render with the active color.
+ *
+ * @return true if this widget is active and all of its parents are
+ * active.
+ */
+ public final boolean getAbsoluteActive() {
+ if (parent == this) {
+ return active;
+ }
+ return (active && parent.getAbsoluteActive());
+ }
+
+ /**
+ * Returns the cursor X position.
+ *
+ * @return absolute screen column number for the cursor's X position
+ */
+ public final int getCursorAbsoluteX() {
+ assert (hasCursor);
+ return getAbsoluteX() + cursorX;
+ }
+
+ /**
+ * Returns the cursor Y position.
+ *
+ * @return absolute screen row number for the cursor's Y position
+ */
+ public final int getCursorAbsoluteY() {
+ assert (hasCursor);
+ return getAbsoluteY() + cursorY;
+ }
+
+ /**
+ * Compute my absolute X position as the sum of my X plus all my parent's
+ * X's.
+ *
+ * @return absolute screen column number for my X position
+ */
+ public final int getAbsoluteX() {
+ assert (parent != null);
+ if (parent == this) {
+ return x;
+ }
+ if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
+ // Widgets on a TWindow have (0,0) as their top-left, but this is
+ // actually the TWindow's (1,1).
+ return parent.getAbsoluteX() + x + 1;
+ }
+ return parent.getAbsoluteX() + x;
+ }
+
+ /**
+ * Compute my absolute Y position as the sum of my Y plus all my parent's
+ * Y's.
+ *
+ * @return absolute screen row number for my Y position
+ */
+ public final int getAbsoluteY() {
+ assert (parent != null);
+ if (parent == this) {
+ return y;
+ }
+ if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
+ // Widgets on a TWindow have (0,0) as their top-left, but this is
+ // actually the TWindow's (1,1).
+ return parent.getAbsoluteY() + y + 1;
+ }
+ return parent.getAbsoluteY() + y;
+ }
+
+ /**
+ * Draw my specific widget. When called, the screen rectangle I draw
+ * into is already setup (offset and clipping).
+ */
+ public void draw() {
+ // Default widget draws nothing.
+ }
+
+ /**
+ * Called by parent to render to TWindow.
+ */
+ public final void drawChildren() {
+ // Set my clipping rectangle
+ assert (window != null);
+ assert (window.getScreen() != null);
+ Screen screen = window.getScreen();
+
+ screen.setClipRight(width);
+ screen.setClipBottom(height);
+
+ int absoluteRightEdge = window.getAbsoluteX() + screen.getWidth();
+ int absoluteBottomEdge = window.getAbsoluteY() + screen.getHeight();
+ if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
+ absoluteRightEdge -= 1;
+ }
+ if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
+ absoluteBottomEdge -= 1;
+ }
+ int myRightEdge = getAbsoluteX() + width;
+ int myBottomEdge = getAbsoluteY() + height;
+ if (getAbsoluteX() > absoluteRightEdge) {
+ // I am offscreen
+ screen.setClipRight(0);
+ } else if (myRightEdge > absoluteRightEdge) {
+ screen.setClipRight(screen.getClipRight()
+ - myRightEdge - absoluteRightEdge);
+ }
+ if (getAbsoluteY() > absoluteBottomEdge) {
+ // I am offscreen
+ screen.setClipBottom(0);
+ } else if (myBottomEdge > absoluteBottomEdge) {
+ screen.setClipBottom(screen.getClipBottom()
+ - myBottomEdge - absoluteBottomEdge);
+ }
+
+ // Set my offset
+ screen.setOffsetX(getAbsoluteX());
+ screen.setOffsetY(getAbsoluteY());
+
+ // Draw me
+ draw();
+
+ // Continue down the chain
+ for (TWidget widget: children) {
+ widget.drawChildren();
+ }
+ }
+
+ /**
+ * Subclasses need this constructor to setup children.
+ */
+ protected TWidget() {
+ children = new LinkedList<TWidget>();
+ }
+
+ /**
+ * Protected constructor.
+ *
+ * @param parent parent widget
+ */
+ protected TWidget(final TWidget parent) {
+ this.parent = parent;
+ this.window = parent.window;
+
+ parent.addChild(this);
+ }
+
+ /**
+ * Add a child widget to my list of children. We set its tabOrder to 0
+ * and increment the tabOrder of all other children.
+ *
+ * @param child TWidget to add
+ */
+ private void addChild(final TWidget child) {
+ children.add(child);
+
+ if ((child.enabled)
+ && !(child instanceof THScroller)
+ && !(child instanceof TVScroller)
+ ) {
+ for (TWidget widget: children) {
+ widget.active = false;
+ }
+ child.active = true;
+ activeChild = child;
+ }
+ for (int i = 0; i < children.size(); i++) {
+ children.get(i).tabOrder = i;
+ }
+ }
+
+ /**
+ * Switch the active child.
+ *
+ * @param child TWidget to activate
+ */
+ public final void activate(final TWidget child) {
+ assert (child.enabled);
+ if ((child instanceof THScroller)
+ || (child instanceof TVScroller)
+ ) {
+ return;
+ }
+
+ if (child != activeChild) {
+ if (activeChild != null) {
+ activeChild.active = false;
+ }
+ child.active = true;
+ activeChild = child;
+ }
+ }
+
+ /**
+ * Switch the active child.
+ *
+ * @param tabOrder tabOrder of the child to activate. If that child
+ * isn't enabled, then the next enabled child will be activated.
+ */
+ public final void activate(final int tabOrder) {
+ if (activeChild == null) {
+ return;
+ }
+ TWidget child = null;
+ for (TWidget widget: children) {
+ if ((widget.enabled)
+ && !(widget instanceof THScroller)
+ && !(widget instanceof TVScroller)
+ && (widget.tabOrder >= tabOrder)
+ ) {
+ child = widget;
+ break;
+ }
+ }
+ if ((child != null) && (child != activeChild)) {
+ activeChild.active = false;
+ assert (child.enabled);
+ child.active = true;
+ activeChild = child;
+ }
+ }
+
+ /**
+ * Switch the active widget with the next in the tab order.
+ *
+ * @param forward if true, then switch to the next enabled widget in the
+ * list, otherwise switch to the previous enabled widget in the list
+ */
+ public final void switchWidget(final boolean forward) {
+
+ // Only switch if there are multiple enabled widgets
+ if ((children.size() < 2) || (activeChild == null)) {
+ return;
+ }
+
+ int tabOrder = activeChild.tabOrder;
+ do {
+ if (forward) {
+ tabOrder++;
+ } else {
+ tabOrder--;
+ }
+ if (tabOrder < 0) {
+
+ // If at the end, pass the switch to my parent.
+ if ((!forward) && (parent != this)) {
+ parent.switchWidget(forward);
+ return;
+ }
+
+ tabOrder = children.size() - 1;
+ } else if (tabOrder == children.size()) {
+ // If at the end, pass the switch to my parent.
+ if ((forward) && (parent != this)) {
+ parent.switchWidget(forward);
+ return;
+ }
+
+ tabOrder = 0;
+ }
+ if (activeChild.tabOrder == tabOrder) {
+ // We wrapped around
+ break;
+ }
+ } while ((!children.get(tabOrder).enabled)
+ && !(children.get(tabOrder) instanceof THScroller)
+ && !(children.get(tabOrder) instanceof TVScroller));
+
+ assert (children.get(tabOrder).enabled);
+
+ activeChild.active = false;
+ children.get(tabOrder).active = true;
+ activeChild = children.get(tabOrder);
+
+ // Refresh
+ window.getApplication().setRepaint();
+ }
+
+ /**
+ * Returns my active widget.
+ *
+ * @return widget that is active, or this if no children
+ */
+ public final TWidget getActiveChild() {
+ if ((this instanceof THScroller)
+ || (this instanceof TVScroller)
+ ) {
+ return parent;
+ }
+
+ for (TWidget widget: children) {
+ if (widget.active) {
+ return widget.getActiveChild();
+ }
+ }
+ // No active children, return me
+ return this;
+ }
+
+ /**
+ * Method that subclasses can override to handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ public void onKeypress(final TKeypressEvent keypress) {
+
+ if ((children.size() == 0)
+ // || (cast(TTreeView)this)
+ // || (cast(TText)this)
+ ) {
+
+ // Defaults:
+ // tab / shift-tab - switch to next/previous widget
+ // right-arrow or down-arrow: same as tab
+ // left-arrow or up-arrow: same as shift-tab
+ if ((keypress.equals(kbTab))
+ || (keypress.equals(kbRight))
+ || (keypress.equals(kbDown))
+ ) {
+ parent.switchWidget(true);
+ return;
+ } else if ((keypress.equals(kbShiftTab))
+ || (keypress.equals(kbBackTab))
+ || (keypress.equals(kbLeft))
+ || (keypress.equals(kbUp))
+ ) {
+ parent.switchWidget(false);
+ return;
+ }
+ }
+
+ // If I have any buttons on me AND this is an Alt-key that matches
+ // its mnemonic, send it an Enter keystroke
+ for (TWidget widget: children) {
+ /*
+ TODO
+
+ if (TButton button = cast(TButton)w) {
+ if (button.enabled &&
+ !keypress.key.isKey &&
+ keypress.key.alt &&
+ !keypress.key.ctrl &&
+ (toLowercase(button.mnemonic.shortcut) == toLowercase(keypress.key.ch))) {
+
+ w.handleEvent(new TKeypressEvent(kbEnter));
+ return;
+ }
+ }
+ */
+ }
+
+ // Dispatch the keypress to an active widget
+ for (TWidget widget: children) {
+ if (widget.active) {
+ window.getApplication().setRepaint();
+ widget.handleEvent(keypress);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Method that subclasses can override to handle mouse button presses.
+ *
+ * @param mouse mouse button event
+ */
+ public void onMouseDown(final TMouseEvent mouse) {
+ // Default: do nothing, pass to children instead
+ for (TWidget widget: children) {
+ if (widget.mouseWouldHit(mouse)) {
+ // Dispatch to this child, also activate it
+ activate(widget);
+
+ // Set x and y relative to the child's coordinates
+ mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
+ mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
+ widget.handleEvent(mouse);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Method that subclasses can override to handle mouse button releases.
+ *
+ * @param mouse mouse button event
+ */
+ public void onMouseUp(final TMouseEvent mouse) {
+ // Default: do nothing, pass to children instead
+ for (TWidget widget: children) {
+ if (widget.mouseWouldHit(mouse)) {
+ // Dispatch to this child, also activate it
+ activate(widget);
+
+ // Set x and y relative to the child's coordinates
+ mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
+ mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
+ widget.handleEvent(mouse);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Method that subclasses can override to handle mouse movements.
+ *
+ * @param mouse mouse motion event
+ */
+ public void onMouseMotion(final TMouseEvent mouse) {
+ // Default: do nothing, pass it on to ALL of my children. This way
+ // the children can see the mouse "leaving" their area.
+ for (TWidget widget: children) {
+ // Set x and y relative to the child's coordinates
+ mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
+ mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
+ widget.handleEvent(mouse);
+ }
+ }
+
+ /**
+ * Method that subclasses can override to handle window/screen resize
+ * events.
+ *
+ * @param resize resize event
+ */
+ public void onResize(final TResizeEvent resize) {
+ // Default: do nothing, pass to children instead
+ for (TWidget widget: children) {
+ widget.onResize(resize);
+ }
+ }
+
+ /**
+ * Method that subclasses can override to handle posted command events.
+ *
+ * @param command command event
+ */
+ public void onCommand(final TCommandEvent command) {
+ // Default: do nothing, pass to children instead
+ for (TWidget widget: children) {
+ widget.onCommand(command);
+ }
+ }
+
+ /**
+ * Method that subclasses can override to handle menu or posted menu
+ * events.
+ *
+ * @param menu menu event
+ */
+ public void onMenu(final TMenuEvent menu) {
+ // Default: do nothing, pass to children instead
+ for (TWidget widget: children) {
+ widget.onMenu(menu);
+ }
+ }
+
+ /**
+ * Method that subclasses can override to do processing when the UI is
+ * idle.
+ */
+ public void onIdle() {
+ // Default: do nothing, pass to children instead
+ for (TWidget widget: children) {
+ widget.onIdle();
+ }
+ }
+
+ /**
+ * Consume event. Subclasses that want to intercept all events in one go
+ * can override this method.
+ *
+ * @param event keyboard, mouse, resize, command, or menu event
+ */
+ public void handleEvent(final TInputEvent event) {
+ // System.err.printf("TWidget (%s) event: %s\n", this.getClass().getName(),
+ // event);
+
+ if (!enabled) {
+ // Discard event
+ // System.err.println(" -- discard --");
+ return;
+ }
+
+ if (event instanceof TKeypressEvent) {
+ onKeypress((TKeypressEvent) event);
+ } else if (event instanceof TMouseEvent) {
+
+ TMouseEvent mouse = (TMouseEvent) event;
+
+ switch (mouse.getType()) {
+
+ case MOUSE_DOWN:
+ onMouseDown(mouse);
+ break;
+
+ case MOUSE_UP:
+ onMouseUp(mouse);
+ break;
+
+ case MOUSE_MOTION:
+ onMouseMotion(mouse);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Invalid mouse event type: "
+ + mouse.getType());
+ }
+ } else if (event instanceof TResizeEvent) {
+ onResize((TResizeEvent) event);
+ } else if (event instanceof TCommandEvent) {
+ onCommand((TCommandEvent) event);
+ } else if (event instanceof TMenuEvent) {
+ onMenu((TMenuEvent) event);
+ }
+
+ // Do nothing else
+ return;
+ }
+
+ /**
+ * Check if a mouse press/release event coordinate is contained in this
+ * widget.
+ *
+ * @param mouse a mouse-based event
+ * @return whether or not a mouse click would be sent to this widget
+ */
+ public final boolean mouseWouldHit(final TMouseEvent mouse) {
+
+ if (!enabled) {
+ return false;
+ }
+
+ if ((mouse.getAbsoluteX() >= getAbsoluteX())
+ && (mouse.getAbsoluteX() < getAbsoluteX() + width)
+ && (mouse.getAbsoluteY() >= getAbsoluteY())
+ && (mouse.getAbsoluteY() < getAbsoluteY() + height)
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+}
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import jexer.bits.Cell;
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+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 static jexer.TCommand.*;
+import static jexer.TKeypress.*;
+
+/**
+ * TWindow is the top-level container and drawing surface for other widgets.
+ */
+public class TWindow extends TWidget {
+
+ /**
+ * Window's parent TApplication.
+ */
+ protected TApplication application;
+
+ /**
+ * Get this TWindow's parent TApplication.
+ *
+ * @return this TWindow's parent TApplication
+ */
+ public final TApplication getApplication() {
+ return application;
+ }
+
+ /**
+ * Get the Screen.
+ *
+ * @return the Screen
+ */
+ public final Screen getScreen() {
+ return application.getScreen();
+ }
+
+ /**
+ * Window title.
+ */
+ protected String title = "";
+
+ /**
+ * Window is resizable (default yes).
+ */
+ public static final int RESIZABLE = 0x01;
+
+ /**
+ * Window is modal (default no).
+ */
+ public static final int MODAL = 0x02;
+
+ /**
+ * Window is centered (default no).
+ */
+ public static final int CENTERED = 0x04;
+
+ /**
+ * Window flags.
+ */
+ private int flags = RESIZABLE;
+
+ /**
+ * Z order. Lower number means more in-front.
+ */
+ private int z = 0;
+
+ /**
+ * If true, then the user clicked on the title bar and is moving the
+ * window.
+ */
+ private boolean inWindowMove = false;
+
+ /**
+ * If true, then the user clicked on the bottom right corner and is
+ * resizing the window.
+ */
+ private boolean inWindowResize = false;
+
+ /**
+ * 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;
+
+ /**
+ * If true, this window is maximized.
+ */
+ private boolean maximized = false;
+
+ /**
+ * Remember mouse state.
+ */
+ protected TMouseEvent mouse;
+
+ // For moving the window. resizing also uses moveWindowMouseX/Y
+ private int moveWindowMouseX;
+ private int moveWindowMouseY;
+ private int oldWindowX;
+ private int oldWindowY;
+
+ // Resizing
+ private int resizeWindowWidth;
+ private int resizeWindowHeight;
+ private int minimumWindowWidth = 10;
+ private int minimumWindowHeight = 2;
+ private int maximumWindowWidth = -1;
+ private int maximumWindowHeight = -1;
+
+ // For maximize/restore
+ private int restoreWindowWidth;
+ private int restoreWindowHeight;
+ private int restoreWindowX;
+ private int restoreWindowY;
+
+ /**
+ * Public constructor. Window will be located at (0, 0).
+ *
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param width width of window
+ * @param height height of window
+ */
+ public TWindow(final TApplication application, final String title,
+ final int width, final int height) {
+
+ this(application, title, 0, 0, width, height, RESIZABLE);
+ }
+
+ /**
+ * Public constructor. Window will be located at (0, 0).
+ *
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param width width of window
+ * @param height height of window
+ * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
+ */
+ public TWindow(final TApplication application, final String title,
+ final int width, final int height, final int flags) {
+
+ this(application, title, 0, 0, width, height, flags);
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of window
+ * @param height height of window
+ */
+ public TWindow(final TApplication application, final String title,
+ final int x, final int y, final int width, final int height) {
+
+ this(application, title, x, y, width, height, RESIZABLE);
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of window
+ * @param height height of window
+ * @param flags mask of RESIZABLE, CENTERED, or MODAL
+ */
+ public TWindow(final TApplication application, final String title,
+ final int x, final int y, final int width, final int height,
+ final int flags) {
+
+ // I am my own window and parent
+ this.parent = this;
+ this.window = this;
+
+ // Save fields
+ this.title = title;
+ this.application = application;
+ this.x = x;
+ this.y = y + application.getDesktopTop();
+ this.width = width;
+ this.height = height;
+ this.flags = flags;
+
+ // Minimum width/height are 10 and 2
+ assert (width >= 10);
+ assert (height >= 2);
+
+ // MODAL implies CENTERED
+ if (isModal()) {
+ this.flags |= CENTERED;
+ }
+
+ // Center window if specified
+ center();
+
+ // Add me to the application
+ application.addWindow(this);
+ }
+
+ /**
+ * Recenter the window on-screen.
+ */
+ public final void center() {
+ if ((flags & CENTERED) != 0) {
+ if (width < getScreen().getWidth()) {
+ x = (getScreen().getWidth() - width) / 2;
+ } else {
+ x = 0;
+ }
+ y = (application.getDesktopBottom() - application.getDesktopTop());
+ y -= height;
+ y /= 2;
+ if (y < 0) {
+ y = 0;
+ }
+ y += application.getDesktopTop();
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Comparison operator sorts on z.
+ *
+ * @param that another TWindow instance
+ * @return difference between this.z and that.z
+ */
+ public final int compare(final TWindow that) {
+ return (z - that.z);
+ }
+
+ /**
+ * Returns true if the mouse is currently on the close button.
+ *
+ * @return true if mouse is currently on the close button
+ */
+ private boolean mouseOnClose() {
+ if ((mouse != null)
+ && (mouse.getAbsoluteY() == y)
+ && (mouse.getAbsoluteX() == x + 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() == y)
+ && (mouse.getAbsoluteX() == x + width - 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() == y + height - 1)
+ && ((mouse.getAbsoluteX() == x + width - 1)
+ || (mouse.getAbsoluteX() == x + width - 2))
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the background color.
+ *
+ * @return the background color
+ */
+ protected final CellAttributes getBackground() {
+ if (!isModal()
+ && (inWindowMove || inWindowResize || inKeyboardResize)
+ ) {
+ assert (active);
+ return application.getTheme().getColor("twindow.background.windowmove");
+ } else if (isModal() && inWindowMove) {
+ assert (active);
+ return application.getTheme().getColor("twindow.background.modal");
+ } else if (isModal()) {
+ if (active) {
+ return application.getTheme().getColor("twindow.background.modal");
+ }
+ return application.getTheme().getColor("twindow.background.modal.inactive");
+ } else if (active) {
+ assert (!isModal());
+ return application.getTheme().getColor("twindow.background");
+ } else {
+ assert (!isModal());
+ return application.getTheme().getColor("twindow.background.inactive");
+ }
+ }
+
+ /**
+ * Retrieve the border color.
+ *
+ * @return the border color
+ */
+ protected final CellAttributes getBorder() {
+ if (!isModal()
+ && (inWindowMove || inWindowResize || inKeyboardResize)
+ ) {
+ assert (active);
+ return application.getTheme().getColor("twindow.border.windowmove");
+ } else if (isModal() && inWindowMove) {
+ assert (active);
+ return application.getTheme().getColor("twindow.border.modal.windowmove");
+ } else if (isModal()) {
+ if (active) {
+ return application.getTheme().getColor("twindow.border.modal");
+ } else {
+ return application.getTheme().getColor("twindow.border.modal.inactive");
+ }
+ } else if (active) {
+ assert (!isModal());
+ return application.getTheme().getColor("twindow.border");
+ } else {
+ assert (!isModal());
+ return application.getTheme().getColor("twindow.border.inactive");
+ }
+ }
+
+ /**
+ * Retrieve the border line type.
+ *
+ * @return the border line type
+ */
+ protected final int getBorderType() {
+ if (!isModal()
+ && (inWindowMove || inWindowResize || inKeyboardResize)
+ ) {
+ assert (active);
+ return 1;
+ } else if (isModal() && inWindowMove) {
+ assert (active);
+ return 1;
+ } else if (isModal()) {
+ if (active) {
+ return 2;
+ } else {
+ return 1;
+ }
+ } else if (active) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Subclasses should override this method to cleanup resources. This is
+ * called by application.closeWindow().
+ */
+ public void onClose() {
+ // Default: do nothing
+ }
+
+ /**
+ * 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, width, height, border,
+ background, borderType, true);
+
+ // Draw the title
+ int titleLeft = (width - title.length() - 2) / 2;
+ putCharXY(titleLeft, 0, ' ', border);
+ putStrXY(titleLeft + 1, 0, title);
+ putCharXY(titleLeft + title.length() + 1, 0, ' ', border);
+
+ if (active) {
+
+ // Draw the close button
+ putCharXY(2, 0, '[', border);
+ putCharXY(4, 0, ']', border);
+ if (mouseOnClose() && mouse.getMouse1()) {
+ putCharXY(3, 0, GraphicsChars.CP437[0x0F],
+ !isModal()
+ ? application.getTheme().getColor("twindow.border.windowmove")
+ : application.getTheme().getColor("twindow.border.modal.windowmove"));
+ } else {
+ putCharXY(3, 0, GraphicsChars.CP437[0xFE],
+ !isModal()
+ ? application.getTheme().getColor("twindow.border.windowmove")
+ : application.getTheme().getColor("twindow.border.modal.windowmove"));
+ }
+
+ // Draw the maximize button
+ if (!isModal()) {
+
+ putCharXY(width - 5, 0, '[', border);
+ putCharXY(width - 3, 0, ']', border);
+ if (mouseOnMaximize() && mouse.getMouse1()) {
+ putCharXY(width - 4, 0, GraphicsChars.CP437[0x0F],
+ application.getTheme().getColor("twindow.border.windowmove"));
+ } else {
+ if (maximized) {
+ putCharXY(width - 4, 0, GraphicsChars.CP437[0x12],
+ application.getTheme().getColor("twindow.border.windowmove"));
+ } else {
+ putCharXY(width - 4, 0, GraphicsChars.UPARROW,
+ application.getTheme().getColor("twindow.border.windowmove"));
+ }
+ }
+
+ // Draw the resize corner
+ if ((flags & RESIZABLE) != 0) {
+ putCharXY(width - 2, height - 1, GraphicsChars.SINGLE_BAR,
+ application.getTheme().getColor("twindow.border.windowmove"));
+ putCharXY(width - 1, height - 1, GraphicsChars.LRCORNER,
+ application.getTheme().getColor("twindow.border.windowmove"));
+ }
+ }
+ }
+ }
+
+ /**
+ * Handle mouse button presses.
+ *
+ * @param mouse mouse button event
+ */
+ @Override
+ public void onMouseDown(final TMouseEvent mouse) {
+ this.mouse = mouse;
+ application.setRepaint();
+
+ inKeyboardResize = false;
+
+ if ((mouse.getAbsoluteY() == y)
+ && mouse.getMouse1()
+ && (x <= mouse.getAbsoluteX())
+ && (mouse.getAbsoluteX() < x + width)
+ && !mouseOnClose()
+ && !mouseOnMaximize()
+ ) {
+ // Begin moving window
+ inWindowMove = true;
+ moveWindowMouseX = mouse.getAbsoluteX();
+ moveWindowMouseY = mouse.getAbsoluteY();
+ oldWindowX = x;
+ oldWindowY = y;
+ if (maximized) {
+ maximized = false;
+ }
+ return;
+ }
+ if (mouseOnResize()) {
+ // Begin window resize
+ inWindowResize = true;
+ moveWindowMouseX = mouse.getAbsoluteX();
+ moveWindowMouseY = mouse.getAbsoluteY();
+ resizeWindowWidth = width;
+ resizeWindowHeight = height;
+ if (maximized) {
+ maximized = false;
+ }
+ return;
+ }
+
+ // I didn't take it, pass it on to my children
+ super.onMouseDown(mouse);
+ }
+
+ /**
+ * Maximize window.
+ */
+ private void maximize() {
+ restoreWindowWidth = width;
+ restoreWindowHeight = height;
+ restoreWindowX = x;
+ restoreWindowY = y;
+ width = getScreen().getWidth();
+ height = application.getDesktopBottom() - 1;
+ x = 0;
+ y = 1;
+ maximized = true;
+ }
+
+ /**
+ * Restote (unmaximize) window.
+ */
+ private void restore() {
+ width = restoreWindowWidth;
+ height = restoreWindowHeight;
+ x = restoreWindowX;
+ y = restoreWindowY;
+ maximized = false;
+ }
+
+ /**
+ * Handle mouse button releases.
+ *
+ * @param mouse mouse button release event
+ */
+ @Override
+ public void onMouseUp(final TMouseEvent mouse) {
+ this.mouse = mouse;
+ application.setRepaint();
+
+ if ((inWindowMove) && (mouse.getMouse1())) {
+ // Stop moving window
+ inWindowMove = false;
+ return;
+ }
+
+ if ((inWindowResize) && (mouse.getMouse1())) {
+ // Stop resizing window
+ inWindowResize = false;
+ return;
+ }
+
+ if (mouse.getMouse1() && mouseOnClose()) {
+ // Close window
+ application.closeWindow(this);
+ return;
+ }
+
+ if ((mouse.getAbsoluteY() == y) && mouse.getMouse1()
+ && mouseOnMaximize()) {
+ if (maximized) {
+ // Restore
+ restore();
+ } else {
+ // Maximize
+ maximize();
+ }
+ // Pass a resize event to my children
+ onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, width, height));
+ return;
+ }
+
+ // I didn't take it, pass it on to my children
+ super.onMouseUp(mouse);
+ }
+
+ /**
+ * Handle mouse movements.
+ *
+ * @param mouse mouse motion event
+ */
+ @Override
+ public void onMouseMotion(final TMouseEvent mouse) {
+ this.mouse = mouse;
+ application.setRepaint();
+
+ if (inWindowMove) {
+ // Move window over
+ x = oldWindowX + (mouse.getAbsoluteX() - moveWindowMouseX);
+ y = oldWindowY + (mouse.getAbsoluteY() - moveWindowMouseY);
+ // Don't cover up the menu bar
+ if (y < application.getDesktopTop()) {
+ y = application.getDesktopTop();
+ }
+ return;
+ }
+
+ if (inWindowResize) {
+ // Move window over
+ width = resizeWindowWidth + (mouse.getAbsoluteX() - moveWindowMouseX);
+ height = resizeWindowHeight + (mouse.getAbsoluteY() - moveWindowMouseY);
+ if (x + width > getScreen().getWidth()) {
+ width = getScreen().getWidth() - x;
+ }
+ if (y + height > application.getDesktopBottom()) {
+ y = application.getDesktopBottom() - height + 1;
+ }
+ // Don't cover up the menu bar
+ if (y < application.getDesktopTop()) {
+ y = application.getDesktopTop();
+ }
+
+ // Keep within min/max bounds
+ if (width < minimumWindowWidth) {
+ width = minimumWindowWidth;
+ inWindowResize = false;
+ }
+ if (height < minimumWindowHeight) {
+ height = minimumWindowHeight;
+ inWindowResize = false;
+ }
+ if ((maximumWindowWidth > 0) && (width > maximumWindowWidth)) {
+ width = maximumWindowWidth;
+ inWindowResize = false;
+ }
+ if ((maximumWindowHeight > 0) && (height > maximumWindowHeight)) {
+ height = maximumWindowHeight;
+ inWindowResize = false;
+ }
+
+ // Pass a resize event to my children
+ onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, width, height));
+ return;
+ }
+
+ // I didn't take it, pass it on to my children
+ super.onMouseMotion(mouse);
+ }
+
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+
+ if (inKeyboardResize) {
+
+ // ESC - Exit size/move
+ if (keypress.equals(kbEsc)) {
+ inKeyboardResize = false;
+ }
+
+ if (keypress.equals(kbLeft)) {
+ if (x > 0) {
+ x--;
+ }
+ }
+ if (keypress.equals(kbRight)) {
+ if (x < getScreen().getWidth() - 1) {
+ x++;
+ }
+ }
+ if (keypress.equals(kbDown)) {
+ if (y < application.getDesktopBottom() - 1) {
+ y++;
+ }
+ }
+ if (keypress.equals(kbUp)) {
+ if (y > 1) {
+ y--;
+ }
+ }
+ if (keypress.equals(kbShiftLeft)) {
+ if (width > minimumWindowWidth) {
+ width--;
+ }
+ }
+ if (keypress.equals(kbShiftRight)) {
+ if (width < maximumWindowWidth) {
+ width++;
+ }
+ }
+ if (keypress.equals(kbShiftUp)) {
+ if (height > minimumWindowHeight) {
+ height--;
+ }
+ }
+ if (keypress.equals(kbShiftDown)) {
+ if (height < maximumWindowHeight) {
+ height++;
+ }
+ }
+
+ 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;
+ }
+
+ // F6 - behave like Alt-TAB
+ if (keypress.equals(kbF6)) {
+ application.switchWindow(true);
+ return;
+ }
+
+ // Shift-F6 - behave like Shift-Alt-TAB
+ if (keypress.equals(kbShiftF6)) {
+ application.switchWindow(false);
+ return;
+ }
+
+ // F5 - zoom
+ if (keypress.equals(kbF5)) {
+ if (maximized) {
+ restore();
+ } else {
+ maximize();
+ }
+ }
+
+ // Ctrl-F5 - size/move
+ if (keypress.equals(kbCtrlF5)) {
+ inKeyboardResize = !inKeyboardResize;
+ }
+
+ // I didn't take it, pass it on to my children
+ super.onKeypress(keypress);
+ }
+
+ /**
+ * Handle posted command events.
+ *
+ * @param command command event
+ */
+ @Override
+ public void onCommand(final TCommandEvent command) {
+
+ // These commands will typically not be seen unless a subclass
+ // overrides onMenu() due to how TApplication dispatches
+ // accelerators.
+
+ if (command.equals(cmWindowClose)) {
+ application.closeWindow(this);
+ return;
+ }
+
+ if (command.equals(cmWindowNext)) {
+ application.switchWindow(true);
+ return;
+ }
+
+ if (command.equals(cmWindowPrevious)) {
+ application.switchWindow(false);
+ return;
+ }
+
+ if (command.equals(cmWindowMove)) {
+ inKeyboardResize = true;
+ return;
+ }
+
+ if (command.equals(cmWindowZoom)) {
+ if (maximized) {
+ restore();
+ } else {
+ maximize();
+ }
+ }
+
+ // I didn't take it, pass it on to my children
+ super.onCommand(command);
+ }
+
+ /**
+ * Handle posted menu events.
+ *
+ * @param menu menu event
+ */
+ @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 (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) {
+ application.switchWindow(false);
+ return;
+ }
+
+ if (menu.getId() == TMenu.MID_WINDOW_MOVE) {
+ inKeyboardResize = true;
+ return;
+ }
+
+ if (menu.getId() == TMenu.MID_WINDOW_ZOOM) {
+ if (maximized) {
+ restore();
+ } else {
+ maximize();
+ }
+ return;
+ }
+
+ // I didn't take it, pass it on to my children
+ 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.
+ *
+ * @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)
+ */
+ public final void putAttrXY(final int x, final int y,
+ final CellAttributes attr) {
+
+ getScreen().putAttrXY(x, y, attr);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Fill the entire screen with one character with attributes.
+ *
+ * @param ch character to draw
+ * @param attr attributes to use (bold, foreColor, backColor)
+ */
+ public final void putAll(final char ch, final CellAttributes attr) {
+ getScreen().putAll(ch, attr);
+ }
+
+ /**
+ * 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 + attributes to draw
+ */
+ public final void putCharXY(final int x, final int y, final Cell ch) {
+ getScreen().putCharXY(x, y, ch);
+ }
+
+ /**
+ * 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)
+ */
+ public final void putCharXY(final int x, final int y, final char ch,
+ final CellAttributes attr) {
+
+ getScreen().putCharXY(x, y, ch, attr);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 putStrXY(final int x, final int y, final String str,
+ final CellAttributes attr) {
+
+ getScreen().putStrXY(x, y, str, attr);
+ }
+
+ /**
+ * 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 putStrXY(final int x, final int y, final String str) {
+ getScreen().putStrXY(x, y, str);
+ }
+
+ /**
+ * 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) {
+
+ getScreen().vLineXY(x, y, n, ch, attr);
+ }
+
+ /**
+ * Draw a horizontal line from (x, y) to (x + n, y).
+ *
+ * @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 hLineXY(final int x, final int y, final int n,
+ final char ch, final CellAttributes attr) {
+
+ getScreen().hLineXY(x, y, n, ch, attr);
+ }
+
+
+}
/**
* The CP437 to Unicode translation map.
*/
- private static final char [] CP437 = {
+ public static final char [] CP437 = {
'\u2007', '\u263A', '\u263B', '\u2665',
'\u2666', '\u2663', '\u2660', '\u2022',
'\u25D8', '\u25CB', '\u25D9', '\u2642',
* characters, e.g. "&File && Stuff" would be "File & Stuff" with the first
* 'F' highlighted.
*/
-public class MnemonicString {
+public final class MnemonicString {
/**
* Keyboard shortcut to activate this item.
*/
private int shortcutIdx = -1;
+ /**
+ * Get location of the highlighted character.
+ *
+ * @return location of the highlighted character
+ */
+ public int getShortcutIdx() {
+ return shortcutIdx;
+ }
+
/**
* The raw (uncolored) string.
*/
private String rawLabel;
+ /**
+ * Get the raw (uncolored) string.
+ *
+ * @return the raw (uncolored) string
+ */
+ public String getRawLabel() {
+ return rawLabel;
+ }
+
/**
* Public constructor.
*
package jexer.event;
/**
- * This class encapsulates several kinds of mouse input events.
+ * This class encapsulates several kinds of mouse input events. Note that
+ * the relative (x,y) ARE MUTABLE: TWidget's onMouse() handlers perform that
+ * update during event dispatching.
*/
public final class TMouseEvent extends TInputEvent {
return x;
}
+ /**
+ * Set x.
+ *
+ * @param x new relative X value
+ * @see jexer.TWidget#onMouseDown(TMouseEvent mouse)
+ * @see jexer.TWidget#onMouseDown(TMouseEvent mouse)
+ * @see jexer.TWidget#onMouseMotion(TMouseEvent mouse)
+ */
+ public void setX(final int x) {
+ this.x = x;
+ }
+
/**
* Mouse Y - relative coordinates.
*/
return y;
}
+ /**
+ * Set y.
+ *
+ * @param y new relative Y value
+ * @see jexer.TWidget#onMouseDown(TMouseEvent mouse)
+ * @see jexer.TWidget#onMouseDown(TMouseEvent mouse)
+ * @see jexer.TWidget#onMouseMotion(TMouseEvent mouse)
+ */
+ public void setY(final int y) {
+ this.y = y;
+ }
+
/**
* Mouse X - absolute screen coordinates.
*/
/**
* Drawing offset for x.
*/
- public int offsetX;
+ protected int offsetX;
+
+ /**
+ * Set drawing offset for x.
+ *
+ * @param offsetX new drawing offset
+ */
+ public final void setOffsetX(final int offsetX) {
+ this.offsetX = offsetX;
+ }
/**
* Drawing offset for y.
*/
- public int offsetY;
+ protected int offsetY;
+ /**
+ * Set drawing offset for y.
+ *
+ * @param offsetY new drawing offset
+ */
+ public final void setOffsetY(final int offsetY) {
+ this.offsetY = offsetY;
+ }
+
/**
* Ignore anything drawn right of clipRight.
*/
- public int clipRight;
+ protected int clipRight;
+
+ /**
+ * Get right drawing clipping boundary.
+ *
+ * @return drawing boundary
+ */
+ public final int getClipRight() {
+ return clipRight;
+ }
+
+ /**
+ * Set right drawing clipping boundary.
+ *
+ * @param clipRight new boundary
+ */
+ public final void setClipRight(final int clipRight) {
+ this.clipRight = clipRight;
+ }
/**
* Ignore anything drawn below clipBottom.
*/
- public int clipBottom;
+ protected int clipBottom;
+
+ /**
+ * Get bottom drawing clipping boundary.
+ *
+ * @return drawing boundary
+ */
+ public final int getClipBottom() {
+ return clipBottom;
+ }
+
+ /**
+ * Set bottom drawing clipping boundary.
+ *
+ * @param clipBottom new boundary
+ */
+ public final void setClipBottom(final int clipBottom) {
+ this.clipBottom = clipBottom;
+ }
/**
* Ignore anything drawn left of clipLeft.
*/
- public int clipLeft;
+ protected int clipLeft;
+
+ /**
+ * Get left drawing clipping boundary.
+ *
+ * @return drawing boundary
+ */
+ public final int getClipLeft() {
+ return clipLeft;
+ }
+
+ /**
+ * Set left drawing clipping boundary.
+ *
+ * @param clipLeft new boundary
+ */
+ public final void setClipLeft(final int clipLeft) {
+ this.clipLeft = clipLeft;
+ }
/**
* Ignore anything drawn above clipTop.
*/
- public int clipTop;
+ protected int clipTop;
+
+ /**
+ * Get top drawing clipping boundary.
+ *
+ * @return drawing boundary
+ */
+ public final int getClipTop() {
+ return clipTop;
+ }
+
+ /**
+ * Set top drawing clipping boundary.
+ *
+ * @param clipTop new boundary
+ */
+ public final void setClipTop(final int clipTop) {
+ this.clipTop = clipTop;
+ }
/**
* The physical screen last sent out on flush().
/**
* When true, logical != physical.
*/
- public boolean dirty;
+ protected boolean dirty;
/**
* Set if the user explicitly wants to redraw everything starting with a