001 /* DefaultKeyboardFocusManager.java --
002 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt;
040
041 import java.awt.event.ActionEvent;
042 import java.awt.event.FocusEvent;
043 import java.awt.event.KeyEvent;
044 import java.awt.event.WindowEvent;
045 import java.util.Iterator;
046 import java.util.LinkedList;
047 import java.util.Set;
048 import java.util.SortedSet;
049 import java.util.TreeSet;
050
051 // FIXME: finish documentation
052 public 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