View Javadoc

    1   /*
    2    * DefaultInputHandler.java - Default implementation of an input handler
    3    * Copyright (C) 1999 Slava Pestov
    4    *
    5    * You may use and modify this package for any purpose. Redistribution is
    6    * permitted, in both source and binary form, provided that this notice
    7    * remains intact in all source distributions of this package.
    8    */
    9
    10  package org.syntax.jedit;
    11
    12  import java.awt.Toolkit;
    13  import java.awt.event.ActionListener;
    14  import java.awt.event.InputEvent;
    15  import java.awt.event.KeyEvent;
    16  import java.util.Hashtable;
    17  import java.util.StringTokenizer;
    18
    19  import javax.swing.KeyStroke;
    20
    21  /***
    22   * The default input handler. It maps sequences of keystrokes into actions
    23   * and inserts key typed events into the text area.
    24   * @author Slava Pestov
    25   * @version $Id$
    26   */
    27  public class DefaultInputHandler extends InputHandler
    28  {
    29  	/***
    30  	 * Creates a new input handler with no key bindings defined.
    31  	 */
    32  	public DefaultInputHandler()
    33  	{
    34  		bindings = currentBindings = new Hashtable();
    35  	}
    36
    37  	/***
    38  	 * Sets up the default key bindings.
    39  	 */
    40  	public void addDefaultKeyBindings()
    41  	{
    42  		addKeyBinding("BACK_SPACE",BACKSPACE);
    43  		addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD);
    44  		addKeyBinding("DELETE",DELETE);
    45  		addKeyBinding("C+DELETE",DELETE_WORD);
    46
    47  		addKeyBinding("ENTER",INSERT_BREAK);
    48  		addKeyBinding("TAB",INSERT_TAB);
    49
    50  		addKeyBinding("INSERT",OVERWRITE);
    51  		addKeyBinding("C+//",TOGGLE_RECT);
    52
    53  		addKeyBinding("HOME",HOME);
    54  		addKeyBinding("END",END);
    55  		addKeyBinding("C+A",SELECT_ALL);
    56  		addKeyBinding("S+HOME",SELECT_HOME);
    57  		addKeyBinding("S+END",SELECT_END);
    58  		addKeyBinding("C+HOME",DOCUMENT_HOME);
    59  		addKeyBinding("C+END",DOCUMENT_END);
    60  		addKeyBinding("CS+HOME",SELECT_DOC_HOME);
    61  		addKeyBinding("CS+END",SELECT_DOC_END);
    62
    63  		addKeyBinding("PAGE_UP",PREV_PAGE);
    64  		addKeyBinding("PAGE_DOWN",NEXT_PAGE);
    65  		addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE);
    66  		addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE);
    67
    68  		addKeyBinding("LEFT",PREV_CHAR);
    69  		addKeyBinding("S+LEFT",SELECT_PREV_CHAR);
    70  		addKeyBinding("C+LEFT",PREV_WORD);
    71  		addKeyBinding("CS+LEFT",SELECT_PREV_WORD);
    72  		addKeyBinding("RIGHT",NEXT_CHAR);
    73  		addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR);
    74  		addKeyBinding("C+RIGHT",NEXT_WORD);
    75  		addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD);
    76  		addKeyBinding("UP",PREV_LINE);
    77  		addKeyBinding("S+UP",SELECT_PREV_LINE);
    78  		addKeyBinding("DOWN",NEXT_LINE);
    79  		addKeyBinding("S+DOWN",SELECT_NEXT_LINE);
    80
    81  		addKeyBinding("C+ENTER",REPEAT);
    82
    83  		// Clipboard
    84  		addKeyBinding("C+C", CLIP_COPY);
    85  		addKeyBinding("C+V", CLIP_PASTE);
    86  		addKeyBinding("C+X", CLIP_CUT);
    87  	}
    88
    89  	/***
    90  	 * Adds a key binding to this input handler. The key binding is
    91  	 * a list of white space separated key strokes of the form
    92  	 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
    93  	 * or S for Shift, and key is either a character (a-z) or a field
    94  	 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
    95  	 * @param keyBinding The key binding
    96  	 * @param action The action
    97  	 */
    98  	public void addKeyBinding(String keyBinding, ActionListener action)
    99  	{
    100 	   Hashtable current = bindings;
    101
    102 		StringTokenizer st = new StringTokenizer(keyBinding);
    103 		while(st.hasMoreTokens())
    104 		{
    105 			KeyStroke keyStroke = parseKeyStroke(st.nextToken());
    106 			if(keyStroke == null)
    107 				return;
    108
    109 			if(st.hasMoreTokens())
    110 			{
    111 				Object o = current.get(keyStroke);
    112 				if(o instanceof Hashtable)
    113 					current = (Hashtable)o;
    114 				else
    115 				{
    116 					o = new Hashtable();
    117 					current.put(keyStroke,o);
    118 					current = (Hashtable)o;
    119 				}
    120 			}
    121 			else
    122 				current.put(keyStroke,action);
    123 		}
    124 	}
    125
    126 	/***
    127 	 * Removes a key binding from this input handler. This is not yet
    128 	 * implemented.
    129 	 * @param keyBinding The key binding
    130 	 */
    131 	public void removeKeyBinding(String keyBinding)
    132 	{
    133 		throw new InternalError("Not yet implemented");
    134 	}
    135
    136 	/***
    137 	 * Removes all key bindings from this input handler.
    138 	 */
    139 	public void removeAllKeyBindings()
    140 	{
    141 		bindings.clear();
    142 	}
    143
    144 	/***
    145 	 * Returns a copy of this input handler that shares the same
    146 	 * key bindings. Setting key bindings in the copy will also
    147 	 * set them in the original.
    148 	 */
    149 	public InputHandler copy()
    150 	{
    151 		return new DefaultInputHandler(this);
    152 	}
    153
    154 	/***
    155 	 * Handle a key pressed event. This will look up the binding for
    156 	 * the key stroke and execute it.
    157 	 */
    158 	public void keyPressed(KeyEvent evt)
    159 	{
    160 		int keyCode = evt.getKeyCode();
    161 		int modifiers = evt.getModifiers();
    162
    163 		if(keyCode == KeyEvent.VK_CONTROL ||
    164 			keyCode == KeyEvent.VK_SHIFT ||
    165 			keyCode == KeyEvent.VK_ALT ||
    166 			keyCode == KeyEvent.VK_META)
    167 			return;
    168
    169 		if((modifiers & ~KeyEvent.SHIFT_MASK) != 0
    170 			|| evt.isActionKey()
    171 			|| keyCode == KeyEvent.VK_BACK_SPACE
    172 			|| keyCode == KeyEvent.VK_DELETE
    173 			|| keyCode == KeyEvent.VK_ENTER
    174 			|| keyCode == KeyEvent.VK_TAB
    175 			|| keyCode == KeyEvent.VK_ESCAPE)
    176 		{
    177 			if(grabAction != null)
    178 			{
    179 				handleGrabAction(evt);
    180 				return;
    181 			}
    182
    183 			KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
    184 				modifiers);
    185 			Object o = currentBindings.get(keyStroke);
    186 			if(o == null)
    187 			{
    188 				// Don't beep if the user presses some
    189 				// key we don't know about unless a
    190 				// prefix is active. Otherwise it will
    191 				// beep when caps lock is pressed, etc.
    192 				if(currentBindings != bindings)
    193 				{
    194 					Toolkit.getDefaultToolkit().beep();
    195 					// F10 should be passed on, but C+e F10
    196 					// shouldn't
    197 					repeatCount = 0;
    198 					repeat = false;
    199 					evt.consume();
    200 				}
    201 				currentBindings = bindings;
    202 				return;
    203 			}
    204 			else if(o instanceof ActionListener)
    205 			{
    206 				currentBindings = bindings;
    207
    208 				executeAction(((ActionListener)o),
    209 					evt.getSource(),null);
    210
    211 				evt.consume();
    212 				return;
    213 			}
    214 			else if(o instanceof Hashtable)
    215 			{
    216 				currentBindings = (Hashtable)o;
    217 				evt.consume();
    218 				return;
    219 			}
    220 		}
    221 	}
    222
    223 	/***
    224 	 * Handle a key typed event. This inserts the key into the text area.
    225 	 */
    226 	public void keyTyped(KeyEvent evt)
    227 	{
    228 		int modifiers = evt.getModifiers();
    229 		char c = evt.getKeyChar();
    230 		if(c != KeyEvent.CHAR_UNDEFINED &&
    231 			(modifiers & KeyEvent.ALT_MASK) == 0 &&
    232 			(modifiers & KeyEvent.CTRL_MASK) == 0 &&
    233 			(modifiers & KeyEvent.META_MASK) == 0)
    234 		{
    235 			if(c >= 0x20 && c != 0x7f)
    236 			{
    237 				KeyStroke keyStroke = KeyStroke.getKeyStroke(
    238 					Character.toUpperCase(c));
    239 				Object o = currentBindings.get(keyStroke);
    240
    241 				if(o instanceof Hashtable)
    242 				{
    243 					currentBindings = (Hashtable)o;
    244 					return;
    245 				}
    246 				else if(o instanceof ActionListener)
    247 				{
    248 					currentBindings = bindings;
    249 					executeAction((ActionListener)o,
    250 						evt.getSource(),
    251 						String.valueOf(c));
    252 					return;
    253 				}
    254
    255 				currentBindings = bindings;
    256
    257 				if(grabAction != null)
    258 				{
    259 					handleGrabAction(evt);
    260 					return;
    261 				}
    262
    263 				// 0-9 adds another 'digit' to the repeat number
    264 				if(repeat && Character.isDigit(c))
    265 				{
    266 					repeatCount *= 10;
    267 					repeatCount += (c - '0');
    268 					return;
    269 				}
    270
    271 				executeAction(INSERT_CHAR,evt.getSource(),
    272 					String.valueOf(evt.getKeyChar()));
    273
    274 				repeatCount = 0;
    275 				repeat = false;
    276 			}
    277 		}
    278 	}
    279
    280 	/***
    281 	 * Converts a string to a keystroke. The string should be of the
    282 	 * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
    283 	 * is any combination of A for Alt, C for Control, S for Shift
    284 	 * or M for Meta, and <i>shortcut</i> is either a single character,
    285 	 * or a keycode name from the <code>KeyEvent</code> class, without
    286 	 * the <code>VK_</code> prefix.
    287 	 * @param keyStroke A string description of the key stroke
    288 	 */
    289 	public static KeyStroke parseKeyStroke(String keyStroke)
    290 	{
    291 		if(keyStroke == null)
    292 			return null;
    293 		int modifiers = 0;
    294 		int index = keyStroke.indexOf('+');
    295 		if(index != -1)
    296 		{
    297 			for(int i = 0; i < index; i++)
    298 			{
    299 				switch(Character.toUpperCase(keyStroke
    300 					.charAt(i)))
    301 				{
    302 				case 'A':
    303 					modifiers |= InputEvent.ALT_MASK;
    304 					break;
    305 				case 'C':
    306 					modifiers |= Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    307 					break;
    308 				case 'M':
    309 					modifiers |= InputEvent.META_MASK;
    310 					break;
    311 				case 'S':
    312 					modifiers |= InputEvent.SHIFT_MASK;
    313 					break;
    314 				}
    315 			}
    316 		}
    317 		String key = keyStroke.substring(index + 1);
    318 		if(key.length() == 1)
    319 		{
    320 			char ch = Character.toUpperCase(key.charAt(0));
    321 			if(modifiers == 0)
    322 				return KeyStroke.getKeyStroke(ch);
    323 			else
    324 				return KeyStroke.getKeyStroke(ch,modifiers);
    325 		}
    326 		else if(key.length() == 0)
    327 		{
    328 			System.err.println("Invalid key stroke: " + keyStroke);
    329 			return null;
    330 		}
    331 		else
    332 		{
    333 			int ch;
    334
    335 			try
    336 			{
    337 				ch = KeyEvent.class.getField("VK_".concat(key))
    338 					.getInt(null);
    339 			}
    340 			catch(Exception e)
    341 			{
    342 				System.err.println("Invalid key stroke: "
    343 					+ keyStroke);
    344 				return null;
    345 			}
    346
    347 			return KeyStroke.getKeyStroke(ch,modifiers);
    348 		}
    349 	}
    350
    351 	// private members
    352 	private Hashtable bindings;
    353 	private Hashtable currentBindings;
    354
    355 	private DefaultInputHandler(DefaultInputHandler copy)
    356 	{
    357 		bindings = currentBindings = copy.bindings;
    358 	}
    359 }