Source for java.awt.DefaultKeyboardFocusManager

   1: /* DefaultKeyboardFocusManager.java -- 
   2:    Copyright (C) 2002, 2004  Free Software Foundation, Inc.
   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.ActionEvent;
  42: import java.awt.event.FocusEvent;
  43: import java.awt.event.KeyEvent;
  44: import java.awt.event.WindowEvent;
  45: import java.util.Iterator;
  46: import java.util.LinkedList;
  47: import java.util.Set;
  48: import java.util.SortedSet;
  49: import java.util.TreeSet;
  50: 
  51: // FIXME: finish documentation
  52: public class DefaultKeyboardFocusManager extends KeyboardFocusManager
  53: {
  54:   /**
  55:    * This class models a request to delay the dispatch of events that
  56:    * arrive after a certain time, until a certain component becomes
  57:    * the focus owner.
  58:    */
  59:   private class EventDelayRequest implements Comparable
  60:   {
  61:     /** A {@link java.util.List} of {@link java.awt.event.KeyEvent}s
  62:         that are being delayed, pending this request's {@link
  63:         Component} receiving the keyboard focus. */
  64:     private LinkedList enqueuedKeyEvents = new LinkedList ();
  65: 
  66:     /** An event timestamp.  All events that arrive after this time
  67:         should be queued in the {@link #enqueuedKeyEvents} {@link
  68:         java.util.List}. */
  69:     public long timestamp;
  70:     /** When this {@link Component} becomes focused, all events
  71:         between this EventDelayRequest and the next one in will be
  72:         dispatched from {@link #enqueuedKeyEvents}. */
  73:     public Component focusedComp;
  74: 
  75:     /**
  76:      * Construct a new EventDelayRequest.
  77:      *
  78:      * @param timestamp events that arrive after this time will be
  79:      * delayed
  80:      * @param focusedComp the Component that needs to receive focus
  81:      * before events are dispatched
  82:      */
  83:     public EventDelayRequest (long timestamp, Component focusedComp)
  84:     {
  85:       this.timestamp = timestamp;
  86:       this.focusedComp = focusedComp;
  87:     }
  88: 
  89:     public int compareTo (Object o)
  90:     {
  91:       if (!(o instanceof EventDelayRequest))
  92:         throw new ClassCastException ();
  93: 
  94:       EventDelayRequest request = (EventDelayRequest) o;
  95: 
  96:       if (request.timestamp < timestamp)
  97:         return -1;
  98:       else if (request.timestamp == timestamp)
  99:         return 0;
 100:       else
 101:         return 1;
 102:     }
 103: 
 104:     public boolean equals (Object o)
 105:     {
 106:       if (!(o instanceof EventDelayRequest) || o == null)
 107:         return false;
 108: 
 109:       EventDelayRequest request = (EventDelayRequest) o;
 110: 
 111:       return (request.timestamp == timestamp
 112:               && request.focusedComp == focusedComp);
 113:     }
 114: 
 115:     public void enqueueEvent (KeyEvent e)
 116:     {
 117:       KeyEvent last = (KeyEvent) enqueuedKeyEvents.getLast ();
 118:       if (last != null && e.getWhen () < last.getWhen ())
 119:         throw new RuntimeException ("KeyEvents enqueued out-of-order");
 120: 
 121:       if (e.getWhen () <= timestamp)
 122:         throw new RuntimeException ("KeyEvents enqueued before starting timestamp");
 123: 
 124:       enqueuedKeyEvents.add (e);
 125:     }
 126: 
 127:     public void dispatchEvents ()
 128:     {
 129:       int size = enqueuedKeyEvents.size ();
 130:       for (int i = 0; i < size; i++)
 131:         {
 132:           KeyEvent e = (KeyEvent) enqueuedKeyEvents.remove (0);
 133:           dispatchKeyEvent (e);
 134:         }
 135:     }
 136: 
 137:     public void discardEvents ()
 138:     {
 139:       enqueuedKeyEvents.clear ();
 140:     }
 141:   }
 142: 
 143:   /**
 144:    * This flag indicates for which focus traversal key release event we
 145:    * possibly wait, before letting any more KEY_TYPED events through.
 146:    */
 147:   private AWTKeyStroke waitForKeyStroke = null;
 148: 
 149:   /** The {@link java.util.SortedSet} of current 
 150:    * {@link EventDelayRequest}s. */
 151:   private SortedSet delayRequests = new TreeSet ();
 152: 
 153:   public DefaultKeyboardFocusManager ()
 154:   {
 155:   }
 156: 
 157:   public boolean dispatchEvent (AWTEvent e)
 158:   {
 159:     if (e instanceof WindowEvent)
 160:       {
 161:         Window target = (Window) e.getSource ();
 162: 
 163:         if (e.id == WindowEvent.WINDOW_ACTIVATED)
 164:           setGlobalActiveWindow (target);
 165:         else if (e.id == WindowEvent.WINDOW_GAINED_FOCUS)
 166:           setGlobalFocusedWindow (target);
 167:         else if (e.id != WindowEvent.WINDOW_LOST_FOCUS
 168:                  && e.id != WindowEvent.WINDOW_DEACTIVATED)
 169:           return false;
 170: 
 171:         redispatchEvent(target, e);
 172:         return true;
 173:       }
 174:     else if (e instanceof FocusEvent)
 175:       {
 176:         Component target = (Component) e.getSource ();
 177: 
 178:         if (e.id == FocusEvent.FOCUS_GAINED)
 179:           {
 180:             if (! (target instanceof Window))
 181:               {
 182:                 if (((FocusEvent) e).isTemporary ())
 183:                   setGlobalFocusOwner (target);
 184:                 else
 185:                   setGlobalPermanentFocusOwner (target);
 186:               }
 187: 
 188:             // Keep track of this window's focus owner.
 189: 
 190:             // Find the target Component's top-level ancestor.  target
 191:             // may be a window.
 192:             Container parent = target.getParent ();
 193: 
 194:             while (parent != null
 195:                    && !(parent instanceof Window))
 196:               parent = parent.getParent ();
 197: 
 198:             // If the parent is null and target is not a window, then target is an
 199:             // unanchored component and so we don't want to set the focus owner.
 200:             if (! (parent == null && ! (target instanceof Window)))
 201:               {
 202:                 Window toplevel = parent == null ?
 203:                   (Window) target : (Window) parent;
 204: 
 205:                 Component focusOwner = getFocusOwner ();
 206:                 if (focusOwner != null
 207:                     && ! (focusOwner instanceof Window))
 208:                   toplevel.setFocusOwner (focusOwner);
 209:               }
 210:           }
 211:         else if (e.id == FocusEvent.FOCUS_LOST)
 212:           {
 213:             if (((FocusEvent) e).isTemporary ())
 214:               setGlobalFocusOwner (null);
 215:             else
 216:               setGlobalPermanentFocusOwner (null);
 217:           }
 218: 
 219:         redispatchEvent(target, e);
 220: 
 221:         return true;
 222:       }
 223:     else if (e instanceof KeyEvent)
 224:       {
 225:         // Loop through all registered KeyEventDispatchers, giving
 226:         // each a chance to handle this event.
 227:         Iterator i = getKeyEventDispatchers().iterator();
 228: 
 229:         while (i.hasNext ())
 230:           {
 231:             KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next ();
 232:             if (dispatcher.dispatchKeyEvent ((KeyEvent) e))
 233:               return true;
 234:           }
 235: 
 236:         // processKeyEvent checks if this event represents a focus
 237:         // traversal key stroke.
 238:         Component focusOwner = getGlobalPermanentFocusOwner ();
 239: 
 240:         if (focusOwner != null)
 241:           processKeyEvent (focusOwner, (KeyEvent) e);
 242: 
 243:         if (e.isConsumed ())
 244:           return true;
 245: 
 246:         if (enqueueKeyEvent ((KeyEvent) e))
 247:           // This event was enqueued for dispatch at a later time.
 248:           return true;
 249:         else
 250:           // This event wasn't handled by any of the registered
 251:           // KeyEventDispatchers, and wasn't enqueued for dispatch
 252:           // later, so send it to the default dispatcher.
 253:           return dispatchKeyEvent ((KeyEvent) e);
 254:       }
 255: 
 256:     return false;
 257:   }
 258: 
 259:   private boolean enqueueKeyEvent (KeyEvent e)
 260:   {
 261:     Iterator i = delayRequests.iterator ();
 262:     boolean oneEnqueued = false;
 263:     while (i.hasNext ())
 264:       {
 265:         EventDelayRequest request = (EventDelayRequest) i.next ();
 266:         if (e.getWhen () > request.timestamp)
 267:           {
 268:             request.enqueueEvent (e);
 269:             oneEnqueued = true;
 270:           }
 271:       }
 272:     return oneEnqueued;
 273:   }
 274: 
 275:   public boolean dispatchKeyEvent (KeyEvent e)
 276:   {
 277:     Component focusOwner = getGlobalPermanentFocusOwner ();
 278: 
 279:     if (focusOwner != null)
 280:       redispatchEvent(focusOwner, e);
 281: 
 282:     // Loop through all registered KeyEventPostProcessors, giving
 283:     // each a chance to process this event.
 284:     Iterator i = getKeyEventPostProcessors().iterator();
 285: 
 286:     while (i.hasNext ())
 287:       {
 288:         KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next ();
 289:         if (processor.postProcessKeyEvent ((KeyEvent) e))
 290:           return true;
 291:       }
 292: 
 293:     // The event hasn't been consumed yet.  Check if it is an
 294:     // MenuShortcut.
 295:     if (postProcessKeyEvent (e))
 296:       return true;
 297: 
 298:     // Always return true.
 299:     return true;
 300:   }
 301: 
 302:   public boolean postProcessKeyEvent (KeyEvent e)
 303:   {
 304:     // Check if this event represents a menu shortcut.
 305: 
 306:     // MenuShortcuts are activated by Ctrl- KeyEvents, only on KEY_PRESSED.
 307:     int modifiers = e.getModifiersEx ();
 308:     if (e.getID() == KeyEvent.KEY_PRESSED
 309:         && (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0)
 310:       {
 311:         Window focusedWindow = getGlobalFocusedWindow ();
 312:         if (focusedWindow instanceof Frame)
 313:           {
 314:             MenuBar menubar = ((Frame) focusedWindow).getMenuBar ();
 315: 
 316:             if (menubar != null)
 317:               {
 318:                 // If there's a menubar, loop through all menu items,
 319:                 // checking whether each one has a shortcut, and if
 320:                 // so, whether this key event should activate it.
 321:                 int numMenus = menubar.getMenuCount ();
 322: 
 323:                 for (int i = 0; i < numMenus; i++)
 324:                   {
 325:                     Menu menu = menubar.getMenu (i);
 326:                     int numItems = menu.getItemCount ();
 327: 
 328:                     for (int j = 0; j < numItems; j++)
 329:                       {
 330:                         MenuItem item = menu.getItem (j);
 331:                         MenuShortcut shortcut = item.getShortcut ();
 332: 
 333:                         if (item.isEnabled() && shortcut != null)
 334:                           {
 335:                             // Dispatch a new ActionEvent if:
 336:                             //
 337:                             //     a) this is a Shift- KeyEvent, and the
 338:                             //        shortcut requires the Shift modifier
 339:                             //
 340:                             // or, b) this is not a Shift- KeyEvent, and the
 341:                             //        shortcut does not require the Shift
 342:                             //        modifier.
 343:                             if (shortcut.getKey () == e.getKeyCode ()
 344:                                 && ((shortcut.usesShiftModifier ()
 345:                                      && (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0)
 346:                                     || (! shortcut.usesShiftModifier ()
 347:                                         && (modifiers & KeyEvent.SHIFT_DOWN_MASK) == 0)))
 348:                               {
 349:                                 item.dispatchEvent (new ActionEvent (item,
 350:                                                                      ActionEvent.ACTION_PERFORMED,
 351:                                                                      item.getActionCommand (),
 352:                                                                      modifiers));
 353:                                 // The event was dispatched.
 354:                                 return true;
 355:                               }
 356:                           }
 357:                       }
 358:                   }
 359:               }
 360:           }
 361:       }
 362:     return false;
 363:   }
 364: 
 365:   public void processKeyEvent (Component comp, KeyEvent e)
 366:   {
 367:     AWTKeyStroke eventKeystroke = AWTKeyStroke.getAWTKeyStrokeForEvent (e);
 368:     // For every focus traversal keystroke, we need to also consume
 369:     // the other two key event types for the same key (e.g. if
 370:     // KEY_PRESSED TAB is a focus traversal keystroke, we also need to
 371:     // consume KEY_RELEASED and KEY_TYPED TAB key events).
 372:     // consuming KEY_RELEASED is easy, because their keyCodes matches
 373:     // the KEY_PRESSED event. Consuming the intermediate KEY_TYPED is
 374:     // very difficult because their is no clean way that we can know
 375:     // which KEY_TYPED belongs to a focusTraversalKey and which not.
 376:     // To address this problem we swallow every KEY_TYPE between the
 377:     // KEY_PRESSED event that matches a focusTraversalKey and the
 378:     // corresponding KEY_RELEASED.
 379:     AWTKeyStroke oppositeKeystroke = AWTKeyStroke.getAWTKeyStroke (e.getKeyCode (),
 380:                                                                    e.getModifiersEx (),
 381:                                                                    !(e.id == KeyEvent.KEY_RELEASED));
 382: 
 383:     // Here we check if we are currently waiting for a KEY_RELEASED and
 384:     // swallow all KeyEvents that are to be delivered in between. This
 385:     // should only be the KEY_TYPED events that correspond to the
 386:     // focusTraversalKey's KEY_PRESSED event
 387:     if (waitForKeyStroke != null)
 388:       {
 389:         if (eventKeystroke.equals(waitForKeyStroke))
 390:           // release this lock
 391:           waitForKeyStroke = null;
 392: 
 393:         // as long as we are waiting for the KEY_RELEASED, we swallow every
 394:         // KeyEvent, including the KEY_RELEASED
 395:         e.consume();
 396:         return;
 397:       }
 398: 
 399:     Set forwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
 400:     Set backwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
 401:     Set upKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
 402:     Set downKeystrokes = null;
 403:     if (comp instanceof Container)
 404:       downKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
 405: 
 406:     if (forwardKeystrokes.contains (eventKeystroke))
 407:       {
 408:         waitForKeyStroke = oppositeKeystroke;        
 409:         focusNextComponent (comp);
 410:         e.consume ();
 411:       }
 412:     else if (backwardKeystrokes.contains (eventKeystroke))
 413:       {
 414:         waitForKeyStroke = oppositeKeystroke;        
 415:         focusPreviousComponent (comp);
 416:         e.consume ();
 417:       }
 418:     else if (upKeystrokes.contains (eventKeystroke))
 419:       {
 420:         waitForKeyStroke = oppositeKeystroke;        
 421:         upFocusCycle (comp);
 422:         e.consume ();
 423:       }
 424:     else if (comp instanceof Container
 425:              && downKeystrokes.contains (eventKeystroke))
 426:       {
 427:         waitForKeyStroke = oppositeKeystroke;        
 428:         downFocusCycle ((Container) comp);
 429:         e.consume ();
 430:       }
 431:   }
 432: 
 433:   protected void enqueueKeyEvents (long after, Component untilFocused)
 434:   {
 435:     delayRequests.add (new EventDelayRequest (after, untilFocused));
 436:   }
 437: 
 438:   protected void dequeueKeyEvents (long after, Component untilFocused)
 439:   {
 440:     // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
 441: 
 442:     // Remove the KeyEvent with the oldest timestamp, which should be
 443:     // the first element in the SortedSet.
 444:     if (after < 0)
 445:       {
 446:         int size = delayRequests.size ();
 447:         if (size > 0)
 448:           delayRequests.remove (delayRequests.first ());
 449:       }
 450:     else
 451:       {
 452:         EventDelayRequest template = new EventDelayRequest (after, untilFocused);
 453:         if (delayRequests.contains (template))
 454:           {
 455:             EventDelayRequest actual = (EventDelayRequest) delayRequests.tailSet (template).first ();
 456:             delayRequests.remove (actual);
 457:             actual.dispatchEvents ();
 458:           }
 459:       }
 460:   }
 461: 
 462:   protected void discardKeyEvents (Component comp)
 463:   {
 464:     // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
 465: 
 466:     Iterator i = delayRequests.iterator ();
 467: 
 468:     while (i.hasNext ())
 469:       {
 470:         EventDelayRequest request = (EventDelayRequest) i.next ();
 471: 
 472:         if (request.focusedComp == comp
 473:             || (comp instanceof Container
 474:                 && ((Container) comp).isAncestorOf (request.focusedComp)))
 475:           request.discardEvents ();
 476:       }
 477:   }
 478: 
 479:   public void focusPreviousComponent (Component comp)
 480:   {
 481:     Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp;
 482:     Container focusCycleRoot = focusComp.getFocusCycleRootAncestor ();
 483:     FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy ();
 484: 
 485:     Component previous = policy.getComponentBefore (focusCycleRoot, focusComp);
 486:     if (previous != null)
 487:       previous.requestFocusInWindow ();
 488:   }
 489: 
 490:   public void focusNextComponent (Component comp)
 491:   {
 492:     Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp;
 493:     Container focusCycleRoot = focusComp.getFocusCycleRootAncestor ();
 494:     FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy ();
 495: 
 496:     Component next = policy.getComponentAfter (focusCycleRoot, focusComp);
 497:     if (next != null)
 498:       next.requestFocusInWindow ();
 499:   }
 500: 
 501:   public void upFocusCycle (Component comp)
 502:   {
 503:     Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp;
 504:     Container focusCycleRoot = focusComp.getFocusCycleRootAncestor ();
 505: 
 506:     if (focusCycleRoot instanceof Window)
 507:       {
 508:         FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy ();
 509:         Component defaultComponent = policy.getDefaultComponent (focusCycleRoot);
 510:         if (defaultComponent != null)
 511:           defaultComponent.requestFocusInWindow ();
 512:       }
 513:     else
 514:       {
 515:         Container parentFocusCycleRoot = focusCycleRoot.getFocusCycleRootAncestor ();
 516: 
 517:         focusCycleRoot.requestFocusInWindow ();
 518:         setGlobalCurrentFocusCycleRoot (parentFocusCycleRoot);
 519:       }
 520:   }
 521: 
 522:   public void downFocusCycle (Container cont)
 523:   {
 524:     if (cont == null)
 525:       return;
 526: 
 527:     if (cont.isFocusCycleRoot (cont))
 528:       {
 529:         FocusTraversalPolicy policy = cont.getFocusTraversalPolicy ();
 530:         Component defaultComponent = policy.getDefaultComponent (cont);
 531:         if (defaultComponent != null)
 532:           defaultComponent.requestFocusInWindow ();        
 533:         setGlobalCurrentFocusCycleRoot (cont);
 534:       }
 535:   }
 536: } // class DefaultKeyboardFocusManager