diff --git a/source/org/thdl/tib/input/DuffPane.java b/source/org/thdl/tib/input/DuffPane.java index 6721c34..ffb1d1d 100644 --- a/source/org/thdl/tib/input/DuffPane.java +++ b/source/org/thdl/tib/input/DuffPane.java @@ -1039,6 +1039,8 @@ public void paste(int offset) { * Other keystrokes are handled by keyTyped. */ public void keyPressed(KeyEvent e) { + // FIXME: exceptions thrown here do not cause the program to fail, even in development mode. + if (e.isActionKey()) initKeyboard(); @@ -1108,6 +1110,8 @@ public void paste(int offset) { * @param e the KeyEvent */ public void keyReleased(KeyEvent e) { + // FIXME: exceptions thrown here do not cause the program to fail, even in development mode. + /* * Apparently it works best to check for backspace * and init the keyboard here in key released @@ -1128,6 +1132,8 @@ public void paste(int offset) { * @param e a KeyEvent */ public void keyTyped(KeyEvent e) { + // FIXME: exceptions thrown here do not cause the program to fail, even in development mode. + e.consume(); // Respect setEditable(boolean): @@ -1174,29 +1180,34 @@ public void paste(int offset) { * TibTextUtils.getGlyphs, which in turn relies on 'tibwn.ini'. * * @param e a KeyEvent */ - public void processTibetan(KeyEvent e) { - char c = e.getKeyChar(); - - int start = getSelectionStart(); - int end = getSelectionEnd(); + public void processTibetan(KeyEvent kev) { boolean shouldIBackSpace = true; // We don't handle just any old keypress. We handle only the // ones that enter text. - if (e.isControlDown() || e.isAltDown()) + if (kev.isControlDown() || kev.isAltDown()) return; - if (start != end) { - if (e.getKeyCode() != KeyEvent.VK_ESCAPE) { - try { - initKeyboard(); - doc.remove(start, end-start); - shouldIBackSpace = false; - } - catch (BadLocationException ble) { - } - } - } + { + int start = getSelectionStart(); + int end = getSelectionEnd(); + if (start != end) { + if (kev.getKeyCode() != KeyEvent.VK_ESCAPE) { + try { + initKeyboard(); + doc.remove(start, end-start); + shouldIBackSpace = false; + } + catch (BadLocationException ble) { + } + } + } + } + processTibetanChar(kev.getKeyChar(), shouldIBackSpace); + } + + /** @see #processTibetan(KeyEvent) */ + private void processTibetanChar(char c, boolean shouldIBackSpace) { // Have we modified the status bar? boolean changedStatus = false; @@ -1371,9 +1382,29 @@ public void paste(int offset) { changedStatus = true; updateStatus("You typed a non-vowel, Tibetan character."); } else { - isTopHypothesis = false; - changedStatus = true; - updateStatus("invalid input, I think"); + if (TibetanMachineWeb.hasInputPrefix(s)) { + isTopHypothesis = false; + changedStatus = true; + updateStatus("incomplete input (like the \"S\" in Extended Wylie's \"Sh\")"); + } else { + // FIXME: ring a bell here so the user knows what's up. + initKeyboard(); + + // The recursive call to + // processTibetanChar will update the + // status bar, so set this to true: + changedStatus = true; + + // This status message is bound to get + // overridden, but this is what we + // would say if we had a queue of + // messages or something like Emacs's + // M-x view-lossage to see a history + // of status messages: + appendStatus(" (because you typed something invalid [2nd way])"); + + processTibetanChar(c, false); + } } } } else { //there is already a character in charList @@ -1454,7 +1485,7 @@ public void paste(int offset) { putVowel(TibetanMachineWeb.A_VOWEL); initKeyboard(); changedStatus = true; - appendStatus("we put a vowel"); + appendStatus(" (because we put a vowel)"); break key_block; } @@ -1470,9 +1501,34 @@ public void paste(int offset) { updateStatus("you didn't type a 'char'; voodoo no. 5 ensues"); } } - } else { //top char is just a guess! just keep it in holdCurrent - changedStatus = true; - updateStatus("top char is just a guess! just keep it in holdCurrent"); + } else { + // top char is just a guess! Just keep c + // in holdCurrent if it may become valid + // input, or reset if we've no hope. + + if (TibetanMachineWeb.hasInputPrefix(s)) { + isTopHypothesis = false; + changedStatus = true; + updateStatus("incomplete input (like the \"S\" in Extended Wylie's \"Sh\")"); + } else { + // FIXME: ring a bell here so the user knows what's up. + initKeyboard(); + + // The recursive call to + // processTibetanChar will update the + // status bar, so set this to true: + changedStatus = true; + + // This status message is bound to get + // overridden, but this is what we + // would say if we had a queue of + // messages or something like Emacs's + // M-x view-lossage to see a history + // of status messages: + appendStatus(" (because you typed something invalid [2nd way])"); + + processTibetanChar(c, false); + } } } } diff --git a/source/org/thdl/tib/text/TibetanKeyboard.java b/source/org/thdl/tib/text/TibetanKeyboard.java index 2727112..c718228 100644 --- a/source/org/thdl/tib/text/TibetanKeyboard.java +++ b/source/org/thdl/tib/text/TibetanKeyboard.java @@ -23,6 +23,8 @@ import java.io.*; import java.lang.*; import java.net.URL; +import org.thdl.util.Trie; + /** * An alternate (non-Extended Wylie) keyboard input * method. A keyboard URL is passed to its constructor. This URL @@ -43,6 +45,11 @@ import java.net.URL; * @version 1.0 */ public class TibetanKeyboard { + + /** This addresses bug 624133, "Input freezes after impossible + character". We store all valid input sequences here. */ + private Trie validInputSequences; + private boolean hasDisambiguatingKey; private char disambiguatingKey; private boolean hasSanskritStackingKey; @@ -68,6 +75,9 @@ public class TibetanKeyboard { public class InvalidKeyboardException extends Exception { } + /** Do not call this. */ + private TibetanKeyboard() { } + /** * Opens the URL specified by the parameter, * and tries to construct a keyboard from it. If the file is @@ -89,6 +99,7 @@ public class TibetanKeyboard { charMap = new HashMap(); vowelMap = new HashMap(); puncMap = new HashMap(); + validInputSequences = new Trie(); command = NO_COMMAND; @@ -176,6 +187,7 @@ public class TibetanKeyboard { break; charMap.put(right, left); + validInputSequences.put(right, left); break; case VOWELS: @@ -186,6 +198,7 @@ public class TibetanKeyboard { break; vowelMap.put(right, left); + validInputSequences.put(right, left); break; case PUNCTUATION: @@ -196,6 +209,7 @@ public class TibetanKeyboard { break; puncMap.put(right, left); + validInputSequences.put(right, left); break; } } @@ -298,14 +312,14 @@ public class TibetanKeyboard { } /** -* Decides whether or not a string is a character in this keyboard. +* Decides whether or not a string is a character (as opposed to a +* vowel or punctuation) in this keyboard. * @return true if the parameter is a character * in this keyboard. This method checks to see * if the passed string has been mapped to a * Wylie character - if not, then it returns false. * -* @param s the possible character -*/ +* @param s the possible character */ public boolean isChar(String s) { if (charMap.containsKey(s)) return true; @@ -388,4 +402,15 @@ public class TibetanKeyboard { return (String)vowelMap.get(s); } + + /** This addresses bug 624133, "Input freezes after impossible + * character". Returns true iff s is a proper prefix of some + * legal input for this keyboard. In the extended Wylie + * keyboard, hasInputPrefix("S") is true because "Sh" is legal + * input. hasInputPrefix("Sh") is false because though "Sh" is + * legal input, ("Sh" + y) is not valid input for any non-empty + * String y. */ + public boolean hasInputPrefix(String s) { + return validInputSequences.hasPrefix(s); + } } diff --git a/source/org/thdl/tib/text/TibetanMachineWeb.java b/source/org/thdl/tib/text/TibetanMachineWeb.java index cdb953d..ae7708a 100644 --- a/source/org/thdl/tib/text/TibetanMachineWeb.java +++ b/source/org/thdl/tib/text/TibetanMachineWeb.java @@ -28,6 +28,7 @@ import javax.swing.text.*; import java.awt.font.*; import org.thdl.util.ThdlDebug; +import org.thdl.util.Trie; /** * Interfaces between Extended Wylie and the TibetanMachineWeb fonts. @@ -37,6 +38,17 @@ import org.thdl.util.ThdlDebug; * @version 1.0 */ public class TibetanMachineWeb { + /** This addresses bug 624133, "Input freezes after impossible + * character". The input sequences that are valid in Extended + * Wylie. For example, "Sh" will be in this container, but "S" + * will not be. */ + private static Trie validInputSequences = new Trie(); + + /** needed because a Trie cannot have a null value associated with + * a key */ + private final static String anyOldObjectWillDo + = "this placeholder is useful for debugging; we need a nonnull Object anyway"; + private static boolean hasReadData = false; private static TibetanKeyboard keyboard = null; private static final String DEFAULT_KEYBOARD = "default_keyboard.ini"; @@ -306,8 +318,11 @@ public class TibetanMachineWeb { line = in.readLine(); charSet = new HashSet(); StringTokenizer st = new StringTokenizer(line,","); - while (st.hasMoreTokens()) - charSet.add(st.nextToken()); + while (st.hasMoreTokens()) { + String ntk; + charSet.add(ntk = st.nextToken()); + validInputSequences.put(ntk, anyOldObjectWillDo); + } } else if (line.equalsIgnoreCase("")) { isSanskrit = false; @@ -315,8 +330,11 @@ public class TibetanMachineWeb { line = in.readLine(); vowelSet = new HashSet(); StringTokenizer st = new StringTokenizer(line,","); - while (st.hasMoreTokens()) - vowelSet.add(st.nextToken()); + while (st.hasMoreTokens()) { + String ntk; + vowelSet.add(ntk = st.nextToken()); + validInputSequences.put(ntk, anyOldObjectWillDo); + } } else if (line.equalsIgnoreCase("")) { isSanskrit = false; @@ -324,8 +342,11 @@ public class TibetanMachineWeb { line = in.readLine(); puncSet = new HashSet(); StringTokenizer st = new StringTokenizer(line,","); - while (st.hasMoreTokens()) - puncSet.add(st.nextToken()); + while (st.hasMoreTokens()) { + String ntk; + puncSet.add(ntk = st.nextToken()); + validInputSequences.put(ntk, anyOldObjectWillDo); + } } else if (line.equalsIgnoreCase("") @@ -445,8 +466,10 @@ public static boolean setKeyboard(TibetanKeyboard kb) { isAChungConsonant = false; hasAVowel = true; aVowel = WYLIE_aVOWEL; - if (!vowelSet.contains(WYLIE_aVOWEL)) + if (!vowelSet.contains(WYLIE_aVOWEL)) { vowelSet.add(WYLIE_aVOWEL); + validInputSequences.put(WYLIE_aVOWEL, anyOldObjectWillDo); + } } else { hasDisambiguatingKey = keyboard.hasDisambiguatingKey(); @@ -702,6 +725,13 @@ public static String getWylieForChar(String s) { return keyboard.getWylieForChar(s); } +/** + * Returns the current keyboard, or, if the current keyboard is the + * Extended Wylie keyboard, null. */ + public static TibetanKeyboard getKeyboard() { + return keyboard; + } + /** * Converts punctuation to its Extended Wylie correspondence. * This assumes that the passed string is punctuation @@ -927,6 +957,22 @@ public static String getWylieForGlyph(DuffCode dc) { return wylieForGlyph(hashKey); } + /** This addresses bug 624133, "Input freezes after impossible + * character". Returns true iff s is a proper prefix of some + * legal input for this keyboard. In the extended Wylie + * keyboard, hasInputPrefix("S") is true because "Sh" is legal + * input. hasInputPrefix("Sh") is false because though "Sh" is + * legal input, ("Sh" + y) is not valid input for any non-empty + * String y. */ + public static boolean hasInputPrefix(String s) { + if (null != keyboard) { + return keyboard.hasInputPrefix(s); + } else { + return validInputSequences.hasPrefix(s); + } + } + + /** * Says whether or not this glyph involves a Sanskrit stack. * @param font the font of a TibetanMachineWeb glyph diff --git a/source/org/thdl/util/Trie.java b/source/org/thdl/util/Trie.java index 67e5ee2..cc2fbe1 100644 --- a/source/org/thdl/util/Trie.java +++ b/source/org/thdl/util/Trie.java @@ -81,6 +81,8 @@ Contributor(s): ______________________________________. package org.thdl.util; +import org.thdl.util.ThdlDebug; + /** * A digital search trie for 7-bit ASCII text. The API is a subset of * java.util.Hashtable. The key must be a 7-bit ASCII string. The @@ -102,6 +104,9 @@ public class Trie m_Root = new Node(); } + private static final boolean caseInsensitive = false; + + /** * Puts an object into the trie for lookup. * @@ -120,7 +125,10 @@ public class Trie for (int i = 0; i < len; i++) { - Node nextNode = node.m_nextChar[Character.toUpperCase(key.charAt(i))]; + Node nextNode + = node.m_nextChar[(caseInsensitive + ? Character.toUpperCase(key.charAt(i)) + : key.charAt(i))]; if (nextNode != null) { @@ -132,7 +140,9 @@ public class Trie { Node newNode = new Node(); - node.m_nextChar[Character.toUpperCase(key.charAt(i))] = newNode; + node.m_nextChar[(caseInsensitive + ? Character.toUpperCase(key.charAt(i)) + : key.charAt(i))] = newNode; node.m_isLeaf = false; node = newNode; } @@ -165,13 +175,17 @@ public class Trie { try { - node = node.m_nextChar[Character.toUpperCase(key.charAt(i))]; + node = node.m_nextChar[(caseInsensitive + ? Character.toUpperCase(key.charAt(i)) + : key.charAt(i))]; } catch (ArrayIndexOutOfBoundsException e) { // the key is not 7-bit ASCII so we won't find it here node = null; + + ThdlDebug.noteIffyCode(); } if (node == null) @@ -204,13 +218,17 @@ public class Trie { try { - node = node.m_nextChar[Character.toUpperCase(key.charAt(i))]; + node = node.m_nextChar[(caseInsensitive + ? Character.toUpperCase(key.charAt(i)) + : key.charAt(i))]; } catch (ArrayIndexOutOfBoundsException e) { // the key is not 7-bit ASCII so we won't find it here node = null; + + ThdlDebug.noteIffyCode(); } if (node == null)