001 /* AWTKeyStroke.java -- an immutable key stroke
002 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt;
040
041 import java.awt.event.InputEvent;
042 import java.awt.event.KeyEvent;
043 import java.io.ObjectStreamException;
044 import java.io.Serializable;
045 import java.lang.reflect.Constructor;
046 import java.lang.reflect.Field;
047 import java.lang.reflect.InvocationTargetException;
048 import java.security.AccessController;
049 import java.security.PrivilegedAction;
050 import java.security.PrivilegedActionException;
051 import java.security.PrivilegedExceptionAction;
052 import java.util.HashMap;
053 import java.util.LinkedHashMap;
054 import java.util.Map;
055 import java.util.StringTokenizer;
056
057 /**
058 * This class mirrors KeyEvents, representing both low-level key presses and
059 * key releases, and high level key typed inputs. However, this class forms
060 * immutable strokes, and can be efficiently reused via the factory methods
061 * for creating them.
062 *
063 * <p>For backwards compatibility with Swing, this supports a way to build
064 * instances of a subclass, using reflection, provided the subclass has a
065 * no-arg constructor (of any accessibility).
066 *
067 * @author Eric Blake (ebb9@email.byu.edu)
068 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
069 * @see #getAWTKeyStroke(char)
070 * @since 1.4
071 * @status updated to 1.4
072 */
073 public class AWTKeyStroke implements Serializable
074 {
075 /**
076 * Compatible with JDK 1.4+.
077 */
078 private static final long serialVersionUID = -6430539691155161871L;
079
080 /** The mask for modifiers. */
081 private static final int MODIFIERS_MASK = 0x3fef;
082
083 /**
084 * The cache of recently created keystrokes. This maps KeyStrokes to
085 * KeyStrokes in a cache which removes the least recently accessed entry,
086 * under the assumption that garbage collection of a new keystroke is
087 * easy when we find the old one that it matches in the cache.
088 */
089 private static final LinkedHashMap<AWTKeyStroke,AWTKeyStroke> cache =
090 new LinkedHashMap<AWTKeyStroke,AWTKeyStroke>(11, 0.75f, true)
091 {
092 /** The largest the keystroke cache can grow. */
093 private static final int MAX_CACHE_SIZE = 2048;
094
095 /** Prune stale entries. */
096 protected boolean removeEldestEntry(Map.Entry<AWTKeyStroke,AWTKeyStroke>
097 eldest)
098 {
099 return size() > MAX_CACHE_SIZE;
100 }
101 };
102
103 /** The most recently generated keystroke, or null. */
104 private static AWTKeyStroke recent;
105
106 /**
107 * The no-arg constructor of a subclass, or null to use AWTKeyStroke. Note
108 * that this will be left accessible, to get around private access; but
109 * it should not be a security risk as it is highly unlikely that creating
110 * protected instances of the subclass via reflection will do much damage.
111 */
112 private static Constructor ctor;
113
114 /**
115 * A table of keyCode names to values. This is package-private to
116 * avoid an accessor method.
117 *
118 * @see #getAWTKeyStroke(String)
119 */
120 static final HashMap<String,Object> vktable = new HashMap<String,Object>();
121 static
122 {
123 // Using reflection saves the hassle of keeping this in sync with KeyEvent,
124 // at the price of an expensive initialization.
125 AccessController.doPrivileged(new PrivilegedAction()
126 {
127 public Object run()
128 {
129 Field[] fields = KeyEvent.class.getFields();
130 int i = fields.length;
131 try
132 {
133 while (--i >= 0)
134 {
135 Field f = fields[i];
136 String name = f.getName();
137 if (name.startsWith("VK_"))
138 vktable.put(name.substring(3), f.get(null));
139 }
140 }
141 catch (Exception e)
142 {
143 throw (Error) new InternalError().initCause(e);
144 }
145 return null;
146 }
147 });
148 }
149
150 /**
151 * The typed character, or CHAR_UNDEFINED for key presses and releases.
152 *
153 * @serial the keyChar
154 */
155 private char keyChar;
156
157 /**
158 * The virtual key code, or VK_UNDEFINED for key typed. Package visible for
159 * use by Component.
160 *
161 * @serial the keyCode
162 */
163 int keyCode;
164
165 /**
166 * The modifiers in effect. To match Sun, this stores the old style masks
167 * for shift, control, alt, meta, and alt-graph (but not button1); as well
168 * as the new style of extended modifiers for all modifiers.
169 *
170 * @serial bitwise or of the *_DOWN_MASK modifiers
171 */
172 private int modifiers;
173
174 /**
175 * True if this is a key release; should only be true if keyChar is
176 * CHAR_UNDEFINED.
177 *
178 * @serial true to distinguish key pressed from key released
179 */
180 private boolean onKeyRelease;
181
182 /**
183 * Construct a keystroke with default values: it will be interpreted as a
184 * key typed event with an invalid character and no modifiers. Client code
185 * should use the factory methods instead.
186 *
187 * @see #getAWTKeyStroke(char)
188 * @see #getAWTKeyStroke(Character, int)
189 * @see #getAWTKeyStroke(int, int, boolean)
190 * @see #getAWTKeyStroke(int, int)
191 * @see #getAWTKeyStrokeForEvent(KeyEvent)
192 * @see #getAWTKeyStroke(String)
193 */
194 protected AWTKeyStroke()
195 {
196 keyChar = KeyEvent.CHAR_UNDEFINED;
197 }
198
199 /**
200 * Construct a keystroke with the given values. Client code should use the
201 * factory methods instead.
202 *
203 * @param keyChar the character entered, if this is a key typed
204 * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed
205 * @param modifiers the modifier keys for the keystroke, in old or new style
206 * @param onKeyRelease true if this is a key release instead of a press
207 * @see #getAWTKeyStroke(char)
208 * @see #getAWTKeyStroke(Character, int)
209 * @see #getAWTKeyStroke(int, int, boolean)
210 * @see #getAWTKeyStroke(int, int)
211 * @see #getAWTKeyStrokeForEvent(KeyEvent)
212 * @see #getAWTKeyStroke(String)
213 */
214 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
215 boolean onKeyRelease)
216 {
217 this.keyChar = keyChar;
218 this.keyCode = keyCode;
219 // No need to call extend(), as only trusted code calls this constructor.
220 this.modifiers = modifiers;
221 this.onKeyRelease = onKeyRelease;
222 }
223
224 /**
225 * Registers a new subclass as being the type of keystrokes to generate in
226 * the factory methods. This operation flushes the cache of stored keystrokes
227 * if the class differs from the current one. The new class must be
228 * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may
229 * be private).
230 *
231 * @param subclass the new runtime type of generated keystrokes
232 * @throws IllegalArgumentException subclass doesn't have no-arg constructor
233 * @throws ClassCastException subclass doesn't extend AWTKeyStroke
234 */
235 protected static void registerSubclass(final Class<?> subclass)
236 {
237 if (subclass == null)
238 throw new IllegalArgumentException();
239 if (subclass.equals(ctor == null ? AWTKeyStroke.class
240 : ctor.getDeclaringClass()))
241 return;
242 if (subclass.equals(AWTKeyStroke.class))
243 {
244 cache.clear();
245 recent = null;
246 ctor = null;
247 return;
248 }
249 try
250 {
251 ctor = (Constructor) AccessController.doPrivileged
252 (new PrivilegedExceptionAction()
253 {
254 public Object run()
255 throws NoSuchMethodException, InstantiationException,
256 IllegalAccessException, InvocationTargetException
257 {
258 Constructor<?> c =
259 subclass.getDeclaredConstructor((Class<?>[])null);
260 c.setAccessible(true);
261 // Create a new instance, to make sure that we can, and
262 // to cause any ClassCastException.
263 AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance();
264 return c;
265 }
266 });
267 }
268 catch (PrivilegedActionException e)
269 {
270 // e.getCause() will not ever be ClassCastException; that should
271 // escape on its own.
272 throw (RuntimeException)
273 new IllegalArgumentException().initCause(e.getCause());
274 }
275 cache.clear();
276 recent = null;
277 }
278
279 /**
280 * Returns a keystroke representing a typed character.
281 *
282 * @param keyChar the typed character
283 * @return the specified keystroke
284 */
285 public static AWTKeyStroke getAWTKeyStroke(char keyChar)
286 {
287 return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
288 }
289
290 /**
291 * Returns a keystroke representing a typed character with the given
292 * modifiers. Note that keyChar is a <code>Character</code> instead of a
293 * <code>char</code> to avoid accidental ambiguity with
294 * <code>getAWTKeyStroke(int, int)</code>. The modifiers are the bitwise
295 * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK)
296 * is preferred, but the old style will work.
297 *
298 * @param keyChar the typed character
299 * @param modifiers the modifiers, or 0
300 * @return the specified keystroke
301 * @throws IllegalArgumentException if keyChar is null
302 */
303 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
304 {
305 if (keyChar == null)
306 throw new IllegalArgumentException();
307 return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
308 extend(modifiers), false);
309 }
310
311 /**
312 * Returns a keystroke representing a pressed or released key event, with
313 * the given modifiers. The "virtual key" should be one of the VK_*
314 * constants in {@link KeyEvent}. The modifiers are the bitwise or of the
315 * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is
316 * preferred, but the old style will work.
317 *
318 * @param keyCode the virtual key
319 * @param modifiers the modifiers, or 0
320 * @param release true if this is a key release instead of a key press
321 * @return the specified keystroke
322 */
323 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
324 boolean release)
325 {
326 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
327 extend(modifiers), release);
328 }
329
330 /**
331 * Returns a keystroke representing a pressed key event, with the given
332 * modifiers. The "virtual key" should be one of the VK_* constants in
333 * {@link KeyEvent}. The modifiers are the bitwise or of the masks found
334 * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the
335 * old style will work.
336 *
337 * @param keyCode the virtual key
338 * @param modifiers the modifiers, or 0
339 * @return the specified keystroke
340 */
341 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers)
342 {
343 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
344 extend(modifiers), false);
345 }
346
347 /**
348 * Returns a keystroke representing what caused the key event.
349 *
350 * @param event the key event to convert
351 * @return the specified keystroke, or null if the event is invalid
352 * @throws NullPointerException if event is null
353 */
354 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event)
355 {
356 switch (event.id)
357 {
358 case KeyEvent.KEY_TYPED:
359 return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED,
360 extend(event.getModifiersEx()), false);
361 case KeyEvent.KEY_PRESSED:
362 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
363 extend(event.getModifiersEx()), false);
364 case KeyEvent.KEY_RELEASED:
365 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
366 extend(event.getModifiersEx()), true);
367 default:
368 return null;
369 }
370 }
371
372 /**
373 * Parses a string and returns the keystroke that it represents. The syntax
374 * for keystrokes is listed below, with tokens separated by an arbitrary
375 * number of spaces:
376 * <pre>
377 * keyStroke := <modifiers>* ( <typedID> | <codeID> )
378 * modifiers := ( shift | control | ctrl | meta | alt
379 * | button1 | button2 | button3 )
380 * typedID := typed <single Unicode character>
381 * codeID := ( pressed | released )? <name>
382 * name := <the KeyEvent field name less the leading "VK_">
383 * </pre>
384 *
385 * <p>Note that the grammar is rather weak, and not all valid keystrokes
386 * can be generated in this manner (for example, a typed space, or anything
387 * with the alt-graph modifier!). The output of AWTKeyStroke.toString()
388 * will not meet the grammar. If pressed or released is not specified,
389 * pressed is assumed. Examples:<br>
390 * <code>
391 * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br>
392 * "control DELETE" =>
393 * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br>
394 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X,
395 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br>
396 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X,
397 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br>
398 * "typed a" => getAWTKeyStroke('a');
399 * </code>
400 *
401 * @param s the string to parse
402 * @throws IllegalArgumentException if s is null or cannot be parsed
403 * @return the specified keystroke
404 */
405 public static AWTKeyStroke getAWTKeyStroke(String s)
406 {
407 if (s == null)
408 throw new IllegalArgumentException("null argument");
409 StringTokenizer t = new StringTokenizer(s, " ");
410 if (! t.hasMoreTokens())
411 throw new IllegalArgumentException("no tokens '" + s + "'");
412 int modifiers = 0;
413 boolean released = false;
414 String token = null;
415 do
416 {
417 token = t.nextToken();
418 if ("shift".equals(token))
419 {
420 modifiers |= KeyEvent.SHIFT_MASK;
421 modifiers |= KeyEvent.SHIFT_DOWN_MASK;
422 }
423 else if ("ctrl".equals(token) || "control".equals(token))
424 {
425 modifiers |= KeyEvent.CTRL_MASK;
426 modifiers |= KeyEvent.CTRL_DOWN_MASK;
427 }
428 else if ("meta".equals(token))
429 {
430 modifiers |= KeyEvent.META_MASK;
431 modifiers |= KeyEvent.META_DOWN_MASK;
432 }
433 else if ("alt".equals(token))
434 {
435 modifiers |= KeyEvent.ALT_MASK;
436 modifiers |= KeyEvent.ALT_DOWN_MASK;
437 }
438 else if ("button1".equals(token))
439 modifiers |= KeyEvent.BUTTON1_DOWN_MASK;
440 else if ("button2".equals(token))
441 modifiers |= KeyEvent.BUTTON2_DOWN_MASK;
442 else if ("button3".equals(token))
443 modifiers |= KeyEvent.BUTTON3_DOWN_MASK;
444 else if ("typed".equals(token))
445 {
446 if (t.hasMoreTokens())
447 {
448 token = t.nextToken();
449 if (! t.hasMoreTokens() && token.length() == 1)
450 return getAWTKeyStroke(token.charAt(0),
451 KeyEvent.VK_UNDEFINED, modifiers,
452 false);
453 }
454 throw new IllegalArgumentException("Invalid 'typed' argument '"
455 + s + "'");
456 }
457 else if ("pressed".equals(token))
458 {
459 if (t.hasMoreTokens())
460 token = t.nextToken();
461 break;
462 }
463 else if ("released".equals(token))
464 {
465 released = true;
466 if (t.hasMoreTokens())
467 token = t.nextToken();
468 break;
469 }
470 else
471 break;
472 }
473 while (t.hasMoreTokens());
474 // Now token contains the VK name we must parse.
475 Integer code = (Integer) vktable.get(token);
476 if (code == null)
477 throw new IllegalArgumentException("Unknown token '" + token
478 + "' in '" + s + "'");
479 if (t.hasMoreTokens())
480 throw new IllegalArgumentException("Too many tokens: " + s);
481 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(),
482 modifiers, released);
483 }
484
485 /**
486 * Returns the character of this keystroke, if it was typed.
487 *
488 * @return the character value, or CHAR_UNDEFINED
489 * @see #getAWTKeyStroke(char)
490 */
491 public final char getKeyChar()
492 {
493 return keyChar;
494 }
495
496 /**
497 * Returns the virtual key code of this keystroke, if it was pressed or
498 * released. This will be a VK_* constant from KeyEvent.
499 *
500 * @return the virtual key code value, or VK_UNDEFINED
501 * @see #getAWTKeyStroke(int, int)
502 */
503 public final int getKeyCode()
504 {
505 return keyCode;
506 }
507
508 /**
509 * Returns the modifiers for this keystroke. This will be a bitwise or of
510 * constants from InputEvent; it includes the old style masks for shift,
511 * control, alt, meta, and alt-graph (but not button1); as well as the new
512 * style of extended modifiers for all modifiers.
513 *
514 * @return the modifiers
515 * @see #getAWTKeyStroke(Character, int)
516 * @see #getAWTKeyStroke(int, int)
517 */
518 public final int getModifiers()
519 {
520 return modifiers;
521 }
522
523 /**
524 * Tests if this keystroke is a key release.
525 *
526 * @return true if this is a key release
527 * @see #getAWTKeyStroke(int, int, boolean)
528 */
529 public final boolean isOnKeyRelease()
530 {
531 return onKeyRelease;
532 }
533
534 /**
535 * Returns the AWT event type of this keystroke. This is one of
536 * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or
537 * {@link KeyEvent#KEY_RELEASED}.
538 *
539 * @return the key event type
540 */
541 public final int getKeyEventType()
542 {
543 return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
544 : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
545 }
546
547 /**
548 * Returns a hashcode for this key event. It is not documented, but appears
549 * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1)
550 * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>.
551 *
552 * @return the hashcode
553 */
554 public int hashCode()
555 {
556 return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
557 + (onKeyRelease ? 1 : 2);
558 }
559
560 /**
561 * Tests two keystrokes for equality.
562 *
563 * @param o the object to test
564 * @return true if it is equal
565 */
566 public final boolean equals(Object o)
567 {
568 if (! (o instanceof AWTKeyStroke))
569 return false;
570 AWTKeyStroke s = (AWTKeyStroke) o;
571 return this == o || (keyChar == s.keyChar && keyCode == s.keyCode
572 && modifiers == s.modifiers
573 && onKeyRelease == s.onKeyRelease);
574 }
575
576 /**
577 * Returns a string representation of this keystroke. For typed keystrokes,
578 * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers())
579 + getKeyChar()</code>; for pressed and released keystrokes, this is
580 * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers())
581 * + KeyEvent.getKeyText(getKeyCode())
582 * + (isOnKeyRelease() ? "-R" : "-P")</code>.
583 *
584 * @return a string representation
585 */
586 public String toString()
587 {
588 if (keyCode == KeyEvent.VK_UNDEFINED)
589 return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar;
590 return "keyCode " + KeyEvent.getKeyModifiersText(modifiers)
591 + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P");
592 }
593
594 /**
595 * Returns a cached version of the deserialized keystroke, if available.
596 *
597 * @return a cached replacement
598 * @throws ObjectStreamException if something goes wrong
599 */
600 protected Object readResolve() throws ObjectStreamException
601 {
602 AWTKeyStroke s = cache.get(this);
603 if (s != null)
604 return s;
605 cache.put(this, this);
606 return this;
607 }
608
609 /**
610 * Gets the appropriate keystroke, creating one if necessary.
611 *
612 * @param keyChar the keyChar
613 * @param keyCode the keyCode
614 * @param modifiers the modifiers
615 * @param release true for key release
616 * @return the specified keystroke
617 */
618 private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
619 int modifiers, boolean release)
620 {
621 // Check level 0 cache.
622 AWTKeyStroke stroke = recent; // Avoid thread races.
623 if (stroke != null && stroke.keyChar == keyChar
624 && stroke.keyCode == keyCode && stroke.modifiers == modifiers
625 && stroke.onKeyRelease == release)
626 return stroke;
627 // Create a new object, on the assumption that if it has a match in the
628 // cache, the VM can easily garbage collect it as it is temporary.
629 Constructor c = ctor; // Avoid thread races.
630 if (c == null)
631 stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release);
632 else
633 try
634 {
635 stroke = (AWTKeyStroke) c.newInstance();
636 stroke.keyChar = keyChar;
637 stroke.keyCode = keyCode;
638 stroke.modifiers = modifiers;
639 stroke.onKeyRelease = release;
640 }
641 catch (Exception e)
642 {
643 throw (Error) new InternalError().initCause(e);
644 }
645 // Check level 1 cache.
646 AWTKeyStroke cached = cache.get(stroke);
647 if (cached == null)
648 cache.put(stroke, stroke);
649 else
650 stroke = cached;
651 return recent = stroke;
652 }
653
654 /**
655 * Converts the modifiers to the appropriate format.
656 *
657 * @param mod the modifiers to convert
658 * @return the adjusted modifiers
659 */
660 private static int extend(int mod)
661 {
662 if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0)
663 mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
664 if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0)
665 mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
666 if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0)
667 mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
668 if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0)
669 mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
670 if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0)
671 mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK;
672 if ((mod & KeyEvent.BUTTON1_MASK) != 0)
673 mod |= KeyEvent.BUTTON1_DOWN_MASK;
674 return mod & MODIFIERS_MASK;
675 }
676 } // class AWTKeyStroke