001/* DefaultKeyboardFocusManager.java -- 002 Copyright (C) 2002, 2004 Free Software Foundation, Inc. 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.ActionEvent; 042import java.awt.event.FocusEvent; 043import java.awt.event.KeyEvent; 044import java.awt.event.WindowEvent; 045import java.util.Iterator; 046import java.util.LinkedList; 047import java.util.Set; 048import java.util.SortedSet; 049import java.util.TreeSet; 050 051// FIXME: finish documentation 052public class DefaultKeyboardFocusManager extends KeyboardFocusManager 053{ 054 /** 055 * This class models a request to delay the dispatch of events that 056 * arrive after a certain time, until a certain component becomes 057 * the focus owner. 058 */ 059 private class EventDelayRequest implements Comparable 060 { 061 /** A {@link java.util.List} of {@link java.awt.event.KeyEvent}s 062 that are being delayed, pending this request's {@link 063 Component} receiving the keyboard focus. */ 064 private LinkedList enqueuedKeyEvents = new LinkedList (); 065 066 /** An event timestamp. All events that arrive after this time 067 should be queued in the {@link #enqueuedKeyEvents} {@link 068 java.util.List}. */ 069 public long timestamp; 070 /** When this {@link Component} becomes focused, all events 071 between this EventDelayRequest and the next one in will be 072 dispatched from {@link #enqueuedKeyEvents}. */ 073 public Component focusedComp; 074 075 /** 076 * Construct a new EventDelayRequest. 077 * 078 * @param timestamp events that arrive after this time will be 079 * delayed 080 * @param focusedComp the Component that needs to receive focus 081 * before events are dispatched 082 */ 083 public EventDelayRequest (long timestamp, Component focusedComp) 084 { 085 this.timestamp = timestamp; 086 this.focusedComp = focusedComp; 087 } 088 089 public int compareTo (Object o) 090 { 091 if (!(o instanceof EventDelayRequest)) 092 throw new ClassCastException (); 093 094 EventDelayRequest request = (EventDelayRequest) o; 095 096 if (request.timestamp < timestamp) 097 return -1; 098 else if (request.timestamp == timestamp) 099 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