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      */
EventDelayRequest(long timestamp, Component focusedComp)83     public EventDelayRequest (long timestamp, Component focusedComp)
84     {
85       this.timestamp = timestamp;
86       this.focusedComp = focusedComp;
87     }
88 
compareTo(Object o)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 
equals(Object o)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 
enqueueEvent(KeyEvent e)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 
dispatchEvents()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 
discardEvents()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 
DefaultKeyboardFocusManager()153   public DefaultKeyboardFocusManager ()
154   {
155   }
156 
dispatchEvent(AWTEvent e)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    */
handleFocusGained(FocusEvent fe)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    */
handleFocusLost(FocusEvent fe)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 
enqueueKeyEvent(KeyEvent e)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 
dispatchKeyEvent(KeyEvent e)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 
postProcessKeyEvent(KeyEvent e)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 
processKeyEvent(Component comp, KeyEvent e)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 
enqueueKeyEvents(long after, Component untilFocused)497   protected void enqueueKeyEvents (long after, Component untilFocused)
498   {
499     delayRequests.add (new EventDelayRequest (after, untilFocused));
500   }
501 
dequeueKeyEvents(long after, Component untilFocused)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 
discardKeyEvents(Component comp)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 
focusPreviousComponent(Component comp)543   public void focusPreviousComponent (Component comp)
544   {
545     if (comp != null)
546       comp.transferFocusBackward();
547   }
548 
focusNextComponent(Component comp)549   public void focusNextComponent (Component comp)
550   {
551     if (comp != null)
552       comp.transferFocus();
553   }
554 
upFocusCycle(Component comp)555   public void upFocusCycle (Component comp)
556   {
557     if (comp != null)
558       comp.transferFocusUpCycle();
559   }
560 
downFocusCycle(Container cont)561   public void downFocusCycle (Container cont)
562   {
563     if (cont != null)
564       cont.transferFocusDownCycle();
565   }
566 } // class DefaultKeyboardFocusManager
567