src/share/classes/java/awt/DefaultKeyboardFocusManager.java

Print this page

        

@@ -38,10 +38,11 @@
 import sun.util.logging.PlatformLogger;
 
 import sun.awt.AppContext;
 import sun.awt.SunToolkit;
 import sun.awt.CausedFocusEvent;
+import sun.awt.TimedWindowEvent;
 
 /**
  * The default KeyboardFocusManager for AWT applications. Focus traversal is
  * done in response to a Component's focus traversal keys, and using a
  * Container's FocusTraversalPolicy.

@@ -69,12 +70,12 @@
     private static final WeakReference<Component> NULL_COMPONENT_WR =
         new WeakReference<Component>(null);
     private WeakReference<Window> realOppositeWindowWR = NULL_WINDOW_WR;
     private WeakReference<Component> realOppositeComponentWR = NULL_COMPONENT_WR;
     private int inSendMessage;
-    private LinkedList enqueuedKeyEvents = new LinkedList(),
-        typeAheadMarkers = new LinkedList();
+    private LinkedList<KeyEvent> enqueuedKeyEvents = new LinkedList<KeyEvent>();
+    private LinkedList<TypeAheadMarker> typeAheadMarkers = new LinkedList<TypeAheadMarker>();
     private boolean consumeNextKeyTyped;
 
     private static class TypeAheadMarker {
         long after;
         Component untilFocused;

@@ -257,10 +258,35 @@
             }
         }
         return se.dispatched;
     }
 
+    /*
+     * Checks if the focus window event follows key events waiting in the type-ahead
+     * queue (if any). This may happen when a user types ahead in the window, the client
+     * listeners hang EDT for a while, and the user switches b/w toplevels. In that
+     * case the focus window events may be dispatched before the type-ahead events
+     * get handled. This may lead to wrong focus behavior and in order to avoid it,
+     * the focus window events are reposted to the end of the event queue. See 6981400.
+     */
+    private boolean repostIfFollowsKeyEvents(WindowEvent e) {
+        if (!(e instanceof TimedWindowEvent)) {
+            return false;
+        }
+        TimedWindowEvent we = (TimedWindowEvent)e;
+        long time = we.getWhen();
+        synchronized (this) {
+            for (KeyEvent ke: enqueuedKeyEvents) {
+                if (time >= ke.getWhen()) {
+                    SunToolkit.postEvent(AppContext.getAppContext(), new SequencedEvent(e));
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
     /**
      * This method is called by the AWT event dispatcher requesting that the
      * current KeyboardFocusManager dispatch the specified event on its behalf.
      * DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents
      * related to focus, and all KeyEvents. These events are dispatched based

@@ -275,10 +301,14 @@
      */
     public boolean dispatchEvent(AWTEvent e) {
         if (focusLog.isLoggable(PlatformLogger.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) focusLog.fine("" + e);
         switch (e.getID()) {
             case WindowEvent.WINDOW_GAINED_FOCUS: {
+                if (repostIfFollowsKeyEvents((WindowEvent)e)) {
+                    break;
+                }
+                
                 WindowEvent we = (WindowEvent)e;
                 Window oldFocusedWindow = getGlobalFocusedWindow();
                 Window newFocusedWindow = we.getWindow();
                 if (newFocusedWindow == oldFocusedWindow) {
                     break;

@@ -634,10 +664,14 @@
                 we.setSource(currentActiveWindow);
                 return typeAheadAssertions(currentActiveWindow, we);
             }
 
             case WindowEvent.WINDOW_LOST_FOCUS: {
+                if (repostIfFollowsKeyEvents((WindowEvent)e)) {
+                    break;
+                }
+                
                 WindowEvent we = (WindowEvent)e;
                 Window currentFocusedWindow = getGlobalFocusedWindow();
                 Window losingFocusWindow = we.getWindow();
                 Window activeWindow = getGlobalActiveWindow();
                 Window oppositeWindow = we.getOppositeWindow();

@@ -813,14 +847,13 @@
         KeyEvent ke;
         do {
             ke = null;
             synchronized (this) {
                 if (enqueuedKeyEvents.size() != 0) {
-                    ke = (KeyEvent)enqueuedKeyEvents.getFirst();
+                    ke = enqueuedKeyEvents.getFirst();
                     if (typeAheadMarkers.size() != 0) {
-                        TypeAheadMarker marker = (TypeAheadMarker)
-                            typeAheadMarkers.getFirst();
+                        TypeAheadMarker marker = typeAheadMarkers.getFirst();
                         // Fixed 5064013: may appears that the events have the same time
                         // if (ke.getWhen() >= marker.after) {
                         // The fix is rolled out.
 
                         if (ke.getWhen() > marker.after) {

@@ -845,13 +878,13 @@
     void dumpMarkers() {
         if (focusLog.isLoggable(PlatformLogger.FINEST)) {
             focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());
             synchronized (this) {
                 if (typeAheadMarkers.size() != 0) {
-                    Iterator iter = typeAheadMarkers.iterator();
+                    Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
                     while (iter.hasNext()) {
-                        TypeAheadMarker marker = (TypeAheadMarker)iter.next();
+                        TypeAheadMarker marker = iter.next();
                         focusLog.finest("    {0}", marker);
                     }
                 }
             }
         }

@@ -869,12 +902,11 @@
             case KeyEvent.KEY_PRESSED:
             case KeyEvent.KEY_RELEASED: {
                 KeyEvent ke = (KeyEvent)e;
                 synchronized (this) {
                     if (e.isPosted && typeAheadMarkers.size() != 0) {
-                        TypeAheadMarker marker = (TypeAheadMarker)
-                            typeAheadMarkers.getFirst();
+                        TypeAheadMarker marker = typeAheadMarkers.getFirst();
                         // Fixed 5064013: may appears that the events have the same time
                         // if (ke.getWhen() >= marker.after) {
                         // The fix is rolled out.
 
                         if (ke.getWhen() > marker.after) {

@@ -903,16 +935,14 @@
                 // not be generated for these additional requests, we
                 // need to clear those markers too.
                 synchronized (this) {
                     boolean found = false;
                     if (hasMarker(target)) {
-                        for (Iterator iter = typeAheadMarkers.iterator();
+                        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
                              iter.hasNext(); )
                         {
-                            if (((TypeAheadMarker)iter.next()).untilFocused ==
-                                target)
-                            {
+                            if (iter.next().untilFocused == target) {
                                 found = true;
                             } else if (found) {
                                 break;
                             }
                             iter.remove();

@@ -943,12 +973,12 @@
      * Returns true if there are some marker associated with component <code>comp</code>
      * in a markers' queue
      * @since 1.5
      */
     private boolean hasMarker(Component comp) {
-        for (Iterator iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
-            if (((TypeAheadMarker)iter.next()).untilFocused == comp) {
+        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
+            if (iter.next().untilFocused == comp) {
                 return true;
             }
         }
         return false;
     }

@@ -970,15 +1000,14 @@
         }
         if (ke.getSource() == null) {
             return true;
         }
 
-        // Explicitly set the current event and most recent timestamp here in
-        // addition to the call in Component.dispatchEventImpl. Because
-        // KeyEvents can be delivered in response to a FOCUS_GAINED event, the
-        // current timestamp may be incorrect. We need to set it here so that
-        // KeyEventDispatchers will use the correct time.
+        // Explicitly set the key event timestamp here (not in Component.dispatchEventImpl):
+        // - A key event is anyway passed to this method which starts its actual dispatching.
+        // - If a key event is put to the type ahead queue, its time stamp should not be registered
+        //   until its dispatching actually starts (by this method).
         EventQueue.setCurrentEventAndMostRecentTime(ke);
 
         /**
          * Fix for 4495473.
          * This fix allows to correctly dispatch events when native

@@ -1162,14 +1191,14 @@
         focusLog.finer("Enqueue at {0} for {1}",
                        after, untilFocused);
 
         int insertionIndex = 0,
             i = typeAheadMarkers.size();
-        ListIterator iter = typeAheadMarkers.listIterator(i);
+        ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator(i);
 
         for (; i > 0; i--) {
-            TypeAheadMarker marker = (TypeAheadMarker)iter.previous();
+            TypeAheadMarker marker = iter.previous();
             if (marker.after <= after) {
                 insertionIndex = i;
                 break;
             }
         }

@@ -1201,25 +1230,25 @@
 
         focusLog.finer("Dequeue at {0} for {1}",
                        after, untilFocused);
 
         TypeAheadMarker marker;
-        ListIterator iter = typeAheadMarkers.listIterator
+        ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator
             ((after >= 0) ? typeAheadMarkers.size() : 0);
 
         if (after < 0) {
             while (iter.hasNext()) {
-                marker = (TypeAheadMarker)iter.next();
+                marker = iter.next();
                 if (marker.untilFocused == untilFocused)
                 {
                     iter.remove();
                     return;
                 }
             }
         } else {
             while (iter.hasPrevious()) {
-                marker = (TypeAheadMarker)iter.previous();
+                marker = iter.previous();
                 if (marker.untilFocused == untilFocused &&
                     marker.after == after)
                 {
                     iter.remove();
                     return;

@@ -1243,12 +1272,12 @@
             return;
         }
 
         long start = -1;
 
-        for (Iterator iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
-            TypeAheadMarker marker = (TypeAheadMarker)iter.next();
+        for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
+            TypeAheadMarker marker = iter.next();
             Component toTest = marker.untilFocused;
             boolean match = (toTest == comp);
             while (!match && toTest != null && !(toTest instanceof Window)) {
                 toTest = toTest.getParent();
                 match = (toTest == comp);

@@ -1275,12 +1304,12 @@
     private void purgeStampedEvents(long start, long end) {
         if (start < 0) {
             return;
         }
 
-        for (Iterator iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
-            KeyEvent ke = (KeyEvent)iter.next();
+        for (Iterator<KeyEvent> iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
+            KeyEvent ke = iter.next();
             long time = ke.getWhen();
 
             if (start < time && (end < 0 || time <= end)) {
                 iter.remove();
             }