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:           {
 167:             setGlobalFocusedWindow (target);
 168:             FocusTraversalPolicy p = target.getFocusTraversalPolicy();
 169:             Component toFocus = p.getInitialComponent(target);
 170:             if (toFocus != null)
 171:               toFocus.requestFocusInWindow();
 172:           }
 173:         else if (e.id != WindowEvent.WINDOW_LOST_FOCUS
 174:                  && e.id != WindowEvent.WINDOW_DEACTIVATED)
 175:           return false;
 176: 
 177:         redispatchEvent(target, e);
 178:         return true;
 179:       }
 180:     else if (e instanceof FocusEvent)
 181:       {
 182:         FocusEvent fe = (FocusEvent) e;
 183:         Component target = fe.getComponent ();
 184: 
 185:         boolean retval = false;
 186:         if (e.id == FocusEvent.FOCUS_GAINED)
 187:           {
 188:             retval = handleFocusGained(fe);
 189:           }
 190:         else if (e.id == FocusEvent.FOCUS_LOST)
 191:           {
 192:             retval = handleFocusLost(fe);
 193:           }
 194:         return true;
 195:       }
 196:     else if (e instanceof KeyEvent)
 197:       {
 198:         // Loop through all registered KeyEventDispatchers, giving
 199:         // each a chance to handle this event.
 200:         Iterator i = getKeyEventDispatchers().iterator();
 201: 
 202:         while (i.hasNext ())
 203:           {
 204:             KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next ();
 205:             if (dispatcher.dispatchKeyEvent ((KeyEvent) e))
 206:               return true;
 207:           }
 208: 
 209:         // processKeyEvent checks if this event represents a focus
 210:         // traversal key stroke.
 211:         Component focusOwner = getGlobalPermanentFocusOwner ();
 212: 
 213:         if (focusOwner != null)
 214:           processKeyEvent (focusOwner, (KeyEvent) e);
 215: 
 216:         if (e.isConsumed ())
 217:           return true;
 218: 
 219:         if (enqueueKeyEvent ((KeyEvent) e))
 220:           // This event was enqueued for dispatch at a later time.
 221:           return true;
 222:         else
 223:           // This event wasn't handled by any of the registered
 224:           // KeyEventDispatchers, and wasn't enqueued for dispatch
 225:           // later, so send it to the default dispatcher.
 226:           return dispatchKeyEvent ((KeyEvent) e);
 227:       }
 228: 
 229:     return false;
 230:   }
 231: 
 232:   /**
 233:    * Handles FOCUS_GAINED events in {@link #dispatchEvent(AWTEvent)}.
 234:    *
 235:    * @param fe the focus event
 236:    */
 237:   private boolean handleFocusGained(FocusEvent fe)
 238:   {
 239:     Component target = fe.getComponent ();
 240: 
 241:     // If old focus owner != new focus owner, notify old focus
 242:     // owner that it has lost focus.
 243:     Component oldFocusOwner = getGlobalFocusOwner();
 244:     if (oldFocusOwner != null && oldFocusOwner != target)
 245:       {
 246:         FocusEvent lost = new FocusEvent(oldFocusOwner,
 247:                                          FocusEvent.FOCUS_LOST,
 248:                                          fe.isTemporary(), target);
 249:         oldFocusOwner.dispatchEvent(lost);
 250:       }
 251: 
 252:      setGlobalFocusOwner (target);
 253:      if (target != getGlobalFocusOwner())
 254:        {
 255:          // Focus transfer was rejected, like when the target is not
 256:          // focusable.
 257:          dequeueKeyEvents(-1, target);
 258:          // FIXME: Restore focus somehow.
 259:        }
 260:      else
 261:        {
 262:          if (! fe.isTemporary())
 263:            {
 264:              setGlobalPermanentFocusOwner (target);
 265:              if (target != getGlobalPermanentFocusOwner())
 266:                {
 267:                  // Focus transfer was rejected, like when the target is not
 268:                  // focusable.
 269:                  dequeueKeyEvents(-1, target);
 270:                  // FIXME: Restore focus somehow.
 271:                }
 272:              else
 273:                {
 274:                  redispatchEvent(target, fe);
 275:                }
 276:            }
 277:        }
 278: 
 279:      return true;
 280:   }
 281: 
 282:   /**
 283:    * Handles FOCUS_LOST events for {@link #dispatchEvent(AWTEvent)}.
 284:    *
 285:    * @param fe the focus event
 286:    *
 287:    * @return if the event has been handled
 288:    */
 289:   private boolean handleFocusLost(FocusEvent fe)
 290:   {
 291:     Component currentFocus = getGlobalFocusOwner();
 292:     if (currentFocus != fe.getOppositeComponent())
 293:       {
 294:         setGlobalFocusOwner(null);
 295:         if (getGlobalFocusOwner() != null)
 296:           {
 297:             // TODO: Is this possible? If so, then we should try to restore
 298:             // the focus.
 299:           }
 300:         else
 301:           {
 302:             if (! fe.isTemporary())
 303:               {
 304:                 setGlobalPermanentFocusOwner(null);
 305:                 if (getGlobalPermanentFocusOwner() != null)
 306:                   {
 307:                     // TODO: Is this possible? If so, then we should try to
 308:                     // restore the focus.
 309:                   }
 310:                 else
 311:                   {
 312:                     fe.setSource(currentFocus);
 313:                     redispatchEvent(currentFocus, fe);
 314:                   }
 315:               }
 316:           }
 317:       }
 318:     return true;
 319:   }
 320: 
 321:   private boolean enqueueKeyEvent (KeyEvent e)
 322:   {
 323:     Iterator i = delayRequests.iterator ();
 324:     boolean oneEnqueued = false;
 325:     while (i.hasNext ())
 326:       {
 327:         EventDelayRequest request = (EventDelayRequest) i.next ();
 328:         if (e.getWhen () > request.timestamp)
 329:           {
 330:             request.enqueueEvent (e);
 331:             oneEnqueued = true;
 332:           }
 333:       }
 334:     return oneEnqueued;
 335:   }
 336: 
 337:   public boolean dispatchKeyEvent (KeyEvent e)
 338:   {
 339:     Component focusOwner = getFocusOwner();
 340:     if (focusOwner == null)
 341:       focusOwner = getFocusedWindow();
 342: 
 343:     if (focusOwner != null)
 344:       redispatchEvent(focusOwner, e);
 345: 
 346:     // Loop through all registered KeyEventPostProcessors, giving
 347:     // each a chance to process this event.
 348:     Iterator i = getKeyEventPostProcessors().iterator();
 349: 
 350:     while (i.hasNext ())
 351:       {
 352:         KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next ();
 353:         if (processor.postProcessKeyEvent (e))
 354:           return true;
 355:       }
 356: 
 357:     // The event hasn't been consumed yet.  Check if it is an
 358:     // MenuShortcut.
 359:     if (postProcessKeyEvent (e))
 360:       return true;
 361: 
 362:     // Always return true.
 363:     return true;
 364:   }
 365: 
 366:   public boolean postProcessKeyEvent (KeyEvent e)
 367:   {
 368:     // Check if this event represents a menu shortcut.
 369: 
 370:     // MenuShortcuts are activated by Ctrl- KeyEvents, only on KEY_PRESSED.
 371:     int modifiers = e.getModifiersEx ();
 372:     if (e.getID() == KeyEvent.KEY_PRESSED
 373:         && (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0)
 374:       {
 375:         Window focusedWindow = getGlobalFocusedWindow ();
 376:         if (focusedWindow instanceof Frame)
 377:           {
 378:             MenuBar menubar = ((Frame) focusedWindow).getMenuBar ();
 379: 
 380:             if (menubar != null)
 381:               {
 382:                 // If there's a menubar, loop through all menu items,
 383:                 // checking whether each one has a shortcut, and if
 384:                 // so, whether this key event should activate it.
 385:                 int numMenus = menubar.getMenuCount ();
 386: 
 387:                 for (int i = 0; i < numMenus; i++)
 388:                   {
 389:                     Menu menu = menubar.getMenu (i);
 390:                     int numItems = menu.getItemCount ();
 391: 
 392:                     for (int j = 0; j < numItems; j++)
 393:                       {
 394:                         MenuItem item = menu.getItem (j);
 395:                         MenuShortcut shortcut = item.getShortcut ();
 396: 
 397:                         if (item.isEnabled() && shortcut != null)
 398:                           {
 399:                             // Dispatch a new ActionEvent if:
 400:                             //
 401:                             //     a) this is a Shift- KeyEvent, and the
 402:                             //        shortcut requires the Shift modifier
 403:                             //
 404:                             // or, b) this is not a Shift- KeyEvent, and the
 405:                             //        shortcut does not require the Shift
 406:                             //        modifier.
 407:                             if (shortcut.getKey () == e.getKeyCode ()
 408:                                 && ((shortcut.usesShiftModifier ()
 409:                                      && (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0)
 410:                                     || (! shortcut.usesShiftModifier ()
 411:                                         && (modifiers & KeyEvent.SHIFT_DOWN_MASK) == 0)))
 412:                               {
 413:                                 item.dispatchEvent (new ActionEvent (item,
 414:                                                                      ActionEvent.ACTION_PERFORMED,
 415:                                                                      item.getActionCommand (),
 416:                                                                      modifiers));
 417:                                 // The event was dispatched.
 418:                                 return true;
 419:                               }
 420:                           }
 421:                       }
 422:                   }
 423:               }
 424:           }
 425:       }
 426:     return false;
 427:   }
 428: 
 429:   public void processKeyEvent (Component comp, KeyEvent e)
 430:   {
 431:     AWTKeyStroke eventKeystroke = AWTKeyStroke.getAWTKeyStrokeForEvent (e);
 432:     // For every focus traversal keystroke, we need to also consume
 433:     // the other two key event types for the same key (e.g. if
 434:     // KEY_PRESSED TAB is a focus traversal keystroke, we also need to
 435:     // consume KEY_RELEASED and KEY_TYPED TAB key events).
 436:     // consuming KEY_RELEASED is easy, because their keyCodes matches
 437:     // the KEY_PRESSED event. Consuming the intermediate KEY_TYPED is
 438:     // very difficult because their is no clean way that we can know
 439:     // which KEY_TYPED belongs to a focusTraversalKey and which not.
 440:     // To address this problem we swallow every KEY_TYPE between the
 441:     // KEY_PRESSED event that matches a focusTraversalKey and the
 442:     // corresponding KEY_RELEASED.
 443:     AWTKeyStroke oppositeKeystroke = AWTKeyStroke.getAWTKeyStroke (e.getKeyCode (),
 444:                                                                    e.getModifiersEx (),
 445:                                                                    !(e.id == KeyEvent.KEY_RELEASED));
 446: 
 447:     // Here we check if we are currently waiting for a KEY_RELEASED and
 448:     // swallow all KeyEvents that are to be delivered in between. This
 449:     // should only be the KEY_TYPED events that correspond to the
 450:     // focusTraversalKey's KEY_PRESSED event
 451:     if (waitForKeyStroke != null)
 452:       {
 453:         if (eventKeystroke.equals(waitForKeyStroke))
 454:           // release this lock
 455:           waitForKeyStroke = null;
 456: 
 457:         // as long as we are waiting for the KEY_RELEASED, we swallow every
 458:         // KeyEvent, including the KEY_RELEASED
 459:         e.consume();
 460:         return;
 461:       }
 462: 
 463:     Set forwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
 464:     Set backwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
 465:     Set upKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
 466:     Set downKeystrokes = null;
 467:     if (comp instanceof Container)
 468:       downKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
 469: 
 470:     if (forwardKeystrokes.contains (eventKeystroke))
 471:       {
 472:         waitForKeyStroke = oppositeKeystroke;
 473:         focusNextComponent (comp);
 474:         e.consume ();
 475:       }
 476:     else if (backwardKeystrokes.contains (eventKeystroke))
 477:       {
 478:         waitForKeyStroke = oppositeKeystroke;
 479:         focusPreviousComponent (comp);
 480:         e.consume ();
 481:       }
 482:     else if (upKeystrokes.contains (eventKeystroke))
 483:       {
 484:         waitForKeyStroke = oppositeKeystroke;
 485:         upFocusCycle (comp);
 486:         e.consume ();
 487:       }
 488:     else if (comp instanceof Container
 489:              && downKeystrokes.contains (eventKeystroke))
 490:       {
 491:         waitForKeyStroke = oppositeKeystroke;
 492:         downFocusCycle ((Container) comp);
 493:         e.consume ();
 494:       }
 495:   }
 496: 
 497:   protected void enqueueKeyEvents (long after, Component untilFocused)
 498:   {
 499:     delayRequests.add (new EventDelayRequest (after, untilFocused));
 500:   }
 501: 
 502:   protected void dequeueKeyEvents (long after, Component untilFocused)
 503:   {
 504:     // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
 505: 
 506:     // Remove the KeyEvent with the oldest timestamp, which should be
 507:     // the first element in the SortedSet.
 508:     if (after < 0)
 509:       {
 510:         int size = delayRequests.size ();
 511:         if (size > 0)
 512:           delayRequests.remove (delayRequests.first ());
 513:       }
 514:     else
 515:       {
 516:         EventDelayRequest template = new EventDelayRequest (after, untilFocused);
 517:         if (delayRequests.contains (template))
 518:           {
 519:             EventDelayRequest actual = (EventDelayRequest) delayRequests.tailSet (template).first ();
 520:             delayRequests.remove (actual);
 521:             actual.dispatchEvents ();
 522:           }
 523:       }
 524:   }
 525: 
 526:   protected void discardKeyEvents (Component comp)
 527:   {
 528:     // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
 529: 
 530:     Iterator i = delayRequests.iterator ();
 531: 
 532:     while (i.hasNext ())
 533:       {
 534:         EventDelayRequest request = (EventDelayRequest) i.next ();
 535: 
 536:         if (request.focusedComp == comp
 537:             || (comp instanceof Container
 538:                 && ((Container) comp).isAncestorOf (request.focusedComp)))
 539:           request.discardEvents ();
 540:       }
 541:   }
 542: 
 543:   public void focusPreviousComponent (Component comp)
 544:   {
 545:     if (comp != null)
 546:       comp.transferFocusBackward();
 547:   }
 548: 
 549:   public void focusNextComponent (Component comp)
 550:   {
 551:     if (comp != null)
 552:       comp.transferFocus();
 553:   }
 554: 
 555:   public void upFocusCycle (Component comp)
 556:   {
 557:     if (comp != null)
 558:       comp.transferFocusUpCycle();
 559:   }
 560: 
 561:   public void downFocusCycle (Container cont)
 562:   {
 563:     if (cont != null)
 564:       cont.transferFocusDownCycle();
 565:   }
 566: } // class DefaultKeyboardFocusManager