public void keyReleased(KeyEvent e) {

    // Application.debug(e+"");

    /* test code to generate unicode strings for Virtual Keyboard
    String text = getText();
    String outStr = "";
    for (int i = 0 ; i < text.length() ; i++) {
    	int ch = text.charAt(i);
    	if (ch < 128) outStr += text.charAt(i);
    	else {
    		String unicode = Integer.toHexString(ch);
    		if (unicode.length() < 4) unicode = "\\u0"+unicode;
    		else unicode = "\\u"+unicode;
    		outStr += unicode;
    	}
    }
    Application.debug(outStr);
    //*/

    // ctrl pressed on Mac
    // or alt on Windows
    boolean modifierKeyPressed = Application.MAC_OS ? e.isControlDown() : e.isAltDown();

    // we don't want to act when AltGr is down
    // as it is used eg for entering {[}] is some locales
    // NB e.isAltGraphDown() doesn't work
    if (e.isAltDown() && e.isControlDown()) modifierKeyPressed = false;

    char charPressed = e.getKeyChar();

    if ((!isLetterOrDigit(charPressed) && !modifierKeyPressed)
        || (ctrlC && Application.MAC_OS) // don't want selection cleared
    ) return;

    clearSelection();

    // handle alt-p etc
    super.keyReleased(e);

    mergeKoreanDoubles();

    if (getAutoComplete()) {
      updateCurrentWord(false);
      startAutoCompletion();
    }

    /*
    if (charCodePressed == KeyEvent.VK_BACK_SPACE &&
      isTextSelected && input.length() > 0) {
        setText(input.substring(0, input.length()));
    }*/
  }
  /**
   * Automatically closes parentheses (, {, [ when next sign is a space or end of input text. and
   * ignores ] }, ) if the brackets already match (simple check)
   */
  public void keyTyped(KeyEvent e) {

    // only handle parentheses
    char ch = e.getKeyChar();
    int caretPos = getCaretPosition();

    String text = getText();

    if (ch == ',') {
      if (caretPos < text.length() && text.charAt(caretPos) == ',') {
        // User typed ',' just in ahead of an existing ',':
        // We may be in the position of filling in the next argument of an autocompleted command
        // Look for a pattern of the form ", < Argument Description > ," or ", < Argument
        // Description > ]"
        // If found, select the argument description so that it can easily be typed over with the
        // value
        // of the argument.
        if (moveToNextArgument(false)) {
          e.consume();
        }
        return;
      }
    }

    if (!(ch == '(' || ch == '{' || ch == '[' || ch == '}' || ch == ')' || ch == ']')) {
      super.keyTyped(e);
      return;
    }

    clearSelection();
    caretPos = getCaretPosition();

    if (ch == '}' || ch == ')' || ch == ']') {

      // simple check if brackets match
      if (text.length() > caretPos && text.charAt(caretPos) == ch) {
        int count = 0;
        for (int i = 0; i < text.length(); i++) {
          char c = text.charAt(i);
          if (c == '{') count++;
          else if (c == '}') count--;
          else if (c == '(') count += 1E3;
          else if (c == ')') count -= 1E3;
          else if (c == '[') count += 1E6;
          else if (c == ']') count -= 1E6;
        }

        if (count == 0) {
          // if brackets match, just move the cursor forwards one
          e.consume();
          caretPos++;
        }
      }
    }

    // auto-close parentheses
    if (caretPos == text.length() || isCloseBracketOrWhitespace(text.charAt(caretPos))) {
      switch (ch) {
        case '(':
          // opening parentheses: insert closing parenthesis automatically
          insertString(")");
          break;

        case '{':
          // opening braces: insert closing parenthesis automatically
          insertString("}");
          break;

        case '[':
          // opening bracket: insert closing parenthesis automatically
          insertString("]");
          break;
      }
    }

    // make sure we keep the previous caret position
    setCaretPosition(Math.min(text.length(), caretPos));
  }