PageRenderTime 130ms CodeModel.GetById 102ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/gui/KeyEventTranslator.java

#
Java | 440 lines | 311 code | 27 blank | 102 comment | 79 complexity | b937c7f0c97f61d9789294d96fbfe78b MD5 | raw file
  1/*
  2 * KeyEventTranslator.java - Hides some warts of AWT event API
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2003 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.gui;
 24
 25//{{{ Imports
 26import java.awt.event.*;
 27import java.awt.Toolkit;
 28import java.util.*;
 29import org.gjt.sp.jedit.*;
 30import org.gjt.sp.util.Log;
 31//}}}
 32
 33/**
 34 * In conjunction with the <code>KeyEventWorkaround</code>, hides some 
 35 * warts in the AWT key event API.
 36 *
 37 * @author Slava Pestov
 38 * @version $Id: KeyEventTranslator.java 4941 2003-12-25 01:37:25Z spestov $
 39 */
 40public class KeyEventTranslator
 41{
 42	//{{{ addTranslation() method
 43	/**
 44	 * Adds a keyboard translation.
 45	 * @param key1 Translate this key
 46	 * @param key2 Into this key
 47	 * @since jEdit 4.2pre3
 48	 */
 49	public static void addTranslation(Key key1, Key key2)
 50	{
 51		transMap.put(key1,key2);
 52	} //}}}
 53
 54	//{{{ translateKeyEvent() method
 55	/**
 56	 * Pass this an event from {@link
 57	 * KeyEventWorkaround#processKeyEvent(java.awt.event.KeyEvent)}.
 58	 * @since jEdit 4.2pre3
 59	 */
 60	public static Key translateKeyEvent(KeyEvent evt)
 61	{
 62		int modifiers = evt.getModifiers();
 63		Key returnValue = null;
 64
 65		switch(evt.getID())
 66		{
 67		case KeyEvent.KEY_PRESSED:
 68			int keyCode = evt.getKeyCode();
 69			if((keyCode >= KeyEvent.VK_0
 70				&& keyCode <= KeyEvent.VK_9)
 71				|| (keyCode >= KeyEvent.VK_A
 72				&& keyCode <= KeyEvent.VK_Z))
 73			{
 74				if(Debug.ALTERNATIVE_DISPATCHER)
 75					return null;
 76				else
 77				{
 78					returnValue = new Key(
 79						modifiersToString(modifiers),
 80						'\0',Character.toLowerCase(
 81						(char)keyCode));
 82				}
 83			}
 84			else
 85			{
 86				if(keyCode == KeyEvent.VK_TAB)
 87				{
 88					evt.consume();
 89					returnValue = new Key(
 90						modifiersToString(modifiers),
 91						keyCode,'\0');
 92				}
 93				else if(keyCode == KeyEvent.VK_SPACE)
 94				{
 95					// for SPACE or S+SPACE we pass the
 96					// key typed since international
 97					// keyboards sometimes produce a
 98					// KEY_PRESSED SPACE but not a
 99					// KEY_TYPED SPACE, eg if you have to
100					// do a "<space> to insert ".
101					if((modifiers & ~InputEvent.SHIFT_MASK) == 0)
102						returnValue = null;
103					else
104					{
105						returnValue = new Key(
106							modifiersToString(modifiers),
107							0,' ');
108					}
109				}
110				else
111				{
112					returnValue = new Key(
113						modifiersToString(modifiers),
114						keyCode,'\0');
115				}
116			}
117			break;
118		case KeyEvent.KEY_TYPED:
119			char ch = evt.getKeyChar();
120
121			switch(ch)
122			{
123			case '\n':
124			case '\t':
125			case '\b':
126				return null;
127			case ' ':
128				if((modifiers & ~InputEvent.SHIFT_MASK) != 0)
129					return null;
130			}
131
132			int ignoreMods;
133			if(Debug.ALT_KEY_PRESSED_DISABLED)
134			{
135				/* on MacOS, A+ can be user input */
136				ignoreMods = (InputEvent.SHIFT_MASK
137					| InputEvent.ALT_GRAPH_MASK
138					| InputEvent.ALT_MASK);
139			}
140			else
141			{
142				/* on MacOS, A+ can be user input */
143				ignoreMods = (InputEvent.SHIFT_MASK
144					| InputEvent.ALT_GRAPH_MASK);
145			}
146
147			boolean mod;
148			if((modifiers & InputEvent.ALT_GRAPH_MASK) == 0
149				&& evt.getWhen()
150				-  KeyEventWorkaround.lastKeyTime < 750
151				&& (KeyEventWorkaround.modifiers & ~ignoreMods)
152				!= 0)
153			{
154				if(Debug.ALTERNATIVE_DISPATCHER)
155				{
156					returnValue = new Key(
157						modifiersToString(modifiers),
158						0,ch);
159				}
160				else
161					return null;
162			}
163			else
164			{
165				if(ch == ' ')
166				{
167					returnValue = new Key(
168						modifiersToString(modifiers),
169						0,ch);
170				}
171				else
172					returnValue = new Key(null,0,ch);
173			}
174			break;
175		default:
176			return null;
177		}
178
179		/* I guess translated events do not have the 'evt' field set
180		so consuming won't work. I don't think this is a problem as
181		nothing uses translation anyway */
182		Key trans = (Key)transMap.get(returnValue);
183		if(trans == null)
184			return returnValue;
185		else
186			return trans;
187	} //}}}
188
189	//{{{ parseKey() method
190	/**
191	 * Converts a string to a keystroke. The string should be of the
192	 * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
193	 * is any combination of A for Alt, C for Control, S for Shift
194	 * or M for Meta, and <i>shortcut</i> is either a single character,
195	 * or a keycode name from the <code>KeyEvent</code> class, without
196	 * the <code>VK_</code> prefix.
197	 * @param keyStroke A string description of the key stroke
198	 * @since jEdit 4.2pre3
199	 */
200	public static Key parseKey(String keyStroke)
201	{
202		if(keyStroke == null)
203			return null;
204		int index = keyStroke.indexOf('+');
205		int modifiers = 0;
206		if(index != -1)
207		{
208			for(int i = 0; i < index; i++)
209			{
210				switch(Character.toUpperCase(keyStroke
211					.charAt(i)))
212				{
213				case 'A':
214					modifiers |= a;
215					break;
216				case 'C':
217					modifiers |= c;
218					break;
219				case 'M':
220					modifiers |= m;
221					break;
222				case 'S':
223					modifiers |= s;
224					break;
225				}
226			}
227		}
228		String key = keyStroke.substring(index + 1);
229		if(key.length() == 1)
230		{
231			return new Key(modifiersToString(modifiers),0,key.charAt(0));
232		}
233		else if(key.length() == 0)
234		{
235			Log.log(Log.ERROR,DefaultInputHandler.class,
236				"Invalid key stroke: " + keyStroke);
237			return null;
238		}
239		else if(key.equals("SPACE"))
240		{
241			return new Key(modifiersToString(modifiers),0,' ');
242		}
243		else
244		{
245			int ch;
246
247			try
248			{
249				ch = KeyEvent.class.getField("VK_".concat(key))
250					.getInt(null);
251			}
252			catch(Exception e)
253			{
254				Log.log(Log.ERROR,DefaultInputHandler.class,
255					"Invalid key stroke: "
256					+ keyStroke);
257				return null;
258			}
259
260			return new Key(modifiersToString(modifiers),ch,'\0');
261		}
262	} //}}}
263
264	//{{{ setModifierMapping() method
265	/**
266	 * Changes the mapping between symbolic modifier key names
267	 * (<code>C</code>, <code>A</code>, <code>M</code>, <code>S</code>) and
268	 * Java modifier flags.
269	 *
270	 * @param c The modifier to map the <code>C</code> modifier to
271	 * @param a The modifier to map the <code>A</code> modifier to
272	 * @param m The modifier to map the <code>M</code> modifier to
273	 * @param s The modifier to map the <code>S</code> modifier to
274	 *
275	 * @since jEdit 4.2pre3
276	 */
277	public static void setModifierMapping(int c, int a, int m, int s)
278	{
279		KeyEventTranslator.c = c;
280		KeyEventTranslator.a = a;
281		KeyEventTranslator.m = m;
282		KeyEventTranslator.s = s;
283	} //}}}
284
285	//{{{ getSymbolicModifierName() method
286	/**
287	 * Returns a the symbolic modifier name for the specified Java modifier
288	 * flag.
289	 *
290	 * @param mod A modifier constant from <code>InputEvent</code>
291	 *
292	 * @since jEdit 4.2pre3
293	 */
294	public static char getSymbolicModifierName(int mod)
295	{
296		// this relies on the fact that if C is mapped to M, then
297		// M will be mapped to C.
298		if(mod == c)
299			return 'C';
300		else if(mod == a)
301			return 'A';
302		else if(mod == m)
303			return 'M';
304		else if(mod == s)
305			return 'S';
306		else
307			return '\0';
308	} //}}}
309
310	//{{{ modifiersToString() method
311	public static String modifiersToString(int mods)
312	{
313		StringBuffer buf = null;
314
315		if((mods & InputEvent.CTRL_MASK) != 0)
316		{
317			if(buf == null)
318				buf = new StringBuffer();
319			buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
320		}
321		if((mods & InputEvent.ALT_MASK) != 0)
322		{
323			if(buf == null)
324				buf = new StringBuffer();
325			buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
326		}
327		if((mods & InputEvent.META_MASK) != 0)
328		{
329			if(buf == null)
330				buf = new StringBuffer();
331			buf.append(getSymbolicModifierName(InputEvent.META_MASK));
332		}
333		if((mods & InputEvent.SHIFT_MASK) != 0)
334		{
335			if(buf == null)
336				buf = new StringBuffer();
337			buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
338		}
339
340		if(buf == null)
341			return null;
342		else
343			return buf.toString();
344	} //}}}
345
346	//{{{ getModifierString() method
347	/**
348	 * Returns a string containing symbolic modifier names set in the
349	 * specified event.
350	 *
351	 * @param evt The event
352	 *
353	 * @since jEdit 4.2pre3
354	 */
355	public static String getModifierString(InputEvent evt)
356	{
357		StringBuffer buf = new StringBuffer();
358		if(evt.isControlDown())
359			buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
360		if(evt.isAltDown())
361			buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
362		if(evt.isMetaDown())
363			buf.append(getSymbolicModifierName(InputEvent.META_MASK));
364		if(evt.isShiftDown())
365			buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
366		return (buf.length() == 0 ? null : buf.toString());
367	} //}}}
368
369	static int c, a, m, s;
370
371	//{{{ Private members
372	private static Map transMap = new HashMap();
373
374	static
375	{
376		if(OperatingSystem.isMacOS())
377		{
378			setModifierMapping(
379				InputEvent.META_MASK,  /* == C+ */
380				InputEvent.CTRL_MASK,  /* == A+ */
381				/* M+ discarded by key event workaround! */
382				InputEvent.ALT_MASK,   /* == M+ */
383				InputEvent.SHIFT_MASK  /* == S+ */);
384		}
385		else
386		{
387			setModifierMapping(
388				InputEvent.CTRL_MASK,
389				InputEvent.ALT_MASK,
390				InputEvent.META_MASK,
391				InputEvent.SHIFT_MASK);
392		}
393	} //}}}
394
395	//{{{ Key class
396	public static class Key
397	{
398		public String modifiers;
399		public int key;
400		public char input;
401
402		public Key(String modifiers, int key, char input)
403		{
404			this.modifiers = modifiers;
405			this.key = key;
406			this.input = input;
407		}
408
409		public int hashCode()
410		{
411			return key + input;
412		}
413
414		public boolean equals(Object o)
415		{
416			if(o instanceof Key)
417			{
418				Key k = (Key)o;
419				if(MiscUtilities.objectsEqual(modifiers,
420					k.modifiers) && key == k.key
421					&& input == k.input)
422				{
423					return true;
424				}
425			}
426
427			return false;
428		}
429
430		public String toString()
431		{
432			return (modifiers == null ? "" : modifiers)
433				+ "<"
434				+ Integer.toString(key,16)
435				+ ","
436				+ Integer.toString(input,16)
437				+ ">";
438		}
439	} //}}}
440}