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