OSDN Git Service

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