From b299e69c251b7639440bfb914dbbdc51aa689af5 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Wed, 11 Mar 2015 17:07:00 -0400 Subject: [PATCH] immutable TKeypressEvent --- README.md | 2 - src/jexer/TApplication.java | 2 +- src/jexer/TKeypress.java | 74 ++++- src/jexer/event/TCommandEvent.java | 6 +- src/jexer/event/TKeypressEvent.java | 69 ++++- src/jexer/io/ECMA48Terminal.java | 407 +++++++--------------------- 6 files changed, 234 insertions(+), 326 deletions(-) diff --git a/README.md b/README.md index ced2867..4166dc7 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,6 @@ version 1.0: - TCommand use getters/setters - TCommand.Type: switch to int so that subclasses can make more kinds of commands - - TKeypress use getters/setters to make immutable - - TKeypressEvent use getters/setters to make immutable - TMouseEvent use getters/setters to make immutable - Get a movable window on screen - TWidget diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index e9256a1..91d0ea7 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -332,7 +332,7 @@ public class TApplication { // DEBUG if (event instanceof TKeypressEvent) { TKeypressEvent keypress = (TKeypressEvent) event; - if (keypress.key.equals(kbAltX)) { + if (keypress.equals(kbAltX)) { quit = true; return; } diff --git a/src/jexer/TKeypress.java b/src/jexer/TKeypress.java index aeaadb0..54be5d3 100644 --- a/src/jexer/TKeypress.java +++ b/src/jexer/TKeypress.java @@ -33,7 +33,7 @@ package jexer; /** * This class represents keystrokes. */ -public class TKeypress { +public final class TKeypress { // Various special keystrokes @@ -175,35 +175,89 @@ public class TKeypress { /** * If true, ch is meaningless, use fnKey instead. */ - public boolean isKey; + private boolean isKey; + + /** + * Getter for isKey. + * + * @return if true, ch is meaningless, use fnKey instead + */ + public boolean getIsKey() { + return isKey; + } /** * Will be set to F1, F2, HOME, END, etc. if isKey is true. */ - public int fnKey; + private int fnKey; + + /** + * Getter for fnKey. + * + * @return fnKey value (only valid is isKey is true) + */ + public int getFnKey() { + return fnKey; + } /** * Keystroke modifier ALT. */ - public boolean alt; + private boolean alt; + + /** + * Getter for ALT. + * + * @return alt value + */ + public boolean getAlt() { + return alt; + } /** * Keystroke modifier CTRL. */ - public boolean ctrl; + private boolean ctrl; + + /** + * Getter for CTRL. + * + * @return ctrl value + */ + public boolean getCtrl() { + return ctrl; + } /** * Keystroke modifier SHIFT. */ - public boolean shift; + private boolean shift; + + /** + * Getter for SHIFT. + * + * @return shift value + */ + public boolean getShift() { + return shift; + } /** * The character received. */ - public char ch; + private char ch; + + /** + * Getter for character. + * + * @return the character (only valid if isKey is false) + */ + public char getCh() { + return ch; + } /** - * Convenience constructor for the pre-defined instances. + * 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) @@ -230,7 +284,7 @@ public class TKeypress { * @return true if all fields are equal */ @Override - public final boolean equals(Object rhs) { + public boolean equals(final Object rhs) { if (!(rhs instanceof TKeypress)) { return false; } @@ -250,7 +304,7 @@ public class TKeypress { * @return displayable String */ @Override - public final String toString() { + public String toString() { if (isKey) { switch (fnKey) { case F1: diff --git a/src/jexer/event/TCommandEvent.java b/src/jexer/event/TCommandEvent.java index 09304d3..cdb5e2d 100644 --- a/src/jexer/event/TCommandEvent.java +++ b/src/jexer/event/TCommandEvent.java @@ -37,7 +37,7 @@ import jexer.TCommand; * generated by menu actions, keyboard accelerators, and other UI elements. * Commands can operate on both the application and individual widgets. */ -public class TCommandEvent extends TInputEvent { +public final class TCommandEvent extends TInputEvent { /** * Command dispatched. @@ -49,7 +49,7 @@ public class TCommandEvent extends TInputEvent { * * @return the TCommand */ - public final TCommand getCmd() { + public TCommand getCmd() { return cmd; } @@ -68,7 +68,7 @@ public class TCommandEvent extends TInputEvent { * @return displayable String */ @Override - public final String toString() { + public String toString() { return String.format("CommandEvent: %s", cmd.toString()); } } diff --git a/src/jexer/event/TKeypressEvent.java b/src/jexer/event/TKeypressEvent.java index 31bc4ae..03c4f71 100644 --- a/src/jexer/event/TKeypressEvent.java +++ b/src/jexer/event/TKeypressEvent.java @@ -35,18 +35,20 @@ import jexer.TKeypress; /** * This class encapsulates a keyboard input event. */ -public class TKeypressEvent extends TInputEvent { +public final class TKeypressEvent extends TInputEvent { /** * Keystroke received. */ - public TKeypress key; + private TKeypress key; /** - * Public contructor sets the key to the special kbNoKey. + * Get keystroke. + * + * @return keystroke */ - public TKeypressEvent() { - key = TKeypress.kbNoKey; + public TKeypress getKey() { + return key; } /** @@ -58,13 +60,68 @@ public class TKeypressEvent extends TInputEvent { this.key = key; } + /** + * Public constructor. + * + * @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 TKeypressEvent(final boolean isKey, final int fnKey, final char ch, + final boolean alt, final boolean ctrl, final boolean shift) { + + this.key = new TKeypress(isKey, fnKey, ch, alt, ctrl, shift); + } + + /** + * Public constructor. + * + * @param key the TKeypress received + * @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 TKeypressEvent(final TKeypress key, + final boolean alt, final boolean ctrl, final boolean shift) { + + this.key = new TKeypress(key.getIsKey(), key.getFnKey(), key.getCh(), + alt, ctrl, shift); + } + + /** + * Comparison check. All fields must match to return true. + * + * @param rhs another TKeypressEvent or TKeypress instance + * @return true if all fields are equal + */ + @Override + public boolean equals(final Object rhs) { + if (!(rhs instanceof TKeypressEvent) + && !(rhs instanceof TKeypress) + ) { + return false; + } + + if (rhs instanceof TKeypressEvent) { + TKeypressEvent that = (TKeypressEvent) rhs; + return (key.equals(that.key) + && (getTime().equals(that.getTime()))); + } + + TKeypress that = (TKeypress) rhs; + return (key.equals(that)); + } + /** * Make human-readable description of this TKeypressEvent. * * @return displayable String */ @Override - public final String toString() { + public String toString() { return String.format("Keypress: %s", key.toString()); } } diff --git a/src/jexer/io/ECMA48Terminal.java b/src/jexer/io/ECMA48Terminal.java index 6defbe4..b8fabda 100644 --- a/src/jexer/io/ECMA48Terminal.java +++ b/src/jexer/io/ECMA48Terminal.java @@ -406,41 +406,34 @@ public class ECMA48Terminal implements Runnable { * etc.). * * @param ch Unicode code point + * @param alt if true, set alt on the TKeypress * @return one TKeypress event, either a control character (e.g. isKey == * false, ch == 'A', ctrl == true), or a special key (e.g. isKey == true, * fnKey == ESC) */ - private TKeypressEvent controlChar(final char ch) { - TKeypressEvent event = new TKeypressEvent(); - + private TKeypressEvent controlChar(final char ch, final boolean alt) { // System.err.printf("controlChar: %02x\n", ch); switch (ch) { case 0x0D: // Carriage return --> ENTER - event.key = kbEnter; - break; + return new TKeypressEvent(kbEnter, alt, false, false); case 0x0A: // Linefeed --> ENTER - event.key = kbEnter; - break; + return new TKeypressEvent(kbEnter, alt, false, false); case 0x1B: // ESC - event.key = kbEsc; - break; + return new TKeypressEvent(kbEsc, alt, false, false); case '\t': // TAB - event.key = kbTab; - break; + return new TKeypressEvent(kbTab, alt, false, false); default: // Make all other control characters come back as the alphabetic // character with the ctrl field set. So SOH would be 'A' + // ctrl. - event.key = new TKeypress(false, 0, (char)(ch + 0x40), - false, true, false); - break; + return new TKeypressEvent(false, 0, (char)(ch + 0x40), + alt, true, false); } - return event; } /** @@ -457,220 +450,64 @@ public class ECMA48Terminal implements Runnable { if (params.size() > 1) { modifier = Integer.parseInt(params.get(1)); } - TKeypressEvent event = new TKeypressEvent(); + boolean alt = false; + boolean ctrl = false; + boolean shift = false; switch (modifier) { case 0: // No modifier - switch (key) { - case 1: - event.key = kbHome; - break; - case 2: - event.key = kbIns; - break; - case 3: - event.key = kbDel; - break; - case 4: - event.key = kbEnd; - break; - case 5: - event.key = kbPgUp; - break; - case 6: - event.key = kbPgDn; - break; - case 15: - event.key = kbF5; - break; - case 17: - event.key = kbF6; - break; - case 18: - event.key = kbF7; - break; - case 19: - event.key = kbF8; - break; - case 20: - event.key = kbF9; - break; - case 21: - event.key = kbF10; - break; - case 23: - event.key = kbF11; - break; - case 24: - event.key = kbF12; - break; - default: - // Unknown - return null; - } - break; case 2: // Shift - switch (key) { - case 1: - event.key = kbShiftHome; - break; - case 2: - event.key = kbShiftIns; - break; - case 3: - event.key = kbShiftDel; - break; - case 4: - event.key = kbShiftEnd; - break; - case 5: - event.key = kbShiftPgUp; - break; - case 6: - event.key = kbShiftPgDn; - break; - case 15: - event.key = kbShiftF5; - break; - case 17: - event.key = kbShiftF6; - break; - case 18: - event.key = kbShiftF7; - break; - case 19: - event.key = kbShiftF8; - break; - case 20: - event.key = kbShiftF9; - break; - case 21: - event.key = kbShiftF10; - break; - case 23: - event.key = kbShiftF11; - break; - case 24: - event.key = kbShiftF12; - break; - default: - // Unknown - return null; - } + shift = true; break; - case 3: // Alt - switch (key) { - case 1: - event.key = kbAltHome; - break; - case 2: - event.key = kbAltIns; - break; - case 3: - event.key = kbAltDel; - break; - case 4: - event.key = kbAltEnd; - break; - case 5: - event.key = kbAltPgUp; - break; - case 6: - event.key = kbAltPgDn; - break; - case 15: - event.key = kbAltF5; - break; - case 17: - event.key = kbAltF6; - break; - case 18: - event.key = kbAltF7; - break; - case 19: - event.key = kbAltF8; - break; - case 20: - event.key = kbAltF9; - break; - case 21: - event.key = kbAltF10; - break; - case 23: - event.key = kbAltF11; - break; - case 24: - event.key = kbAltF12; - break; - default: - // Unknown - return null; - } + alt = true; break; - case 5: // Ctrl - switch (key) { - case 1: - event.key = kbCtrlHome; - break; - case 2: - event.key = kbCtrlIns; - break; - case 3: - event.key = kbCtrlDel; - break; - case 4: - event.key = kbCtrlEnd; - break; - case 5: - event.key = kbCtrlPgUp; - break; - case 6: - event.key = kbCtrlPgDn; - break; - case 15: - event.key = kbCtrlF5; - break; - case 17: - event.key = kbCtrlF6; - break; - case 18: - event.key = kbCtrlF7; - break; - case 19: - event.key = kbCtrlF8; - break; - case 20: - event.key = kbCtrlF9; - break; - case 21: - event.key = kbCtrlF10; - break; - case 23: - event.key = kbCtrlF11; - break; - case 24: - event.key = kbCtrlF12; - break; - default: - // Unknown - return null; - } + ctrl = true; break; - + default: + // Unknown modifier, bail out + return null; + } + + switch (key) { + case 1: + return new TKeypressEvent(kbHome, alt, ctrl, shift); + case 2: + return new TKeypressEvent(kbIns, alt, ctrl, shift); + case 3: + return new TKeypressEvent(kbDel, alt, ctrl, shift); + case 4: + return new TKeypressEvent(kbEnd, alt, ctrl, shift); + case 5: + return new TKeypressEvent(kbPgUp, alt, ctrl, shift); + case 6: + return new TKeypressEvent(kbPgDn, alt, ctrl, shift); + case 15: + return new TKeypressEvent(kbF5, alt, ctrl, shift); + case 17: + return new TKeypressEvent(kbF6, alt, ctrl, shift); + case 18: + return new TKeypressEvent(kbF7, alt, ctrl, shift); + case 19: + return new TKeypressEvent(kbF8, alt, ctrl, shift); + case 20: + return new TKeypressEvent(kbF9, alt, ctrl, shift); + case 21: + return new TKeypressEvent(kbF10, alt, ctrl, shift); + case 23: + return new TKeypressEvent(kbF11, alt, ctrl, shift); + case 24: + return new TKeypressEvent(kbF12, alt, ctrl, shift); default: // Unknown return null; } - - // All OK, return a keypress - return event; } /** @@ -840,19 +677,24 @@ public class ECMA48Terminal implements Runnable { */ private void processChar(final List events, final char ch) { - TKeypressEvent keypress; - Date now = new Date(); - // ESCDELAY type timeout + Date now = new Date(); if (state == ParseState.ESCAPE) { long escDelay = now.getTime() - escapeTime; if (escDelay > 250) { // After 0.25 seconds, assume a true escape character - events.add(controlChar((char)0x1B)); + events.add(controlChar((char)0x1B, false)); reset(); } } + // TKeypress fields + boolean ctrl = false; + boolean alt = false; + boolean shift = false; + char keyCh = ch; + TKeypress key; + // System.err.printf("state: %s ch %c\r\n", state, ch); switch (state) { @@ -866,17 +708,15 @@ public class ECMA48Terminal implements Runnable { if (ch <= 0x1F) { // Control character - events.add(controlChar(ch)); + events.add(controlChar(ch, false)); reset(); return; } if (ch >= 0x20) { // Normal character - keypress = new TKeypressEvent(); - keypress.key.isKey = false; - keypress.key.ch = ch; - events.add(keypress); + events.add(new TKeypressEvent(false, 0, ch, + false, false, false)); reset(); return; } @@ -886,9 +726,7 @@ public class ECMA48Terminal implements Runnable { case ESCAPE: if (ch <= 0x1F) { // ALT-Control character - keypress = controlChar(ch); - keypress.key.alt = true; - events.add(keypress); + events.add(controlChar(ch, true)); reset(); return; } @@ -906,39 +744,33 @@ public class ECMA48Terminal implements Runnable { } // Everything else is assumed to be Alt-keystroke - keypress = new TKeypressEvent(); - keypress.key.isKey = false; - keypress.key.ch = ch; - keypress.key.alt = true; if ((ch >= 'A') && (ch <= 'Z')) { - keypress.key.shift = true; + shift = true; } - events.add(keypress); + alt = true; + events.add(new TKeypressEvent(false, 0, ch, alt, ctrl, shift)); reset(); return; case ESCAPE_INTERMEDIATE: if ((ch >= 'P') && (ch <= 'S')) { // Function key - keypress = new TKeypressEvent(); - keypress.key.isKey = true; switch (ch) { case 'P': - keypress.key.fnKey = TKeypress.F1; + events.add(new TKeypressEvent(kbF1)); break; case 'Q': - keypress.key.fnKey = TKeypress.F2; + events.add(new TKeypressEvent(kbF2)); break; case 'R': - keypress.key.fnKey = TKeypress.F3; + events.add(new TKeypressEvent(kbF3)); break; case 'S': - keypress.key.fnKey = TKeypress.F4; + events.add(new TKeypressEvent(kbF4)); break; default: break; } - events.add(keypress); reset(); return; } @@ -965,102 +797,81 @@ public class ECMA48Terminal implements Runnable { switch (ch) { case 'A': // Up - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.UP; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbUp, alt, ctrl, shift)); reset(); return; case 'B': // Down - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.DOWN; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbDown, alt, ctrl, shift)); reset(); return; case 'C': // Right - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.RIGHT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbRight, alt, ctrl, shift)); reset(); return; case 'D': // Left - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.LEFT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbLeft, alt, ctrl, shift)); reset(); return; case 'H': // Home - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.HOME; - events.add(keypress); + events.add(new TKeypressEvent(kbHome)); reset(); return; case 'F': // End - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.END; - events.add(keypress); + events.add(new TKeypressEvent(kbEnd)); reset(); return; case 'Z': // CBT - Cursor backward X tab stops (default 1) - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.BTAB; - events.add(keypress); + events.add(new TKeypressEvent(kbBackTab)); reset(); return; case 'M': @@ -1100,78 +911,66 @@ public class ECMA48Terminal implements Runnable { switch (ch) { case 'A': // Up - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.UP; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbUp, alt, ctrl, shift)); reset(); return; case 'B': // Down - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.DOWN; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbDown, alt, ctrl, shift)); reset(); return; case 'C': // Right - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.RIGHT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbRight, alt, ctrl, shift)); reset(); return; case 'D': // Left - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.LEFT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbLeft, alt, ctrl, shift)); reset(); return; default: -- 2.27.0