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 */ EventDelayRequest(long timestamp, Component focusedComp)83 public EventDelayRequest (long timestamp, Component focusedComp) 84 { 85 this.timestamp = timestamp; 86 this.focusedComp = focusedComp; 87 } 88 compareTo(Object o)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 equals(Object o)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 enqueueEvent(KeyEvent e)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 dispatchEvents()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 discardEvents()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 DefaultKeyboardFocusManager()153 public DefaultKeyboardFocusManager () 154 { 155 } 156 dispatchEvent(AWTEvent e)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 */ handleFocusGained(FocusEvent fe)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 */ handleFocusLost(FocusEvent fe)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 enqueueKeyEvent(KeyEvent e)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 dispatchKeyEvent(KeyEvent e)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 postProcessKeyEvent(KeyEvent e)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 processKeyEvent(Component comp, KeyEvent e)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 enqueueKeyEvents(long after, Component untilFocused)497 protected void enqueueKeyEvents (long after, Component untilFocused) 498 { 499 delayRequests.add (new EventDelayRequest (after, untilFocused)); 500 } 501 dequeueKeyEvents(long after, Component untilFocused)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 discardKeyEvents(Component comp)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 focusPreviousComponent(Component comp)543 public void focusPreviousComponent (Component comp) 544 { 545 if (comp != null) 546 comp.transferFocusBackward(); 547 } 548 focusNextComponent(Component comp)549 public void focusNextComponent (Component comp) 550 { 551 if (comp != null) 552 comp.transferFocus(); 553 } 554 upFocusCycle(Component comp)555 public void upFocusCycle (Component comp) 556 { 557 if (comp != null) 558 comp.transferFocusUpCycle(); 559 } 560 downFocusCycle(Container cont)561 public void downFocusCycle (Container cont) 562 { 563 if (cont != null) 564 cont.transferFocusDownCycle(); 565 } 566 } // class DefaultKeyboardFocusManager 567