OSDN Git Service

Imported GNU Classpath 0.90
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / awt / DefaultKeyboardFocusManager.java
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      */
83     public EventDelayRequest (long timestamp, Component focusedComp)
84     {
85       this.timestamp = timestamp;
86       this.focusedComp = focusedComp;
87     }
88
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
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           setGlobalFocusedWindow (target);
167         else if (e.id != WindowEvent.WINDOW_LOST_FOCUS
168                  && e.id != WindowEvent.WINDOW_DEACTIVATED)
169           return false;
170
171         redispatchEvent(target, e);
172         return true;
173       }
174     else if (e instanceof FocusEvent)
175       {
176         Component target = (Component) e.getSource ();
177
178         if (e.id == FocusEvent.FOCUS_GAINED)
179           {
180             if (! (target instanceof Window))
181               {
182                 if (((FocusEvent) e).isTemporary ())
183                   setGlobalFocusOwner (target);
184                 else
185                   setGlobalPermanentFocusOwner (target);
186               }
187
188             // Keep track of this window's focus owner.
189
190             // Find the target Component's top-level ancestor.  target
191             // may be a window.
192             Container parent = target.getParent ();
193
194             while (parent != null
195                    && !(parent instanceof Window))
196               parent = parent.getParent ();
197
198             // If the parent is null and target is not a window, then target is an
199             // unanchored component and so we don't want to set the focus owner.
200             if (! (parent == null && ! (target instanceof Window)))
201               {
202                 Window toplevel = parent == null ?
203                   (Window) target : (Window) parent;
204
205                 Component focusOwner = getFocusOwner ();
206                 if (focusOwner != null
207                     && ! (focusOwner instanceof Window))
208                   toplevel.setFocusOwner (focusOwner);
209               }
210           }
211         else if (e.id == FocusEvent.FOCUS_LOST)
212           {
213             if (((FocusEvent) e).isTemporary ())
214               setGlobalFocusOwner (null);
215             else
216               setGlobalPermanentFocusOwner (null);
217           }
218
219         redispatchEvent(target, e);
220
221         return true;
222       }
223     else if (e instanceof KeyEvent)
224       {
225         // Loop through all registered KeyEventDispatchers, giving
226         // each a chance to handle this event.
227         Iterator i = getKeyEventDispatchers().iterator();
228
229         while (i.hasNext ())
230           {
231             KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next ();
232             if (dispatcher.dispatchKeyEvent ((KeyEvent) e))
233               return true;
234           }
235
236         // processKeyEvent checks if this event represents a focus
237         // traversal key stroke.
238         Component focusOwner = getGlobalPermanentFocusOwner ();
239
240         if (focusOwner != null)
241           processKeyEvent (focusOwner, (KeyEvent) e);
242
243         if (e.isConsumed ())
244           return true;
245
246         if (enqueueKeyEvent ((KeyEvent) e))
247           // This event was enqueued for dispatch at a later time.
248           return true;
249         else
250           // This event wasn't handled by any of the registered
251           // KeyEventDispatchers, and wasn't enqueued for dispatch
252           // later, so send it to the default dispatcher.
253           return dispatchKeyEvent ((KeyEvent) e);
254       }
255
256     return false;
257   }
258
259   private boolean enqueueKeyEvent (KeyEvent e)
260   {
261     Iterator i = delayRequests.iterator ();
262     boolean oneEnqueued = false;
263     while (i.hasNext ())
264       {
265         EventDelayRequest request = (EventDelayRequest) i.next ();
266         if (e.getWhen () > request.timestamp)
267           {
268             request.enqueueEvent (e);
269             oneEnqueued = true;
270           }
271       }
272     return oneEnqueued;
273   }
274
275   public boolean dispatchKeyEvent (KeyEvent e)
276   {
277     Component focusOwner = getGlobalPermanentFocusOwner ();
278
279     if (focusOwner != null)
280       redispatchEvent(focusOwner, e);
281
282     // Loop through all registered KeyEventPostProcessors, giving
283     // each a chance to process this event.
284     Iterator i = getKeyEventPostProcessors().iterator();
285
286     while (i.hasNext ())
287       {
288         KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next ();
289         if (processor.postProcessKeyEvent ((KeyEvent) e))
290           return true;
291       }
292
293     // The event hasn't been consumed yet.  Check if it is an
294     // MenuShortcut.
295     if (postProcessKeyEvent (e))
296       return true;
297
298     // Always return true.
299     return true;
300   }
301
302   public boolean postProcessKeyEvent (KeyEvent e)
303   {
304     // Check if this event represents a menu shortcut.
305
306     // MenuShortcuts are activated by Ctrl- KeyEvents, only on KEY_PRESSED.
307     int modifiers = e.getModifiersEx ();
308     if (e.getID() == KeyEvent.KEY_PRESSED
309         && (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0)
310       {
311         Window focusedWindow = getGlobalFocusedWindow ();
312         if (focusedWindow instanceof Frame)
313           {
314             MenuBar menubar = ((Frame) focusedWindow).getMenuBar ();
315
316             if (menubar != null)
317               {
318                 // If there's a menubar, loop through all menu items,
319                 // checking whether each one has a shortcut, and if
320                 // so, whether this key event should activate it.
321                 int numMenus = menubar.getMenuCount ();
322
323                 for (int i = 0; i < numMenus; i++)
324                   {
325                     Menu menu = menubar.getMenu (i);
326                     int numItems = menu.getItemCount ();
327
328                     for (int j = 0; j < numItems; j++)
329                       {
330                         MenuItem item = menu.getItem (j);
331                         MenuShortcut shortcut = item.getShortcut ();
332
333                         if (item.isEnabled() && shortcut != null)
334                           {
335                             // Dispatch a new ActionEvent if:
336                             //
337                             //     a) this is a Shift- KeyEvent, and the
338                             //        shortcut requires the Shift modifier
339                             //
340                             // or, b) this is not a Shift- KeyEvent, and the
341                             //        shortcut does not require the Shift
342                             //        modifier.
343                             if (shortcut.getKey () == e.getKeyCode ()
344                                 && ((shortcut.usesShiftModifier ()
345                                      && (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0)
346                                     || (! shortcut.usesShiftModifier ()
347                                         && (modifiers & KeyEvent.SHIFT_DOWN_MASK) == 0)))
348                               {
349                                 item.dispatchEvent (new ActionEvent (item,
350                                                                      ActionEvent.ACTION_PERFORMED,
351                                                                      item.getActionCommand (),
352                                                                      modifiers));
353                                 // The event was dispatched.
354                                 return true;
355                               }
356                           }
357                       }
358                   }
359               }
360           }
361       }
362     return false;
363   }
364
365   public void processKeyEvent (Component comp, KeyEvent e)
366   {
367     AWTKeyStroke eventKeystroke = AWTKeyStroke.getAWTKeyStrokeForEvent (e);
368     // For every focus traversal keystroke, we need to also consume
369     // the other two key event types for the same key (e.g. if
370     // KEY_PRESSED TAB is a focus traversal keystroke, we also need to
371     // consume KEY_RELEASED and KEY_TYPED TAB key events).
372     // consuming KEY_RELEASED is easy, because their keyCodes matches
373     // the KEY_PRESSED event. Consuming the intermediate KEY_TYPED is
374     // very difficult because their is no clean way that we can know
375     // which KEY_TYPED belongs to a focusTraversalKey and which not.
376     // To address this problem we swallow every KEY_TYPE between the
377     // KEY_PRESSED event that matches a focusTraversalKey and the
378     // corresponding KEY_RELEASED.
379     AWTKeyStroke oppositeKeystroke = AWTKeyStroke.getAWTKeyStroke (e.getKeyCode (),
380                                                                    e.getModifiersEx (),
381                                                                    !(e.id == KeyEvent.KEY_RELEASED));
382
383     // Here we check if we are currently waiting for a KEY_RELEASED and
384     // swallow all KeyEvents that are to be delivered in between. This
385     // should only be the KEY_TYPED events that correspond to the
386     // focusTraversalKey's KEY_PRESSED event
387     if (waitForKeyStroke != null)
388       {
389         if (eventKeystroke.equals(waitForKeyStroke))
390           // release this lock
391           waitForKeyStroke = null;
392
393         // as long as we are waiting for the KEY_RELEASED, we swallow every
394         // KeyEvent, including the KEY_RELEASED
395         e.consume();
396         return;
397       }
398
399     Set forwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
400     Set backwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
401     Set upKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
402     Set downKeystrokes = null;
403     if (comp instanceof Container)
404       downKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
405
406     if (forwardKeystrokes.contains (eventKeystroke))
407       {
408         waitForKeyStroke = oppositeKeystroke;        
409         focusNextComponent (comp);
410         e.consume ();
411       }
412     else if (backwardKeystrokes.contains (eventKeystroke))
413       {
414         waitForKeyStroke = oppositeKeystroke;        
415         focusPreviousComponent (comp);
416         e.consume ();
417       }
418     else if (upKeystrokes.contains (eventKeystroke))
419       {
420         waitForKeyStroke = oppositeKeystroke;        
421         upFocusCycle (comp);
422         e.consume ();
423       }
424     else if (comp instanceof Container
425              && downKeystrokes.contains (eventKeystroke))
426       {
427         waitForKeyStroke = oppositeKeystroke;        
428         downFocusCycle ((Container) comp);
429         e.consume ();
430       }
431   }
432
433   protected void enqueueKeyEvents (long after, Component untilFocused)
434   {
435     delayRequests.add (new EventDelayRequest (after, untilFocused));
436   }
437
438   protected void dequeueKeyEvents (long after, Component untilFocused)
439   {
440     // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
441
442     // Remove the KeyEvent with the oldest timestamp, which should be
443     // the first element in the SortedSet.
444     if (after < 0)
445       {
446         int size = delayRequests.size ();
447         if (size > 0)
448           delayRequests.remove (delayRequests.first ());
449       }
450     else
451       {
452         EventDelayRequest template = new EventDelayRequest (after, untilFocused);
453         if (delayRequests.contains (template))
454           {
455             EventDelayRequest actual = (EventDelayRequest) delayRequests.tailSet (template).first ();
456             delayRequests.remove (actual);
457             actual.dispatchEvents ();
458           }
459       }
460   }
461
462   protected void discardKeyEvents (Component comp)
463   {
464     // FIXME: need synchronization on delayRequests and enqueuedKeyEvents.
465
466     Iterator i = delayRequests.iterator ();
467
468     while (i.hasNext ())
469       {
470         EventDelayRequest request = (EventDelayRequest) i.next ();
471
472         if (request.focusedComp == comp
473             || (comp instanceof Container
474                 && ((Container) comp).isAncestorOf (request.focusedComp)))
475           request.discardEvents ();
476       }
477   }
478
479   public void focusPreviousComponent (Component comp)
480   {
481     if (comp != null)
482       comp.transferFocusBackward();
483   }
484
485   public void focusNextComponent (Component comp)
486   {
487     if (comp != null)
488       comp.transferFocus();
489   }
490
491   public void upFocusCycle (Component comp)
492   {
493     if (comp != null)
494       comp.transferFocusUpCycle();
495   }
496
497   public void downFocusCycle (Container cont)
498   {
499     if (cont != null)
500       cont.transferFocusDownCycle();
501   }
502 } // class DefaultKeyboardFocusManager