public final TTerminalWindow openTerminal(final int x, final int y,
final int flags, final String commandLine) {
- return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s"));
+ return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s+"));
}
/**
public final TTerminalWindow openTerminal(final int x, final int y,
final int flags, final String commandLine, final boolean closeOnExit) {
- return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s"),
+ return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s+"),
closeOnExit);
}
*/
package jexer;
+import static jexer.TKeypress.kbEnter;
+import static jexer.TKeypress.kbEsc;
import static jexer.TKeypress.kbSpace;
import jexer.bits.CellAttributes;
import jexer.bits.GraphicsChars;
+import jexer.bits.MnemonicString;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
private boolean checked = false;
/**
- * Label for this checkbox.
+ * The shortcut and checkbox label.
*/
- private String label;
+ private MnemonicString mnemonic;
/**
* If true, use the window's background color.
// Set parent and window
super(parent, x, y, label.length() + 4, 1);
- this.label = label;
+ mnemonic = new MnemonicString(label);
this.checked = checked;
setCursorVisible(true);
*/
@Override
public void onKeypress(final TKeypressEvent keypress) {
- if (keypress.equals(kbSpace)) {
+ if (keypress.equals(kbSpace)
+ || keypress.equals(kbEnter)
+ ) {
checked = !checked;
return;
}
+ if (keypress.equals(kbEsc)) {
+ checked = false;
+ return;
+ }
+
// Pass to parent for the things we don't care about.
super.onKeypress(keypress);
}
@Override
public void draw() {
CellAttributes checkboxColor;
+ CellAttributes mnemonicColor;
if (isAbsoluteActive()) {
checkboxColor = getTheme().getColor("tcheckbox.active");
+ mnemonicColor = getTheme().getColor("tcheckbox.mnemonic.highlighted");
} else {
checkboxColor = getTheme().getColor("tcheckbox.inactive");
+ mnemonicColor = getTheme().getColor("tcheckbox.mnemonic");
}
if (useWindowBackground) {
CellAttributes background = getWindow().getBackground();
putCharXY(1, 0, ' ', checkboxColor);
}
putCharXY(2, 0, ']', checkboxColor);
- putStringXY(4, 0, label, checkboxColor);
+ putStringXY(4, 0, mnemonic.getRawLabel(), checkboxColor);
+ if (mnemonic.getShortcutIdx() >= 0) {
+ putCharXY(4 + mnemonic.getShortcutIdx(), 0,
+ mnemonic.getShortcut(), mnemonicColor);
+ }
}
// ------------------------------------------------------------------------
this.checked = checked;
}
+ /**
+ * Get the mnemonic string for this checkbox.
+ *
+ * @return mnemonic string
+ */
+ public MnemonicString getMnemonic() {
+ return mnemonic;
+ }
+
}
package jexer;
import jexer.bits.CellAttributes;
+import jexer.bits.MnemonicString;
/**
- * TLabel implements a simple label.
+ * TLabel implements a simple label, with an optional mnemonic hotkey action
+ * associated with it.
*/
public class TLabel extends TWidget {
// ------------------------------------------------------------------------
/**
- * Label text.
+ * The shortcut and label.
*/
- private String label = "";
+ private MnemonicString mnemonic;
+
+ /**
+ * The action to perform when the mnemonic shortcut is pressed.
+ */
+ private TAction action;
/**
* Label color.
this(parent, text, x, y, "tlabel");
}
+ /**
+ * Public constructor, using the default "tlabel" for colorKey.
+ *
+ * @param parent parent widget
+ * @param text label on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param action to call when shortcut is pressed
+ */
+ public TLabel(final TWidget parent, final String text, final int x,
+ final int y, final TAction action) {
+
+ this(parent, text, x, y, "tlabel", action);
+ }
+
/**
* Public constructor.
*
this(parent, text, x, y, colorKey, true);
}
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param text label on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param colorKey ColorTheme key color to use for foreground text
+ * @param action to call when shortcut is pressed
+ */
+ public TLabel(final TWidget parent, final String text, final int x,
+ final int y, final String colorKey, final TAction action) {
+
+ this(parent, text, x, y, colorKey, true, action);
+ }
+
/**
* Public constructor.
*
public TLabel(final TWidget parent, final String text, final int x,
final int y, final String colorKey, final boolean useWindowBackground) {
+ this(parent, text, x, y, colorKey, useWindowBackground, null);
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param text label on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param colorKey ColorTheme key color to use for foreground text
+ * @param useWindowBackground if true, use the window's background color
+ * @param action to call when shortcut is pressed
+ */
+ public TLabel(final TWidget parent, final String text, final int x,
+ final int y, final String colorKey, final boolean useWindowBackground,
+ final TAction action) {
+
// Set parent and window
super(parent, false, x, y, text.length(), 1);
- this.label = text;
+ mnemonic = new MnemonicString(text);
this.colorKey = colorKey;
this.useWindowBackground = useWindowBackground;
+ this.action = action;
}
// ------------------------------------------------------------------------
public void draw() {
// Setup my color
CellAttributes color = new CellAttributes();
+ CellAttributes mnemonicColor = new CellAttributes();
color.setTo(getTheme().getColor(colorKey));
+ mnemonicColor.setTo(getTheme().getColor("tlabel.mnemonic"));
if (useWindowBackground) {
CellAttributes background = getWindow().getBackground();
color.setBackColor(background.getBackColor());
+ mnemonicColor.setBackColor(background.getBackColor());
+ }
+ putStringXY(0, 0, mnemonic.getRawLabel(), color);
+ if (mnemonic.getShortcutIdx() >= 0) {
+ putCharXY(mnemonic.getShortcutIdx(), 0,
+ mnemonic.getShortcut(), mnemonicColor);
}
- putStringXY(0, 0, label, color);
}
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
/**
- * Get label text.
+ * Get label raw text.
*
* @return label text
*/
public String getLabel() {
- return label;
+ return mnemonic.getRawLabel();
+ }
+
+ /**
+ * Get the mnemonic string for this label.
+ *
+ * @return mnemonic string
+ */
+ public MnemonicString getMnemonic() {
+ return mnemonic;
}
/**
* @param label new label text
*/
public void setLabel(final String label) {
- this.label = label;
+ mnemonic = new MnemonicString(label);
+ }
+
+ /**
+ * Act as though the mnemonic shortcut was pressed.
+ */
+ public void dispatch() {
+ if (action != null) {
+ action.DO();
+ }
}
}
import jexer.bits.CellAttributes;
import jexer.bits.GraphicsChars;
+import jexer.bits.MnemonicString;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
import static jexer.TKeypress.*;
/**
* TRadioButton implements a selectable radio button.
+ *
+ * If the user clicks or presses space on this button, it is selected.
+ *
+ * If the user presses escape on this button, it is unselected.
*/
public class TRadioButton extends TWidget {
private boolean selected = false;
/**
- * Label for this radio button.
+ * The shortcut and radio button label.
*/
- private String label;
+ private MnemonicString mnemonic;
/**
* ID for this radio button. Buttons start counting at 1 in the
// Set parent and window
super(parent, x, y, label.length() + 4, 1);
- this.label = label;
+ mnemonic = new MnemonicString(label);
this.id = id;
setCursorVisible(true);
public void onMouseDown(final TMouseEvent mouse) {
if ((mouseOnRadioButton(mouse)) && (mouse.isMouse1())) {
// Switch state
- selected = !selected;
- if (selected) {
- ((TRadioGroup) getParent()).setSelected(this);
- }
+ selected = true;
+ ((TRadioGroup) getParent()).setSelected(this);
}
}
public void onKeypress(final TKeypressEvent keypress) {
if (keypress.equals(kbSpace)) {
- selected = !selected;
- if (selected) {
- ((TRadioGroup) getParent()).setSelected(this);
+ selected = true;
+ ((TRadioGroup) getParent()).setSelected(this);
+ return;
+ }
+
+ if (keypress.equals(kbEsc)) {
+ TRadioGroup parent = (TRadioGroup) getParent();
+ if (parent.requiresSelection == false) {
+ selected = false;
+ parent.setSelected(0);
}
return;
}
@Override
public void draw() {
CellAttributes radioButtonColor;
+ CellAttributes mnemonicColor;
if (isAbsoluteActive()) {
radioButtonColor = getTheme().getColor("tradiobutton.active");
+ mnemonicColor = getTheme().getColor("tradiobutton.mnemonic.highlighted");
} else {
radioButtonColor = getTheme().getColor("tradiobutton.inactive");
+ mnemonicColor = getTheme().getColor("tradiobutton.mnemonic");
}
putCharXY(0, 0, '(', radioButtonColor);
putCharXY(1, 0, ' ', radioButtonColor);
}
putCharXY(2, 0, ')', radioButtonColor);
- putStringXY(4, 0, label, radioButtonColor);
+ putStringXY(4, 0, mnemonic.getRawLabel(), radioButtonColor);
+ if (mnemonic.getShortcutIdx() >= 0) {
+ putCharXY(4 + mnemonic.getShortcutIdx(), 0,
+ mnemonic.getShortcut(), mnemonicColor);
+ }
}
// ------------------------------------------------------------------------
return id;
}
+ /**
+ * Get the mnemonic string for this button.
+ *
+ * @return mnemonic string
+ */
+ public MnemonicString getMnemonic() {
+ return mnemonic;
+ }
+
}
*/
private TRadioButton selectedButton = null;
+ /**
+ * If true, one of the children MUST be selected. Note package private
+ * access.
+ */
+ boolean requiresSelection = true;
+
// ------------------------------------------------------------------------
// Constructors -----------------------------------------------------------
// ------------------------------------------------------------------------
*/
void setSelected(final TRadioButton button) {
assert (button.isSelected());
- if (selectedButton != null) {
+ if ((selectedButton != null) && (selectedButton != button)) {
selectedButton.setSelected(false);
}
selectedButton = button;
}
+ /**
+ * Set the new selected radio button. 1-based.
+ *
+ * @param id ID of the selected button, or 0 to unselect
+ */
+ public void setSelected(final int id) {
+ if ((id < 0) || (id > getChildren().size())) {
+ return;
+ }
+
+ if (id == 0) {
+ for (TWidget widget: getChildren()) {
+ ((TRadioButton) widget).setSelected(false);
+ }
+ selectedButton = null;
+ return;
+ }
+ assert ((id > 0) && (id <= getChildren().size()));
+ TRadioButton button = (TRadioButton) (getChildren().get(id - 1));
+ button.setSelected(true);
+ selectedButton = button;
+ }
+
/**
* Convenience function to add a radio button to this group.
*
setWidth(label.length() + 7);
}
setHeight(getChildren().size() + 3);
- return new TRadioButton(this, buttonX, buttonY, label,
+ TRadioButton button = new TRadioButton(this, buttonX, buttonY, label,
getChildren().size() + 1);
+
+ // Default to the first item on the list.
+ activate(getChildren().get(0));
+
+ return button;
}
}
public TTerminalWindow(final TApplication application, final int x,
final int y, final String commandLine) {
- this(application, x, y, RESIZABLE, commandLine.split("\\s"),
+ this(application, x, y, RESIZABLE, commandLine.split("\\s+"),
System.getProperty("jexer.TTerminal.closeOnExit",
"false").equals("true"));
}
public TTerminalWindow(final TApplication application, final int x,
final int y, final String commandLine, final boolean closeOnExit) {
- this(application, x, y, RESIZABLE, commandLine.split("\\s"),
+ this(application, x, y, RESIZABLE, commandLine.split("\\s+"),
closeOnExit);
}
equals("true"))
) {
ptypipe = true;
- spawnShell(cmdShellPtypipe.split("\\s"));
+ spawnShell(cmdShellPtypipe.split("\\s+"));
} else if (System.getProperty("os.name").startsWith("Windows")) {
- spawnShell(cmdShellWindows.split("\\s"));
+ spawnShell(cmdShellWindows.split("\\s+"));
} else if (System.getProperty("os.name").startsWith("Mac")) {
- spawnShell(cmdShellBSD.split("\\s"));
+ spawnShell(cmdShellBSD.split("\\s+"));
} else if (System.getProperty("os.name").startsWith("Linux")) {
- spawnShell(cmdShellGNU.split("\\s"));
+ spawnShell(cmdShellGNU.split("\\s+"));
} else {
// When all else fails, assume GNU.
- spawnShell(cmdShellGNU.split("\\s"));
+ spawnShell(cmdShellGNU.split("\\s+"));
}
}
}
// If I have any buttons on me AND this is an Alt-key that matches
- // its mnemonic, send it an Enter keystroke
+ // its mnemonic, send it an Enter keystroke.
for (TWidget widget: children) {
if (widget instanceof TButton) {
TButton button = (TButton) widget;
}
}
+ // If I have any labels on me AND this is an Alt-key that matches
+ // its mnemonic, call its action.
+ for (TWidget widget: children) {
+ if (widget instanceof TLabel) {
+ TLabel label = (TLabel) widget;
+ if (!keypress.getKey().isFnKey()
+ && keypress.getKey().isAlt()
+ && !keypress.getKey().isCtrl()
+ && (Character.toLowerCase(label.getMnemonic().getShortcut())
+ == Character.toLowerCase(keypress.getKey().getChar()))
+ ) {
+
+ label.dispatch();
+ return;
+ }
+ }
+ }
+
+ // If I have any radiobuttons on me AND this is an Alt-key that
+ // matches its mnemonic, select it and send a Space to it.
+ for (TWidget widget: children) {
+ if (widget instanceof TRadioButton) {
+ TRadioButton button = (TRadioButton) widget;
+ if (button.isEnabled()
+ && !keypress.getKey().isFnKey()
+ && keypress.getKey().isAlt()
+ && !keypress.getKey().isCtrl()
+ && (Character.toLowerCase(button.getMnemonic().getShortcut())
+ == Character.toLowerCase(keypress.getKey().getChar()))
+ ) {
+ activate(widget);
+ widget.onKeypress(new TKeypressEvent(kbSpace));
+ return;
+ }
+ }
+ if (widget instanceof TRadioGroup) {
+ for (TWidget child: widget.getChildren()) {
+ if (child instanceof TRadioButton) {
+ TRadioButton button = (TRadioButton) child;
+ if (button.isEnabled()
+ && !keypress.getKey().isFnKey()
+ && keypress.getKey().isAlt()
+ && !keypress.getKey().isCtrl()
+ && (Character.toLowerCase(button.getMnemonic().getShortcut())
+ == Character.toLowerCase(keypress.getKey().getChar()))
+ ) {
+ activate(widget);
+ widget.activate(child);
+ child.onKeypress(new TKeypressEvent(kbSpace));
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // If I have any checkboxes on me AND this is an Alt-key that matches
+ // its mnemonic, select it and set it to checked.
+ for (TWidget widget: children) {
+ if (widget instanceof TCheckBox) {
+ TCheckBox checkBox = (TCheckBox) widget;
+ if (checkBox.isEnabled()
+ && !keypress.getKey().isFnKey()
+ && keypress.getKey().isAlt()
+ && !keypress.getKey().isCtrl()
+ && (Character.toLowerCase(checkBox.getMnemonic().getShortcut())
+ == Character.toLowerCase(keypress.getKey().getChar()))
+ ) {
+ activate(checkBox);
+ checkBox.setChecked(true);
+ return;
+ }
+ }
+ }
+
// Dispatch the keypress to an active widget
for (TWidget widget: children) {
if (widget.active) {
}
}
+ /**
+ * Reset the tab order of children to match their position in the list.
+ * Available so that subclasses can re-order their widgets if needed.
+ */
+ protected void resetTabOrder() {
+ for (int i = 0; i < children.size(); i++) {
+ children.get(i).tabOrder = i;
+ }
+ }
+
/**
* Switch the active child.
*
return;
}
- if (activeChild == null) {
- return;
- }
TWidget child = null;
for (TWidget widget: children) {
if ((widget.enabled)
}
}
if ((child != null) && (child != activeChild)) {
- activeChild.active = false;
+ if (activeChild != null) {
+ activeChild.active = false;
+ }
assert (child.enabled);
child.active = true;
activeChild = child;
public final void switchWidget(final boolean forward) {
// No children: do nothing.
- if ((children.size() == 0) || (activeChild == null)) {
+ if (children.size() == 0) {
return;
}
// Two or more children: go forward or backward to the next enabled
// child.
- int tabOrder = activeChild.tabOrder;
+ int tabOrder = 0;
+ if (activeChild != null) {
+ tabOrder = activeChild.tabOrder;
+ }
do {
if (forward) {
tabOrder++;
tabOrder = 0;
}
- if (activeChild.tabOrder == tabOrder) {
+ if (activeChild == null) {
+ if (tabOrder == 0) {
+ // We wrapped around
+ break;
+ }
+ } else if (activeChild.tabOrder == tabOrder) {
// We wrapped around
break;
}
&& !(children.get(tabOrder) instanceof THScroller)
&& !(children.get(tabOrder) instanceof TVScroller));
- assert (children.get(tabOrder).enabled);
+ if (activeChild != null) {
+ assert (children.get(tabOrder).enabled);
- activeChild.active = false;
- children.get(tabOrder).active = true;
- activeChild = children.get(tabOrder);
+ activeChild.active = false;
+ }
+ if (children.get(tabOrder).enabled == true) {
+ children.get(tabOrder).active = true;
+ activeChild = children.get(tabOrder);
+ }
}
/**
return addLabel(text, x, y, "tlabel");
}
+ /**
+ * Convenience function to add a label to this container/window.
+ *
+ * @param text label
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param action to call when shortcut is pressed
+ * @return the new label
+ */
+ public final TLabel addLabel(final String text, final int x, final int y,
+ final TAction action) {
+
+ return addLabel(text, x, y, "tlabel", action);
+ }
+
/**
* Convenience function to add a label to this container/window.
*
return new TLabel(this, text, x, y, colorKey);
}
+ /**
+ * Convenience function to add a label to this container/window.
+ *
+ * @param text label
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param colorKey ColorTheme key color to use for foreground text.
+ * Default is "tlabel"
+ * @param action to call when shortcut is pressed
+ * @return the new label
+ */
+ public final TLabel addLabel(final String text, final int x, final int y,
+ final String colorKey, final TAction action) {
+
+ return new TLabel(this, text, x, y, colorKey, action);
+ }
+
/**
* Convenience function to add a label to this container/window.
*
return new TLabel(this, text, x, y, colorKey, useWindowBackground);
}
+ /**
+ * Convenience function to add a label to this container/window.
+ *
+ * @param text label
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param colorKey ColorTheme key color to use for foreground text.
+ * Default is "tlabel"
+ * @param useWindowBackground if true, use the window's background color
+ * @param action to call when shortcut is pressed
+ * @return the new label
+ */
+ public final TLabel addLabel(final String text, final int x, final int y,
+ final String colorKey, final boolean useWindowBackground,
+ final TAction action) {
+
+ return new TLabel(this, text, x, y, colorKey, useWindowBackground,
+ action);
+ }
+
/**
* Convenience function to add a button to this container/window.
*
*/
private Screen otherScreen;
- /**
- * The mouse X position as seen on the other screen.
- */
- private int otherMouseX = -1;
-
- /**
- * The mouse Y position as seen on the other screen.
- */
- private int otherMouseY = -1;
-
/**
* The session information.
*/
otherScreen = new LogicalScreen();
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
/**
otherScreen = new LogicalScreen();
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
/**
otherScreen = new LogicalScreen();
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
/**
otherScreen = new LogicalScreen();
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
// ------------------------------------------------------------------------
event.setY(mouse.getY() - 1);
event.setAbsoluteX(event.getX());
event.setAbsoluteY(event.getY());
- otherMouseX = event.getX() + getX() + 1;
- otherMouseY = event.getY() + getY() + 1;
synchronized (eventQueue) {
eventQueue.add(event);
}
synchronized (listener) {
listener.notifyAll();
}
- } else {
- otherMouseX = -1;
- otherMouseY = -1;
}
super.onMouseMotion(mouse);
}
}
}
- // If the mouse pointer is over the other window, draw its
- // pointer again here. (Their TApplication drew it, then our
- // TApplication drew it again (undo-ing it), so now we draw it a
- // third time so that it is visible.)
- if ((otherMouseX != -1) && (otherMouseY != -1)) {
- CellAttributes attr = getAttrXY(otherMouseX, otherMouseY);
- attr.setForeColor(attr.getForeColor().invert());
- attr.setBackColor(attr.getBackColor().invert());
- putAttrXY(otherMouseX, otherMouseY, attr, false);
- }
-
// If their cursor is visible, draw that here too.
if (otherScreen.isCursorVisible()) {
setCursorX(otherScreen.getCursorX() + 1);
color.setBackColor(Color.BLUE);
color.setBold(true);
colors.put("tlabel", color);
+ color = new CellAttributes();
+ color.setForeColor(Color.YELLOW);
+ color.setBackColor(Color.BLUE);
+ color.setBold(true);
+ colors.put("tlabel.mnemonic", color);
// TText text
color = new CellAttributes();
color.setBackColor(Color.BLACK);
color.setBold(true);
colors.put("tcheckbox.active", color);
+ color = new CellAttributes();
+ color.setForeColor(Color.YELLOW);
+ color.setBackColor(Color.BLUE);
+ color.setBold(true);
+ colors.put("tcheckbox.mnemonic", color);
+ color = new CellAttributes();
+ color.setForeColor(Color.RED);
+ color.setBackColor(Color.BLACK);
+ color.setBold(true);
+ colors.put("tcheckbox.mnemonic.highlighted", color);
// TComboBox
color = new CellAttributes();
color.setBackColor(Color.BLACK);
color.setBold(true);
colors.put("tradiobutton.active", color);
+ color = new CellAttributes();
+ color.setForeColor(Color.YELLOW);
+ color.setBackColor(Color.BLUE);
+ color.setBold(true);
+ colors.put("tradiobutton.mnemonic", color);
+ color = new CellAttributes();
+ color.setForeColor(Color.RED);
+ color.setBackColor(Color.BLACK);
+ color.setBold(true);
+ colors.put("tradiobutton.mnemonic.highlighted", color);
// TRadioGroup
color = new CellAttributes();