Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / awt / DefaultKeyboardFocusManager.java
blob9c94d8a220b65eb8ee8ca126338661eeed549b24
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)
9 any later version.
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
19 02111-1307 USA.
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.
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. */
39 package java.awt;
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;
51 // FIXME: finish documentation
52 public class DefaultKeyboardFocusManager extends KeyboardFocusManager
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.
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
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;
75 /**
76 * Construct a new EventDelayRequest.
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
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)
97 return -1;
98 else if (request.timestamp == timestamp)
99 return 0;
100 else
101 return 1;
104 public boolean equals (Object o)
106 if (!(o instanceof EventDelayRequest) || o == null)
107 return false;
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)
163 return false;
165 redispatchEvent(target, e);
166 return true;
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);
178 else
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);
203 else
204 setGlobalPermanentFocusOwner (null);
207 redispatchEvent(target, e);
209 return true;
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();
217 while (i.hasNext ())
219 KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next ();
220 if (dispatcher.dispatchKeyEvent ((KeyEvent) e))
221 return true;
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);
231 if (e.isConsumed ())
232 return true;
234 if (enqueueKeyEvent ((KeyEvent) e))
235 // This event was enqueued for dispatch at a later time.
236 return true;
237 else
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);
244 return false;
247 private boolean enqueueKeyEvent (KeyEvent e)
249 Iterator i = delayRequests.iterator ();
250 boolean oneEnqueued = false;
251 while (i.hasNext ())
253 EventDelayRequest request = (EventDelayRequest) i.next ();
254 if (e.getWhen () > request.timestamp)
256 request.enqueueEvent (e);
257 oneEnqueued = true;
260 return oneEnqueued;
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();
274 while (i.hasNext ())
276 KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next ();
277 if (processor.postProcessKeyEvent ((KeyEvent) e))
278 return true;
281 // The event hasn't been consumed yet. Check if it is an
282 // MenuShortcut.
283 if (postProcessKeyEvent (e))
284 return true;
286 // Always return true.
287 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 ();
304 if (menubar != null)
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
330 // modifier.
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 (),
340 modifiers));
341 // The event was dispatched.
342 return true;
350 return false;
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 (),
361 e.getModifiersEx (),
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);
374 e.consume ();
376 else if (backwardKeystrokes.contains (eventKeystroke))
378 focusPreviousComponent (comp);
379 e.consume ();
381 else if (upKeystrokes.contains (eventKeystroke))
383 upFocusCycle (comp);
384 e.consume ();
386 else if (comp instanceof Container
387 && downKeystrokes.contains (eventKeystroke))
389 downFocusCycle ((Container) comp);
390 e.consume ();
392 else if (forwardKeystrokes.contains (oppositeKeystroke)
393 || backwardKeystrokes.contains (oppositeKeystroke)
394 || upKeystrokes.contains (oppositeKeystroke)
395 || (comp instanceof Container &&
396 downKeystrokes.contains (oppositeKeystroke)))
397 e.consume ();
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.
411 if (after < 0)
413 int size = delayRequests.size ();
414 if (size > 0)
415 delayRequests.remove (delayRequests.first ());
417 else
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 ();
435 while (i.hasNext ())
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);
464 if (next != null)
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 ();
480 else
482 Container parentFocusCycleRoot = focusCycleRoot.getFocusCycleRootAncestor ();
484 focusCycleRoot.requestFocusInWindow ();
485 setGlobalCurrentFocusCycleRoot (parentFocusCycleRoot);
489 public void downFocusCycle (Container cont)
491 if (cont == null)
492 return;
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