1 /* DefaultKeyboardFocusManager.java --
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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., 59 Temple Place, Suite 330, Boston, MA
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
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. */
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
;
48 import java
.util
.SortedSet
;
49 import java
.util
.TreeSet
;
51 // FIXME: finish documentation
52 public class DefaultKeyboardFocusManager
extends KeyboardFocusManager
55 * This class models a request to delay the dispatch of events that
56 * arrive after a certain time, until a certain component becomes
59 private class EventDelayRequest
implements Comparable
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 ();
66 /** An event timestamp. All events that arrive after this time
67 should be queued in the {@link #enqueuedKeyEvents} {@link
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
;
76 * Construct a new EventDelayRequest.
78 * @param timestamp events that arrive after this time will be
80 * @param focusedComp the Component that needs to receive focus
81 * before events are dispatched
83 public EventDelayRequest (long timestamp
, Component focusedComp
)
85 this.timestamp
= timestamp
;
86 this.focusedComp
= focusedComp
;
89 public int compareTo (Object o
)
91 if (!(o
instanceof EventDelayRequest
))
92 throw new ClassCastException ();
94 EventDelayRequest request
= (EventDelayRequest
) o
;
96 if (request
.timestamp
< timestamp
)
98 else if (request
.timestamp
== timestamp
)
104 public boolean equals (Object o
)
106 if (!(o
instanceof EventDelayRequest
) || o
== null)
109 EventDelayRequest request
= (EventDelayRequest
) o
;
111 return (request
.timestamp
== timestamp
112 && request
.focusedComp
== focusedComp
);
115 public void enqueueEvent (KeyEvent e
)
117 KeyEvent last
= (KeyEvent
) enqueuedKeyEvents
.getLast ();
118 if (last
!= null && e
.getWhen () < last
.getWhen ())
119 throw new RuntimeException ("KeyEvents enqueued out-of-order");
121 if (e
.getWhen () <= timestamp
)
122 throw new RuntimeException ("KeyEvents enqueued before starting timestamp");
124 enqueuedKeyEvents
.add (e
);
127 public void dispatchEvents ()
129 int size
= enqueuedKeyEvents
.size ();
130 for (int i
= 0; i
< size
; i
++)
132 KeyEvent e
= (KeyEvent
) enqueuedKeyEvents
.remove (0);
133 dispatchKeyEvent (e
);
137 public void discardEvents ()
139 enqueuedKeyEvents
.clear ();
143 /** The {@link java.util.SortedSet} of current {@link
144 #EventDelayRequest}s. */
145 private SortedSet delayRequests
= new TreeSet ();
147 public DefaultKeyboardFocusManager ()
151 public boolean dispatchEvent (AWTEvent e
)
153 if (e
instanceof WindowEvent
)
155 Window target
= (Window
) e
.getSource ();
157 if (e
.id
== WindowEvent
.WINDOW_ACTIVATED
)
158 setGlobalActiveWindow (target
);
159 else if (e
.id
== WindowEvent
.WINDOW_GAINED_FOCUS
)
160 setGlobalFocusedWindow (target
);
161 else if (e
.id
!= WindowEvent
.WINDOW_LOST_FOCUS
162 && e
.id
!= WindowEvent
.WINDOW_DEACTIVATED
)
165 redispatchEvent(target
, e
);
168 else if (e
instanceof FocusEvent
)
170 Component target
= (Component
) e
.getSource ();
172 if (e
.id
== FocusEvent
.FOCUS_GAINED
)
174 if (! (target
instanceof Window
))
176 if (((FocusEvent
) e
).isTemporary ())
177 setGlobalFocusOwner (target
);
179 setGlobalPermanentFocusOwner (target
);
182 // Keep track of this window's focus owner.
184 // Find the target Component's top-level ancestor.
185 Container parent
= target
.getParent ();
187 while (parent
!= null
188 && !(parent
instanceof Window
))
189 parent
= parent
.getParent ();
191 Window toplevel
= parent
== null ?
192 (Window
) target
: (Window
) parent
;
194 Component focusOwner
= getFocusOwner ();
195 if (focusOwner
!= null
196 && ! (focusOwner
instanceof Window
))
197 toplevel
.setFocusOwner (focusOwner
);
199 else if (e
.id
== FocusEvent
.FOCUS_LOST
)
201 if (((FocusEvent
) e
).isTemporary ())
202 setGlobalFocusOwner (null);
204 setGlobalPermanentFocusOwner (null);
207 redispatchEvent(target
, e
);
211 else if (e
instanceof KeyEvent
)
213 // Loop through all registered KeyEventDispatchers, giving
214 // each a chance to handle this event.
215 Iterator i
= getKeyEventDispatchers().iterator();
219 KeyEventDispatcher dispatcher
= (KeyEventDispatcher
) i
.next ();
220 if (dispatcher
.dispatchKeyEvent ((KeyEvent
) e
))
224 // processKeyEvent checks if this event represents a focus
225 // traversal key stroke.
226 Component focusOwner
= getGlobalPermanentFocusOwner ();
228 if (focusOwner
!= null)
229 processKeyEvent (focusOwner
, (KeyEvent
) e
);
234 if (enqueueKeyEvent ((KeyEvent
) e
))
235 // This event was enqueued for dispatch at a later time.
238 // This event wasn't handled by any of the registered
239 // KeyEventDispatchers, and wasn't enqueued for dispatch
240 // later, so send it to the default dispatcher.
241 return dispatchKeyEvent ((KeyEvent
) e
);
247 private boolean enqueueKeyEvent (KeyEvent e
)
249 Iterator i
= delayRequests
.iterator ();
250 boolean oneEnqueued
= false;
253 EventDelayRequest request
= (EventDelayRequest
) i
.next ();
254 if (e
.getWhen () > request
.timestamp
)
256 request
.enqueueEvent (e
);
263 public boolean dispatchKeyEvent (KeyEvent e
)
265 Component focusOwner
= getGlobalPermanentFocusOwner ();
267 if (focusOwner
!= null)
268 redispatchEvent(focusOwner
, e
);
270 // Loop through all registered KeyEventPostProcessors, giving
271 // each a chance to process this event.
272 Iterator i
= getKeyEventPostProcessors().iterator();
276 KeyEventPostProcessor processor
= (KeyEventPostProcessor
) i
.next ();
277 if (processor
.postProcessKeyEvent ((KeyEvent
) e
))
281 // The event hasn't been consumed yet. Check if it is an
283 if (postProcessKeyEvent (e
))
286 // Always return true.
290 public boolean postProcessKeyEvent (KeyEvent e
)
292 // Check if this event represents a menu shortcut.
294 // MenuShortcuts are activated by Ctrl- KeyEvents, only on KEY_PRESSED.
295 int modifiers
= e
.getModifiersEx ();
296 if (e
.getID() == KeyEvent
.KEY_PRESSED
297 && (modifiers
& KeyEvent
.CTRL_DOWN_MASK
) != 0)
299 Window focusedWindow
= getGlobalFocusedWindow ();
300 if (focusedWindow
instanceof Frame
)
302 MenuBar menubar
= ((Frame
) focusedWindow
).getMenuBar ();
306 // If there's a menubar, loop through all menu items,
307 // checking whether each one has a shortcut, and if
308 // so, whether this key event should activate it.
309 int numMenus
= menubar
.getMenuCount ();
311 for (int i
= 0; i
< numMenus
; i
++)
313 Menu menu
= menubar
.getMenu (i
);
314 int numItems
= menu
.getItemCount ();
316 for (int j
= 0; j
< numItems
; j
++)
318 MenuItem item
= menu
.getItem (j
);
319 MenuShortcut shortcut
= item
.getShortcut ();
321 if (item
.isEnabled() && shortcut
!= null)
323 // Dispatch a new ActionEvent if:
325 // a) this is a Shift- KeyEvent, and the
326 // shortcut requires the Shift modifier
328 // or, b) this is not a Shift- KeyEvent, and the
329 // shortcut does not require the Shift
331 if (shortcut
.getKey () == e
.getKeyCode ()
332 && ((shortcut
.usesShiftModifier ()
333 && (modifiers
& KeyEvent
.SHIFT_DOWN_MASK
) != 0)
334 || (! shortcut
.usesShiftModifier ()
335 && (modifiers
& KeyEvent
.SHIFT_DOWN_MASK
) == 0)))
337 item
.dispatchEvent (new ActionEvent (item
,
338 ActionEvent
.ACTION_PERFORMED
,
339 item
.getActionCommand (),
341 // The event was dispatched.
353 public void processKeyEvent (Component comp
, KeyEvent e
)
355 AWTKeyStroke eventKeystroke
= AWTKeyStroke
.getAWTKeyStrokeForEvent (e
);
356 // For every focus traversal keystroke, we need to also consume
357 // the other two key event types for the same key (e.g. if
358 // KEY_PRESSED TAB is a focus traversal keystroke, we also need to
359 // consume KEY_RELEASED and KEY_TYPED TAB key events).
360 AWTKeyStroke oppositeKeystroke
= AWTKeyStroke
.getAWTKeyStroke (e
.getKeyCode (),
362 !(e
.id
== KeyEvent
.KEY_RELEASED
));
364 Set forwardKeystrokes
= comp
.getFocusTraversalKeys (KeyboardFocusManager
.FORWARD_TRAVERSAL_KEYS
);
365 Set backwardKeystrokes
= comp
.getFocusTraversalKeys (KeyboardFocusManager
.BACKWARD_TRAVERSAL_KEYS
);
366 Set upKeystrokes
= comp
.getFocusTraversalKeys (KeyboardFocusManager
.UP_CYCLE_TRAVERSAL_KEYS
);
367 Set downKeystrokes
= null;
368 if (comp
instanceof Container
)
369 downKeystrokes
= comp
.getFocusTraversalKeys (KeyboardFocusManager
.DOWN_CYCLE_TRAVERSAL_KEYS
);
371 if (forwardKeystrokes
.contains (eventKeystroke
))
373 focusNextComponent (comp
);
376 else if (backwardKeystrokes
.contains (eventKeystroke
))
378 focusPreviousComponent (comp
);
381 else if (upKeystrokes
.contains (eventKeystroke
))
386 else if (comp
instanceof Container
387 && downKeystrokes
.contains (eventKeystroke
))
389 downFocusCycle ((Container
) comp
);
392 else if (forwardKeystrokes
.contains (oppositeKeystroke
)
393 || backwardKeystrokes
.contains (oppositeKeystroke
)
394 || upKeystrokes
.contains (oppositeKeystroke
)
395 || (comp
instanceof Container
&&
396 downKeystrokes
.contains (oppositeKeystroke
)))
400 protected void enqueueKeyEvents (long after
, Component untilFocused
)
402 delayRequests
.add (new EventDelayRequest (after
, untilFocused
));
405 protected void dequeueKeyEvents (long after
, Component untilFocused
)
407 // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
409 // Remove the KeyEvent with the oldest timestamp, which should be
410 // the first element in the SortedSet.
413 int size
= delayRequests
.size ();
415 delayRequests
.remove (delayRequests
.first ());
419 EventDelayRequest template
= new EventDelayRequest (after
, untilFocused
);
420 if (delayRequests
.contains (template
))
422 EventDelayRequest actual
= (EventDelayRequest
) delayRequests
.tailSet (template
).first ();
423 delayRequests
.remove (actual
);
424 actual
.dispatchEvents ();
429 protected void discardKeyEvents (Component comp
)
431 // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
433 Iterator i
= delayRequests
.iterator ();
437 EventDelayRequest request
= (EventDelayRequest
) i
.next ();
439 if (request
.focusedComp
== comp
440 || (comp
instanceof Container
441 && ((Container
) comp
).isAncestorOf (request
.focusedComp
)))
442 request
.discardEvents ();
446 public void focusPreviousComponent (Component comp
)
448 Component focusComp
= (comp
== null) ?
getGlobalFocusOwner () : comp
;
449 Container focusCycleRoot
= focusComp
.getFocusCycleRootAncestor ();
450 FocusTraversalPolicy policy
= focusCycleRoot
.getFocusTraversalPolicy ();
452 Component previous
= policy
.getComponentBefore (focusCycleRoot
, focusComp
);
453 if (previous
!= null)
454 previous
.requestFocusInWindow ();
457 public void focusNextComponent (Component comp
)
459 Component focusComp
= (comp
== null) ?
getGlobalFocusOwner () : comp
;
460 Container focusCycleRoot
= focusComp
.getFocusCycleRootAncestor ();
461 FocusTraversalPolicy policy
= focusCycleRoot
.getFocusTraversalPolicy ();
463 Component next
= policy
.getComponentAfter (focusCycleRoot
, focusComp
);
465 next
.requestFocusInWindow ();
468 public void upFocusCycle (Component comp
)
470 Component focusComp
= (comp
== null) ?
getGlobalFocusOwner () : comp
;
471 Container focusCycleRoot
= focusComp
.getFocusCycleRootAncestor ();
473 if (focusCycleRoot
instanceof Window
)
475 FocusTraversalPolicy policy
= focusCycleRoot
.getFocusTraversalPolicy ();
476 Component defaultComponent
= policy
.getDefaultComponent (focusCycleRoot
);
477 if (defaultComponent
!= null)
478 defaultComponent
.requestFocusInWindow ();
482 Container parentFocusCycleRoot
= focusCycleRoot
.getFocusCycleRootAncestor ();
484 focusCycleRoot
.requestFocusInWindow ();
485 setGlobalCurrentFocusCycleRoot (parentFocusCycleRoot
);
489 public void downFocusCycle (Container cont
)
494 if (cont
.isFocusCycleRoot (cont
))
496 FocusTraversalPolicy policy
= cont
.getFocusTraversalPolicy ();
497 Component defaultComponent
= policy
.getDefaultComponent (cont
);
498 if (defaultComponent
!= null)
499 defaultComponent
.requestFocusInWindow ();
500 setGlobalCurrentFocusCycleRoot (cont
);
503 } // class DefaultKeyboardFocusManager