Merge branch 'subtree'
[fanfix.git] / src / jexer / TKeypress.java
index 06518ebba9fd3966f90f4a6d343e09216d912af4..20db8bb267ebc43389f2d900c49aa5b3462d22d1 100644 (file)
@@ -1,29 +1,27 @@
 /*
  * Jexer - Java Text User Interface
  *
- * License: LGPLv3 or later
+ * The MIT License (MIT)
  *
- * 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) 2019 Kevin Lamonte
  *
- *     Copyright (C) 2015  Kevin Lamonte
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * 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.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * 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
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * @author Kevin Lamonte [kevin.lamonte@gmail.com]
  * @version 1
@@ -33,7 +31,11 @@ package jexer;
 /**
  * This class represents keystrokes.
  */
-public final class TKeypress {
+public class TKeypress {
+
+    // ------------------------------------------------------------------------
+    // Constants --------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     // Various special keystrokes
 
@@ -172,358 +174,9 @@ public final class TKeypress {
      */
     public static final int ESC         = 43;
 
-    /**
-     * If true, ch is meaningless, use keyCode instead.
-     */
-    private boolean isFunctionKey;
-
-    /**
-     * Getter for isFunctionKey.
-     *
-     * @return if true, ch is meaningless, use keyCode instead
-     */
-    public boolean isFnKey() {
-        return isFunctionKey;
-    }
-
-    /**
-     * Will be set to F1, F2, HOME, END, etc. if isKey is true.
-     */
-    private int keyCode;
-
-    /**
-     * Getter for function key code.
-     *
-     * @return function key code int value (only valid is isKey is true)
-     */
-    public int getKeyCode() {
-        return keyCode;
-    }
-
-    /**
-     * Keystroke modifier ALT.
-     */
-    private boolean alt;
-
-    /**
-     * Getter for ALT.
-     *
-     * @return alt value
-     */
-    public boolean isAlt() {
-        return alt;
-    }
-
-    /**
-     * Keystroke modifier CTRL.
-     */
-    private boolean ctrl;
-
-    /**
-     * Getter for CTRL.
-     *
-     * @return ctrl value
-     */
-    public boolean isCtrl() {
-        return ctrl;
-    }
-
-    /**
-     * Keystroke modifier SHIFT.
-     */
-    private boolean shift;
-
-    /**
-     * Getter for SHIFT.
-     *
-     * @return shift value
-     */
-    public boolean isShift() {
-        return shift;
-    }
-
-    /**
-     * The character received.
-     */
-    private char ch;
-
-    /**
-     * Getter for character.
-     *
-     * @return the character (only valid if isKey is false)
-     */
-    public char getChar() {
-        return ch;
-    }
-
-    /**
-     * Public constructor makes an immutable instance.
-     *
-     * @param isKey is true, this is a function key
-     * @param fnKey the function key code (only valid if isKey is true)
-     * @param ch the character (only valid if fnKey is false)
-     * @param alt if true, ALT was pressed with this keystroke
-     * @param ctrl if true, CTRL was pressed with this keystroke
-     * @param shift if true, SHIFT was pressed with this keystroke
-     */
-    public TKeypress(final boolean isKey, final int fnKey, final char ch,
-            final boolean alt, final boolean ctrl, final boolean shift) {
-
-        this.isFunctionKey = isKey;
-        this.keyCode       = fnKey;
-        this.ch            = ch;
-        this.alt           = alt;
-        this.ctrl          = ctrl;
-        this.shift         = shift;
-    }
-
-    /**
-     * Comparison check.  All fields must match to return true.
-     *
-     * @param rhs another TKeypress instance
-     * @return true if all fields are equal
-     */
-    @Override
-    public boolean equals(final Object rhs) {
-        if (!(rhs instanceof TKeypress)) {
-            return false;
-        }
-
-        TKeypress that = (TKeypress) rhs;
-        return ((isFunctionKey == that.isFunctionKey)
-                && (keyCode == that.keyCode)
-                && (ch == that.ch)
-                && (alt == that.alt)
-                && (ctrl == that.ctrl)
-                && (shift == that.shift));
-    }
-
-    /**
-     * Hashcode uses all fields in equals().
-     *
-     * @return the hash
-     */
-    @Override
-    public int hashCode() {
-        int A = 13;
-        int B = 23;
-        int hash = A;
-        hash = (B * hash) + (isFunctionKey ? 1 : 0);
-        hash = (B * hash) + keyCode;
-        hash = (B * hash) + ch;
-        hash = (B * hash) + (alt ? 1 : 0);
-        hash = (B * hash) + (ctrl ? 1 : 0);
-        hash = (B * hash) + (shift ? 1 : 0);
-        return hash;
-    }
-
-    /**
-     * Make human-readable description of this TKeypress.
-     *
-     * @return displayable String
-     */
-    @Override
-    public String toString() {
-        if (isFunctionKey) {
-            switch (keyCode) {
-            case F1:
-                return String.format("%s%s%sF1",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F2:
-                return String.format("%s%s%sF2",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F3:
-                return String.format("%s%s%sF3",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F4:
-                return String.format("%s%s%sF4",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F5:
-                return String.format("%s%s%sF5",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F6:
-                return String.format("%s%s%sF6",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F7:
-                return String.format("%s%s%sF7",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F8:
-                return String.format("%s%s%sF8",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F9:
-                return String.format("%s%s%sF9",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F10:
-                return String.format("%s%s%sF10",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F11:
-                return String.format("%s%s%sF11",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case F12:
-                return String.format("%s%s%sF12",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case HOME:
-                return String.format("%s%s%sHOME",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case END:
-                return String.format("%s%s%sEND",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case PGUP:
-                return String.format("%s%s%sPGUP",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case PGDN:
-                return String.format("%s%s%sPGDN",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case INS:
-                return String.format("%s%s%sINS",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case DEL:
-                return String.format("%s%s%sDEL",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case RIGHT:
-                return String.format("%s%s%sRIGHT",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case LEFT:
-                return String.format("%s%s%sLEFT",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case UP:
-                return String.format("%s%s%sUP",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case DOWN:
-                return String.format("%s%s%sDOWN",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case TAB:
-                return String.format("%s%s%sTAB",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case BTAB:
-                return String.format("%s%s%sBTAB",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case ENTER:
-                return String.format("%s%s%sENTER",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            case ESC:
-                return String.format("%s%s%sESC",
-                        ctrl ? "Ctrl+" : "",
-                                alt ? "Alt+" : "",
-                                        shift ? "Shift+" : "");
-            default:
-                return String.format("--UNKNOWN--");
-            }
-        } else {
-            if (alt && !shift && !ctrl) {
-                // Alt-X
-                return String.format("Alt+%c", Character.toUpperCase(ch));
-            } else if (!alt && shift && !ctrl) {
-                // Shift-X
-                return String.format("%c", ch);
-            } else if (!alt && !shift && ctrl) {
-                // Ctrl-X
-                return String.format("Ctrl+%c", ch);
-            } else if (alt && shift && !ctrl) {
-                // Alt-Shift-X
-                return String.format("Alt+Shift+%c", ch);
-            } else if (!alt && shift && ctrl) {
-                // Ctrl-Shift-X
-                return String.format("Ctrl+Shift+%c", ch);
-            } else if (alt && !shift && ctrl) {
-                // Ctrl-Alt-X
-                return String.format("Ctrl+Alt+%c", Character.toUpperCase(ch));
-            } else if (alt && shift && ctrl) {
-                // Ctrl-Alt-Shift-X
-                return String.format("Ctrl+Alt+Shift+%c",
-                        Character.toUpperCase(ch));
-            } else {
-                // X
-                return String.format("%c", ch);
-            }
-        }
-    }
-
-    /**
-     * Convert a keypress to lowercase.  Function keys and alt/ctrl keys are
-     * not converted.
-     *
-     * @return a new instance with the key converted
-     */
-    public TKeypress toLowerCase() {
-        TKeypress newKey = new TKeypress(isFunctionKey, keyCode, ch, alt, ctrl,
-                shift);
-        if (!isFunctionKey && (ch >= 'A') && (ch <= 'Z') && !ctrl && !alt) {
-            newKey.shift = false;
-            newKey.ch += 32;
-        }
-        return newKey;
-    }
-
-    /**
-     * Convert a keypress to uppercase.  Function keys and alt/ctrl keys are
-     * not converted.
-     *
-     * @return a new instance with the key converted
-     */
-    public TKeypress toUpperCase() {
-        TKeypress newKey = new TKeypress(isFunctionKey, keyCode, ch, alt, ctrl,
-                shift);
-        if (!isFunctionKey && (ch >= 'a') && (ch <= 'z') && !ctrl && !alt) {
-            newKey.shift = true;
-            newKey.ch -= 32;
-        }
-        return newKey;
-    }
-
-    // Special "no-key" keypress, used to ignore undefined keystrokes
-    public static final TKeypress kbNoKey = new TKeypress(true,
-            TKeypress.NONE, ' ', false, false, false);
+    // Special "no-key" keypress, used to ignore undefined keystrokes
+    public static final TKeypress kbNoKey = new TKeypress(true,
+            TKeypress.NONE, ' ', false, false, false);
 
     // Normal keys
     public static final TKeypress kbF1 = new TKeypress(true,
@@ -834,6 +487,26 @@ public final class TKeypress {
             0, 'y', true, false, false);
     public static final TKeypress kbAltZ = new TKeypress(false,
             0, 'z', true, false, false);
+    public static final TKeypress kbAlt0 = new TKeypress(false,
+            0, '0', true, false, false);
+    public static final TKeypress kbAlt1 = new TKeypress(false,
+            0, '1', true, false, false);
+    public static final TKeypress kbAlt2 = new TKeypress(false,
+            0, '2', true, false, false);
+    public static final TKeypress kbAlt3 = new TKeypress(false,
+            0, '3', true, false, false);
+    public static final TKeypress kbAlt4 = new TKeypress(false,
+            0, '4', true, false, false);
+    public static final TKeypress kbAlt5 = new TKeypress(false,
+            0, '5', true, false, false);
+    public static final TKeypress kbAlt6 = new TKeypress(false,
+            0, '6', true, false, false);
+    public static final TKeypress kbAlt7 = new TKeypress(false,
+            0, '7', true, false, false);
+    public static final TKeypress kbAlt8 = new TKeypress(false,
+            0, '8', true, false, false);
+    public static final TKeypress kbAlt9 = new TKeypress(false,
+            0, '9', true, false, false);
     public static final TKeypress kbCtrlA = new TKeypress(false,
             0, 'A', false, true, false);
     public static final TKeypress kbCtrlB = new TKeypress(false,
@@ -939,6 +612,41 @@ public final class TKeypress {
     public static final TKeypress kbAltShiftZ = new TKeypress(false,
             0, 'Z', true, false, true);
 
+    public static final TKeypress kbAltShiftHome = new TKeypress(true,
+            TKeypress.HOME, ' ', true, false, true);
+    public static final TKeypress kbAltShiftEnd = new TKeypress(true,
+            TKeypress.END, ' ', true, false, true);
+    public static final TKeypress kbAltShiftPgUp = new TKeypress(true,
+            TKeypress.PGUP, ' ', true, false, true);
+    public static final TKeypress kbAltShiftPgDn = new TKeypress(true,
+            TKeypress.PGDN, ' ', true, false, true);
+    public static final TKeypress kbAltShiftUp = new TKeypress(true,
+            TKeypress.UP, ' ', true, false, true);
+    public static final TKeypress kbAltShiftDown = new TKeypress(true,
+            TKeypress.DOWN, ' ', true, false, true);
+    public static final TKeypress kbAltShiftLeft = new TKeypress(true,
+            TKeypress.LEFT, ' ', true, false, true);
+    public static final TKeypress kbAltShiftRight = new TKeypress(true,
+            TKeypress.RIGHT, ' ', true, false, true);
+
+    public static final TKeypress kbCtrlShiftHome = new TKeypress(true,
+            TKeypress.HOME, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftEnd = new TKeypress(true,
+            TKeypress.END, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftPgUp = new TKeypress(true,
+            TKeypress.PGUP, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftPgDn = new TKeypress(true,
+            TKeypress.PGDN, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftUp = new TKeypress(true,
+            TKeypress.UP, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftDown = new TKeypress(true,
+            TKeypress.DOWN, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftLeft = new TKeypress(true,
+            TKeypress.LEFT, ' ', false, true, true);
+    public static final TKeypress kbCtrlShiftRight = new TKeypress(true,
+            TKeypress.RIGHT, ' ', false, true, true);
+
+
     /**
      * Backspace as ^H.
      */
@@ -949,6 +657,412 @@ public final class TKeypress {
      * Backspace as ^?.
      */
     public static final TKeypress kbBackspaceDel = new TKeypress(false,
-            0, (char)0x7F, false, false, false);
+            0, (char) 0x7F, false, false, false);
+
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * If true, ch is meaningless, use keyCode instead.
+     */
+    private boolean isFunctionKey;
+
+    /**
+     * Will be set to F1, F2, HOME, END, etc. if isKey is true.
+     */
+    private int keyCode;
+
+    /**
+     * Keystroke modifier ALT.
+     */
+    private boolean alt;
+
+    /**
+     * Keystroke modifier CTRL.
+     */
+    private boolean ctrl;
+
+    /**
+     * Keystroke modifier SHIFT.
+     */
+    private boolean shift;
+
+    /**
+     * The character received.
+     */
+    private int ch;
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Public constructor makes an immutable instance.
+     *
+     * @param isKey is true, this is a function key
+     * @param fnKey the function key code (only valid if isKey is true)
+     * @param ch the character (only valid if fnKey is false)
+     * @param alt if true, ALT was pressed with this keystroke
+     * @param ctrl if true, CTRL was pressed with this keystroke
+     * @param shift if true, SHIFT was pressed with this keystroke
+     */
+    public TKeypress(final boolean isKey, final int fnKey, final int ch,
+            final boolean alt, final boolean ctrl, final boolean shift) {
+
+        this.isFunctionKey = isKey;
+        this.keyCode       = fnKey;
+        this.ch            = ch;
+        this.alt           = alt;
+        this.ctrl          = ctrl;
+        this.shift         = shift;
+    }
+
+    // ------------------------------------------------------------------------
+    // TKeypress --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Getter for isFunctionKey.
+     *
+     * @return if true, ch is meaningless, use keyCode instead
+     */
+    public boolean isFnKey() {
+        return isFunctionKey;
+    }
+
+    /**
+     * Getter for function key code.
+     *
+     * @return function key code int value (only valid is isKey is true)
+     */
+    public int getKeyCode() {
+        return keyCode;
+    }
+
+    /**
+     * Getter for ALT.
+     *
+     * @return alt value
+     */
+    public boolean isAlt() {
+        return alt;
+    }
+
+    /**
+     * Getter for CTRL.
+     *
+     * @return ctrl value
+     */
+    public boolean isCtrl() {
+        return ctrl;
+    }
+
+    /**
+     * Getter for SHIFT.
+     *
+     * @return shift value
+     */
+    public boolean isShift() {
+        return shift;
+    }
+
+    /**
+     * Getter for character.
+     *
+     * @return the character (only valid if isKey is false)
+     */
+    public int getChar() {
+        return ch;
+    }
+
+    /**
+     * Create a duplicate instance.
+     *
+     * @return duplicate intance
+     */
+    public TKeypress dup() {
+        TKeypress keypress = new TKeypress(isFunctionKey, keyCode, ch,
+            alt, ctrl, shift);
+        return keypress;
+    }
+
+    /**
+     * Comparison check.  All fields must match to return true.
+     *
+     * @param rhs another TKeypress instance
+     * @return true if all fields are equal
+     */
+    @Override
+    public boolean equals(final Object rhs) {
+        if (!(rhs instanceof TKeypress)) {
+            return false;
+        }
+
+        TKeypress that = (TKeypress) rhs;
+        return ((isFunctionKey == that.isFunctionKey)
+                && (keyCode == that.keyCode)
+                && (ch == that.ch)
+                && (alt == that.alt)
+                && (ctrl == that.ctrl)
+                && (shift == that.shift));
+    }
+
+    /**
+     * Comparison check, omitting the ctrl/alt/shift flags.
+     *
+     * @param rhs another TKeypress instance
+     * @return true if all fields (except for ctrl/alt/shift) are equal
+     */
+    public boolean equalsWithoutModifiers(final Object rhs) {
+        if (!(rhs instanceof TKeypress)) {
+            return false;
+        }
+
+        TKeypress that = (TKeypress) rhs;
+        return ((isFunctionKey == that.isFunctionKey)
+                && (keyCode == that.keyCode)
+                && (ch == that.ch));
+    }
+
+    /**
+     * Hashcode uses all fields in equals().
+     *
+     * @return the hash
+     */
+    @Override
+    public int hashCode() {
+        int A = 13;
+        int B = 23;
+        int hash = A;
+        hash = (B * hash) + (isFunctionKey ? 1 : 0);
+        hash = (B * hash) + keyCode;
+        hash = (B * hash) + ch;
+        hash = (B * hash) + (alt ? 1 : 0);
+        hash = (B * hash) + (ctrl ? 1 : 0);
+        hash = (B * hash) + (shift ? 1 : 0);
+        return hash;
+    }
+
+    /**
+     * Make human-readable description of this TKeypress.
+     *
+     * @return displayable String
+     */
+    @Override
+    public String toString() {
+        // Special case: Enter is "<arrow> <line> <angle>"
+        if (equals(kbEnter)) {
+            return "\u25C0\u2500\u2518";
+        }
+
+        // Special case: Space is "Space"
+        if (equals(kbSpace)) {
+            return "Space";
+        }
+
+        if (equals(kbShiftLeft)) {
+            return "Shift+\u2190";
+        }
+        if (equals(kbShiftRight)) {
+            return "Shift+\u2192";
+        }
+
+        if (isFunctionKey) {
+            switch (keyCode) {
+            case F1:
+                return String.format("%s%s%sF1",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F2:
+                return String.format("%s%s%sF2",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F3:
+                return String.format("%s%s%sF3",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F4:
+                return String.format("%s%s%sF4",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F5:
+                return String.format("%s%s%sF5",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F6:
+                return String.format("%s%s%sF6",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F7:
+                return String.format("%s%s%sF7",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F8:
+                return String.format("%s%s%sF8",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F9:
+                return String.format("%s%s%sF9",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F10:
+                return String.format("%s%s%sF10",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F11:
+                return String.format("%s%s%sF11",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case F12:
+                return String.format("%s%s%sF12",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case HOME:
+                return String.format("%s%s%sHOME",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case END:
+                return String.format("%s%s%sEND",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case PGUP:
+                return String.format("%s%s%sPGUP",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case PGDN:
+                return String.format("%s%s%sPGDN",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case INS:
+                return String.format("%s%s%sINS",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case DEL:
+                return String.format("%s%s%sDEL",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case RIGHT:
+                return String.format("%s%s%sRIGHT",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case LEFT:
+                return String.format("%s%s%sLEFT",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case UP:
+                return String.format("%s%s%sUP",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case DOWN:
+                return String.format("%s%s%sDOWN",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case TAB:
+                return String.format("%s%s%sTAB",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case BTAB:
+                return String.format("%s%s%sBTAB",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case ENTER:
+                return String.format("%s%s%sENTER",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            case ESC:
+                return String.format("%s%s%sESC",
+                        ctrl ? "Ctrl+" : "",
+                                alt ? "Alt+" : "",
+                                        shift ? "Shift+" : "");
+            default:
+                return String.format("--UNKNOWN--");
+            }
+        } else {
+            if (alt && !shift && !ctrl) {
+                // Alt-X
+                return String.format("Alt+%c", Character.toUpperCase(ch));
+            } else if (!alt && shift && !ctrl) {
+                // Shift-X
+                return String.format("%c", ch);
+            } else if (!alt && !shift && ctrl) {
+                // Ctrl-X
+                return String.format("Ctrl+%c", ch);
+            } else if (alt && shift && !ctrl) {
+                // Alt-Shift-X
+                return String.format("Alt+Shift+%c", ch);
+            } else if (!alt && shift && ctrl) {
+                // Ctrl-Shift-X
+                return String.format("Ctrl+Shift+%c", ch);
+            } else if (alt && !shift && ctrl) {
+                // Ctrl-Alt-X
+                return String.format("Ctrl+Alt+%c", Character.toUpperCase(ch));
+            } else if (alt && shift && ctrl) {
+                // Ctrl-Alt-Shift-X
+                return String.format("Ctrl+Alt+Shift+%c",
+                        Character.toUpperCase(ch));
+            } else {
+                // X
+                return String.format("%c", ch);
+            }
+        }
+    }
+
+    /**
+     * Convert a keypress to lowercase.  Function keys and alt/ctrl keys are
+     * not converted.
+     *
+     * @return a new instance with the key converted
+     */
+    public TKeypress toLowerCase() {
+        TKeypress newKey = new TKeypress(isFunctionKey, keyCode, ch, alt, ctrl,
+                shift);
+        if (!isFunctionKey && (ch >= 'A') && (ch <= 'Z') && !ctrl && !alt) {
+            newKey.shift = false;
+            newKey.ch += 32;
+        }
+        return newKey;
+    }
+
+    /**
+     * Convert a keypress to uppercase.  Function keys and alt/ctrl keys are
+     * not converted.
+     *
+     * @return a new instance with the key converted
+     */
+    public TKeypress toUpperCase() {
+        TKeypress newKey = new TKeypress(isFunctionKey, keyCode, ch, alt, ctrl,
+                shift);
+        if (!isFunctionKey && (ch >= 'a') && (ch <= 'z') && !ctrl && !alt) {
+            newKey.shift = true;
+            newKey.ch -= 32;
+        }
+        return newKey;
+    }
 
 }