/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.jedit; /* * DefaultInputHandler.java - Default implementation of an input handler * Copyright (C) 1999 Slava Pestov * * You may use and modify this package for any purpose. Redistribution is * permitted, in both source and binary form, provided that this notice * remains intact in all source distributions of this package. */ import java.awt.Toolkit; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.Hashtable; import java.util.StringTokenizer; import javax.swing.KeyStroke; import org.openflexo.toolbox.ToolBox; /** * The default input handler. It maps sequences of keystrokes into actions and inserts key typed events into the text area. * * @author Slava Pestov */ public class DefaultInputHandler extends InputHandler { /** * Creates a new input handler with no key bindings defined. */ public DefaultInputHandler() { bindings = currentBindings = new Hashtable(); } /** * Sets up the default key bindings. */ @Override public void addDefaultKeyBindings() { addKeyBinding("BACK_SPACE", BACKSPACE); addKeyBinding("C+BACK_SPACE", BACKSPACE_WORD); addKeyBinding("DELETE", DELETE); addKeyBinding("C+DELETE", DELETE_WORD); addKeyBinding("ENTER", INSERT_BREAK); addKeyBinding("TAB", INSERT_TAB); addKeyBinding("INSERT", OVERWRITE); addKeyBinding("C+\\", TOGGLE_RECT); addKeyBinding("HOME", HOME); addKeyBinding("END", END); addKeyBinding("S+HOME", SELECT_HOME); addKeyBinding("S+END", SELECT_END); addKeyBinding("C+HOME", DOCUMENT_HOME); addKeyBinding("C+END", DOCUMENT_END); addKeyBinding("CS+HOME", SELECT_DOC_HOME); addKeyBinding("CS+END", SELECT_DOC_END); addKeyBinding("PAGE_UP", PREV_PAGE); addKeyBinding("PAGE_DOWN", NEXT_PAGE); addKeyBinding("S+PAGE_UP", SELECT_PREV_PAGE); addKeyBinding("S+PAGE_DOWN", SELECT_NEXT_PAGE); addKeyBinding("LEFT", PREV_CHAR); addKeyBinding("S+LEFT", SELECT_PREV_CHAR); addKeyBinding("C+LEFT", PREV_WORD); addKeyBinding("CS+LEFT", SELECT_PREV_WORD); addKeyBinding("RIGHT", NEXT_CHAR); addKeyBinding("S+RIGHT", SELECT_NEXT_CHAR); addKeyBinding("C+RIGHT", NEXT_WORD); addKeyBinding("CS+RIGHT", SELECT_NEXT_WORD); addKeyBinding("UP", PREV_LINE); addKeyBinding("S+UP", SELECT_PREV_LINE); addKeyBinding("DOWN", NEXT_LINE); addKeyBinding("S+DOWN", SELECT_NEXT_LINE); addKeyBinding("C+ENTER", REPEAT); addKeyBinding("A+UP", MOVE_LINE_UP); addKeyBinding("A+DOWN", MOVE_LINE_DOWN); if (ToolBox.getPLATFORM() == ToolBox.MACOS) { addKeyBinding("M+C", COPY); addKeyBinding("M+c", COPY); addKeyBinding("M+X", CUT); addKeyBinding("M+V", PASTE); addKeyBinding("M+A", SELECT_ALL); addKeyBinding("M+F", FIND); addKeyBinding("M+G", GOTOLINE); addKeyBinding("M+L", GOTOLINE); addKeyBinding("MA+UP", COPY_LINE_UP); addKeyBinding("MA+DOWN", COPY_LINE_DOWN); // addKeyBinding("M+Z",UNDO); // addKeyBinding("M+Y",REDO); } else { addKeyBinding("C+C", COPY); addKeyBinding("C+X", CUT); addKeyBinding("C+V", PASTE); addKeyBinding("C+A", SELECT_ALL); addKeyBinding("C+F", FIND); addKeyBinding("C+G", GOTOLINE); addKeyBinding("C+L", GOTOLINE); // addKeyBinding("C+Z",UNDO); // addKeyBinding("C+Y",REDO); addKeyBinding("CA+UP", COPY_LINE_UP); addKeyBinding("CA+DOWN", COPY_LINE_DOWN); } } /** * Adds a key binding to this input handler. The key binding is a list of white space separated key strokes of the form * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt, or S for Shift, and key is either a character (a-z) or a field * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE) * * @param keyBinding * The key binding * @param action * The action */ @Override public void addKeyBinding(String keyBinding, ActionListener action) { Hashtable current = bindings; StringTokenizer st = new StringTokenizer(keyBinding); while (st.hasMoreTokens()) { // System.out.println("Registering for "+keyBinding); KeyStroke keyStroke = parseKeyStroke(st.nextToken()); if (keyStroke == null) { return; } if (st.hasMoreTokens()) { Object o = current.get(keyStroke); if (o instanceof Hashtable) { current = (Hashtable) o; } else { o = new Hashtable(); current.put(keyStroke, o); current = (Hashtable) o; } } else { current.put(keyStroke, action); } } } /** * Removes a key binding from this input handler. This is not yet implemented. * * @param keyBinding * The key binding */ @Override public void removeKeyBinding(String keyBinding) { throw new InternalError("Not yet implemented"); } /** * Removes all key bindings from this input handler. */ @Override public void removeAllKeyBindings() { bindings.clear(); } /** * Returns a copy of this input handler that shares the same key bindings. Setting key bindings in the copy will also set them in the * original. */ @Override public InputHandler copy() { return new DefaultInputHandler(this); } /** * Handle a key pressed event. This will look up the binding for the key stroke and execute it. */ @Override public void keyPressed(KeyEvent evt) { int keyCode = evt.getKeyCode(); int modifiers = evt.getModifiers(); if (keyCode == KeyEvent.VK_ENTER) { notifyEnterPressed(evt); } if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT || keyCode == KeyEvent.VK_META) { return; } if ((modifiers & ~InputEvent.SHIFT_MASK) != 0 || evt.isActionKey() || keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ESCAPE) { if (grabAction != null) { handleGrabAction(evt); return; } KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); Object o = currentBindings.get(keyStroke); // System.out.println("keyPressed() keyCode="+keyCode+" modifiers="+modifiers+" o="+o+ "event="+evt); if (o == null) { // Don't beep if the user presses some // key we don't know about unless a // prefix is active. Otherwise it will // beep when caps lock is pressed, etc. if (currentBindings != bindings) { Toolkit.getDefaultToolkit().beep(); // F10 should be passed on, but C+e F10 // shouldn't repeatCount = 0; repeat = false; evt.consume(); } currentBindings = bindings; return; } else if (o instanceof ActionListener) { currentBindings = bindings; executeAction((ActionListener) o, evt.getSource(), null); evt.consume(); return; } else if (o instanceof Hashtable) { currentBindings = (Hashtable) o; evt.consume(); return; } } } /** * Handle a key typed event. This inserts the key into the text area. */ @Override public void keyTyped(KeyEvent evt) { int modifiers = evt.getModifiers(); int keyCode = evt.getKeyCode(); char c = evt.getKeyChar(); if (c != KeyEvent.CHAR_UNDEFINED /*&& (modifiers & KeyEvent.ALT_MASK) == 0*/) { if (c >= 0x20 && c != 0x7f) { // Fixed issue for handling special chars // Take care that keyCode may values 0 and data stored in keyChar !!! // (SGU, 16/05/2007) KeyStroke keyStroke; if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') { keyStroke = KeyStroke.getKeyStroke(Character.toUpperCase(c), modifiers); } else { keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); } Object o = currentBindings.get(keyStroke); // System.out.println("keyTyped() keyCode="+keyCode+" c="+c+" modifiers="+modifiers+" o="+o+ "event="+evt); if (o instanceof Hashtable) { currentBindings = (Hashtable) o; return; } else if (o instanceof ActionListener) { currentBindings = bindings; // This has been performed in the keyPressed, forgetting it /*executeAction((ActionListener)o, evt.getSource(), String.valueOf(c));*/ return; } currentBindings = bindings; if (grabAction != null) { handleGrabAction(evt); return; } // 0-9 adds another 'digit' to the repeat number if (repeat && Character.isDigit(c)) { repeatCount *= 10; repeatCount += c - '0'; return; } executeAction(INSERT_CHAR, evt.getSource(), String.valueOf(evt.getKeyChar())); repeatCount = 0; repeat = false; } } } /** * Converts a string to a keystroke. The string should be of the form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i> is any * combination of A for Alt, C for Control, S for Shift or M for Meta, and <i>shortcut</i> is either a single character, or a keycode * name from the <code>KeyEvent</code> class, without the <code>VK_</code> prefix. * * @param keyStroke * A string description of the key stroke */ public static KeyStroke parseKeyStroke(String keyStroke) { if (keyStroke == null) { return null; } int modifiers = 0; int index = keyStroke.indexOf('+'); if (index != -1) { for (int i = 0; i < index; i++) { switch (Character.toUpperCase(keyStroke.charAt(i))) { case 'A': modifiers |= InputEvent.ALT_MASK; break; case 'C': modifiers |= InputEvent.CTRL_MASK; break; case 'M': modifiers |= InputEvent.META_MASK; break; case 'S': modifiers |= InputEvent.SHIFT_MASK; break; } } } String key = keyStroke.substring(index + 1); if (key.length() == 1) { char ch = Character.toUpperCase(key.charAt(0)); if (modifiers == 0) { return KeyStroke.getKeyStroke(ch); } else { return KeyStroke.getKeyStroke(ch, modifiers); } } else if (key.length() == 0) { System.err.println("Invalid key stroke: " + keyStroke); return null; } else { int ch; try { ch = KeyEvent.class.getField("VK_".concat(key)).getInt(null); } catch (Exception e) { System.err.println("Invalid key stroke: " + keyStroke); return null; } return KeyStroke.getKeyStroke(ch, modifiers); } } // private members private Hashtable bindings; private Hashtable currentBindings; private DefaultInputHandler(DefaultInputHandler copy) { bindings = currentBindings = copy.bindings; } }