001/* AWTKeyStroke.java -- an immutable key stroke
002   Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt;
040
041import java.awt.event.InputEvent;
042import java.awt.event.KeyEvent;
043import java.io.ObjectStreamException;
044import java.io.Serializable;
045import java.lang.reflect.Constructor;
046import java.lang.reflect.Field;
047import java.lang.reflect.InvocationTargetException;
048import java.security.AccessController;
049import java.security.PrivilegedAction;
050import java.security.PrivilegedActionException;
051import java.security.PrivilegedExceptionAction;
052import java.util.HashMap;
053import java.util.LinkedHashMap;
054import java.util.Map;
055import 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 */
073public 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 := &lt;modifiers&gt;* ( &lt;typedID&gt; | &lt;codeID&gt; )
378   * modifiers := ( shift | control | ctrl | meta | alt
379   *                | button1 | button2 | button3 )
380   * typedID := typed &lt;single Unicode character&gt;
381   * codeID := ( pressed | released )? &lt;name&gt;
382   * name := &lt;the KeyEvent field name less the leading "VK_"&gt;
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" =&gt; getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br>
392   * "control DELETE" =&gt;
393   *    getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br>
394   * "alt shift X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
395   *    InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br>
396   * "alt shift released X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
397   *    InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br>
398   * "typed a" =&gt; 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