[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[albireo-dev] Forcing AWT permanent focus loss

I've committed code to handle the problem described in:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=60967

As described in comment #6, it is due to temporary focus loss when an embedded AWT frame is deactivated. Since it is embedded with other controls in a single SWT shell, a permanent focus loss would be more appropriate, allowing Swing validation code to be activated.

The new code forces a permanent focus loss when another control in the same shell is activated. This is a little tricky in general due to lack of API on the AWT side. Where possible, I use KeyboardFocusManager.clearGlobalFocusOwner() to make it happen. But if the previously active embedded AWT frame is no longer the AWT global focus owner (a rare but possible case), I fall back to a hack of disabling and re-enabling the current focus owner in the frame. This fallback is visually noticeable; that's why we shouldn't use it all of the time.

When the frame for which we forced a permanent focus loss is re-activated, Component.requestFocus() is called to manually reset focus to the correct owner.

This feature is optional and can be controlled through SwingControl.setAWTPermanentFocusLossForced().

### Eclipse Workspace Patch 1.0
#P org.eclipse.albireo.core
Index: src/org/eclipse/albireo/core/AwtEnvironment.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/AwtEnvironment.java,v
retrieving revision 1.26
diff -u -r1.26 AwtEnvironment.java
--- src/org/eclipse/albireo/core/AwtEnvironment.java	1 Aug 2008 20:31:36 -0000	1.26
+++ src/org/eclipse/albireo/core/AwtEnvironment.java	4 Aug 2008 20:21:42 -0000
@@ -33,6 +33,7 @@
 import org.eclipse.albireo.internal.AwtDialogListener;
 import org.eclipse.albireo.internal.FocusDebugging;
 import org.eclipse.albireo.internal.FocusHandler;
+import org.eclipse.albireo.internal.GlobalFocusHandler;
 import org.eclipse.albireo.internal.Platform;
 import org.eclipse.albireo.internal.SwtInputBlocker;
 import org.eclipse.swt.SWT;
@@ -46,7 +47,6 @@
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Widget;


 /**
@@ -144,6 +144,7 @@

     private final Display display;
     private final AwtDialogListener dialogListener;
+    private final GlobalFocusHandler globalFocusHandler;


     // Private constructor - clients use getInstance() to obtain instances
@@ -201,7 +202,7 @@
         // Dismiss AWT popups when SWT menus are shown
         initSwingPopupsDismissal();

-        initializeEventFilter();
+        globalFocusHandler = new GlobalFocusHandler(display);
     }

     void dispose() {
@@ -210,7 +211,7 @@
             popupParent.setVisible(false);
             popupParent.dispose();
         }
-        disposeEventFilter();
+        globalFocusHandler.dispose();
     }

     // ======================= Look&Feel initialization =======================
@@ -543,148 +544,10 @@
         }
     }

-    // ----------------------- Event Listening ------------------------------------------
+    // ----------------------- Focus Handling ------------------------------------------

-    private SwtEventFilter swtEventFilter;
-    private final List listeners = new ArrayList();
-
-    public int getCurrentSwtTraversal() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.currentSwtTraversal;
-    }
-
-    public Widget getActiveWidget() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.activeWidget;
-    }
-
-    public Shell getActiveShell() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.activeShell;
-    }
-
-    public SwingControl getActiveEmbedded() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.activeEmbedded;
-    }
-
-    public int getLastSwtTraversal() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.lastSwtTraversal;
-    }
-
-    public Widget getLastActiveWidget() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.lastActiveWidget;
-    }
-
-    public SwingControl getLastActiveEmbedded() {
-        assert Display.getCurrent() != null; // On SWT event thread
-        return swtEventFilter.lastActiveEmbedded;
-    }
-
-    public void addEventFilter(Listener filter) {
-        listeners.add(filter);
-    }
-
-    public void removeEventFilter(Listener filter) {
-        listeners.remove(filter);
-    }
-
-    protected void fireEvent(Event event) {
-        for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
-            Listener listener = (Listener)iterator.next();
-            listener.handleEvent(event);
-        }
-    }
-
-    protected void initializeEventFilter() {
-        swtEventFilter = new SwtEventFilter();
-        display.addFilter(SWT.Activate, swtEventFilter);
-        display.addFilter(SWT.Deactivate, swtEventFilter);
-        display.addFilter(SWT.Traverse, swtEventFilter);
-    }
-
-    protected void disposeEventFilter() {
-        display.removeFilter(SWT.Activate, swtEventFilter);
-        display.removeFilter(SWT.Deactivate, swtEventFilter);
-        display.removeFilter(SWT.Traverse, swtEventFilter);
-    }
-
-    protected class SwtEventFilter implements Listener {
-
-        int currentSwtTraversal = SWT.TRAVERSE_NONE;
-        Widget activeWidget;
-        Shell activeShell;
-        SwingControl activeEmbedded;
-
-        int lastSwtTraversal = SWT.TRAVERSE_NONE;
-        Widget lastActiveWidget = null;
-        SwingControl lastActiveEmbedded = null;
-
-        public SwtEventFilter() {
-            activeWidget = display.getFocusControl();
-            activeShell = display.getActiveShell();
-            if ((activeWidget instanceof SwingControl) && ((activeWidget.getStyle() & SWT.EMBEDDED) != 0)) {
-                activeEmbedded = (SwingControl)activeWidget;
-            }
-        }
-
-        public void handleEvent(Event event) {
-            Widget widget = event.widget;
-            switch (event.type) {
-            case SWT.Activate:
-                activeWidget = widget;
-
-                // Track the currently active shell. This is more reliable than
-                // depending on Display.getActiveShell() which sometimes returns an
-                // inactive shell.
-                if (widget instanceof Shell) {
-                    activeShell = (Shell)widget;
-                }
-
-                if ((widget instanceof SwingControl) && ((widget.getStyle() & SWT.EMBEDDED) != 0)) {
-                    activeEmbedded = (SwingControl)widget;
-                }
-
-                if (currentSwtTraversal != SWT.TRAVERSE_NONE) {
-                    lastSwtTraversal = currentSwtTraversal;
-                }
-                currentSwtTraversal = SWT.TRAVERSE_NONE;
-                break;
-
-            case SWT.Deactivate:
-                if (activeWidget != null) {
-                    lastActiveWidget = activeWidget;
-                    activeWidget = null;
-                }
-
-                if (event.widget instanceof Shell) {
-                    activeShell = null;
-                }
-
-                if ((widget instanceof SwingControl) && ((widget.getStyle() & SWT.EMBEDDED) != 0)) {
-                    if (activeEmbedded != null) {
-                        lastActiveEmbedded = activeEmbedded;
-                        activeEmbedded = null;
-                    }
-                }
-
-                break;
-
-            case SWT.Traverse:
-                currentSwtTraversal = event.detail;
-
-                break;
-            }
-
-            // Propagate to any listeners
-            fireEvent(event);
-
-
-        }
-
+    protected GlobalFocusHandler getGlobalFocusHandler() {
+        return globalFocusHandler;
     }

-    // ========================================================================
 }
Index: src/org/eclipse/albireo/core/SwingControl.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/SwingControl.java,v
retrieving revision 1.88
diff -u -r1.88 SwingControl.java
--- src/org/eclipse/albireo/core/SwingControl.java	24 Jun 2008 19:22:15 -0000	1.88
+++ src/org/eclipse/albireo/core/SwingControl.java	4 Aug 2008 20:21:42 -0000
@@ -40,6 +40,7 @@
 import org.eclipse.albireo.internal.CleanResizeListener;
 import org.eclipse.albireo.internal.ComponentDebugging;
 import org.eclipse.albireo.internal.FocusHandler;
+import org.eclipse.albireo.internal.GlobalFocusHandler;
 import org.eclipse.albireo.internal.Platform;
 import org.eclipse.albireo.internal.RunnableWithResult;
 import org.eclipse.albireo.internal.SwtPopupHandler;
@@ -1077,7 +1078,7 @@
      * should be enabled or not.
      * <p>
      * For this setting to be useful, a background colour should have been
-       * set that approximately matches the window's contents.
+     * set that approximately matches the window's contents.
      * <p>
      * By default, this setting is enabled on Windows with JDK 1.5 or older, and
      * disabled otherwise.
@@ -1254,12 +1255,14 @@
     // ============================= Focus Management =============================
     private FocusHandler focusHandler;
     private boolean isSwtTabOrderExtended = true;
-
+    private boolean isAWTPermanentFocusLossForced = true;
+
     protected void initializeFocusManagement() {
         assert frame != null;
         assert Display.getCurrent() != null;     // On SWT event thread

-        focusHandler = new FocusHandler(this, borderlessChild, frame);
+        GlobalFocusHandler handler = AwtEnvironment.getInstance(display).getGlobalFocusHandler();
+        focusHandler = new FocusHandler(this, handler, borderlessChild, frame);
     }


@@ -1296,7 +1299,38 @@
         return isSwtTabOrderExtended;
     }

-    /**
+    /**
+     * Returns whether a permanent focus lost event is forced on a SwingControl when focus moves to
+     * another SWT component within the same shell. See {@link #setAWTPermanentFocusLossForced(boolean)}
+     * for more information.
+     *
+     * @return boolean
+     */
+    public boolean isAWTPermanentFocusLossForced() {
+        return isAWTPermanentFocusLossForced;
+    }
+
+    /**
+     * Controls whether a permanent focus lost event is forced on a SwingControl when focus moves to
+     * another SWT component within the same shell. Normally, when an AWT frame loses focus to another
+     * window, it only receives a temporary focus lost event. This can cause unexpected results when
+     * the AWT window is embedded in a SWT shell and should really act more like a composite widget
+     * within that shell. If this property is set to <code>true</code> (the default), then
+     * permanent focus loss is synthesized.
+     * <p>
+     * For more information on permanent/temporary focus loss,
+     * see the <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/FocusSpec.html";>AWT
+     * Focus Subsystem</a> spec. For an example of the type of problem solved by keeping this property
+     * <code>true</code>, see <a href="http://bugs.eclipse.org/60967";>bug 60967</a>.
+     *
+     * @param isAWTPermanentFocusLossForced - <code>true</code> to enable the forcing of permanent
+     * focus loss. <code>false</code> to disable it.
+     */
+    public void setAWTPermanentFocusLossForced(boolean isAWTPermanentFocusLossForced) {
+        this.isAWTPermanentFocusLossForced = isAWTPermanentFocusLossForced;
+    }
+
+   /**
      * {@inheritDoc}
      * <p>
      * Overridden to return false and prevent any focus change if the embedded Swing component is
@@ -1530,7 +1564,7 @@
     // ============================= Keystroke Management =============================

     Set consumedKeystrokes = new HashSet();
-
+
     /**
      * Initializes keystroke management for this control.
      */
Index: src/org/eclipse/albireo/internal/FocusHandler.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/internal/FocusHandler.java,v
retrieving revision 1.23
diff -u -r1.23 FocusHandler.java
--- src/org/eclipse/albireo/internal/FocusHandler.java	1 Aug 2008 20:31:36 -0000	1.23
+++ src/org/eclipse/albireo/internal/FocusHandler.java	4 Aug 2008 20:21:42 -0000
@@ -27,7 +27,6 @@
 import javax.swing.text.Caret;
 import javax.swing.text.JTextComponent;

-import org.eclipse.albireo.core.AwtEnvironment;
 import org.eclipse.albireo.core.SwingControl;
 import org.eclipse.albireo.core.ThreadingHandler;
 import org.eclipse.swt.SWT;
@@ -62,13 +61,14 @@
     private final Composite borderless; // the Control corresponding to the frame
     private final SwingControl swingControl; // either borderless or its parent
     private final Display display;
+    private final GlobalFocusHandler globalHandler;
     private boolean pendingTraverseOut = false;
     private int pendingTraverseOutSeqNum = 0;
     private int currentTraverseOutSeqNum = 0;
     private int extraTabCount = 0;
     private boolean isFocusedSwt;
     private boolean pendingDeactivate = false;
-    private AwtEnvironment env;
+

     // Listeners
     private KeyEventDispatcher keyEventDispatcher = new AwtKeyDispatcher();
@@ -76,7 +76,8 @@
     private FocusListener swtFocusListener = new SwtFocusListener();
     private Listener swtEventFilter = new SwtEventFilter();

-    public FocusHandler(final SwingControl swingControl, final Composite borderless, final Frame frame) {
+    public FocusHandler(final SwingControl swingControl, GlobalFocusHandler globalHandler, final Composite borderless, final Frame frame) {
+        this.globalHandler = globalHandler;
         assert Display.getCurrent() != null;     // On SWT event thread

         if (verboseFocusEvents)
@@ -86,11 +87,10 @@
         this.borderless = borderless;
         this.frame = frame;
         display = swingControl.getDisplay();
-        env = AwtEnvironment.getInstance(display);

         getSynthesizeMethod(frame.getClass());

-        env.addEventFilter(swtEventFilter);
+        globalHandler.addEventFilter(swtEventFilter);

         frame.addWindowFocusListener(awtWindowFocusListener);
         KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(keyEventDispatcher);
@@ -100,7 +100,7 @@
     }

     public void dispose() {
-        env.removeEventFilter(swtEventFilter);
+        globalHandler.removeEventFilter(swtEventFilter);
         frame.removeWindowFocusListener(awtWindowFocusListener);
         KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(keyEventDispatcher);
         borderless.removeFocusListener(swtFocusListener);
@@ -230,7 +230,7 @@
         if (verboseTraverseOut) {
             trace("SWT: traversing, control=" + focusControl);
         }
-        SwingControl activeBorderless = env.getActiveEmbedded();
+        SwingControl activeBorderless = globalHandler.getActiveEmbedded();
         if ((focusControl == null) && (activeBorderless != null)) {
             focusControl = activeBorderless;
             if (verboseTraverseOut) {
@@ -278,7 +278,7 @@
             // the SwingControl to be skipped while tabbing. Try to fix it here by resetting
             // the forceFocus result back to true when SWT events indicate we really do have
             // focus.
-            if (!result && (env.getActiveWidget() == borderless) && isFocusedSwt) {
+            if (!result && (globalHandler.getActiveWidget() == borderless) && isFocusedSwt) {
                 // Force focus should have returned true
                 result = true;
                 if (verboseTraverseOut) {
@@ -416,8 +416,8 @@
     public void activateEmbeddedFrame() {
         assert Display.getCurrent() != null;

-        Shell activeShell = env.getActiveShell();
-        SwingControl activeBorderless = env.getActiveEmbedded();
+        Shell activeShell = globalHandler.getActiveShell();
+        SwingControl activeBorderless = globalHandler.getActiveEmbedded();

         if (!borderless.isDisposed() &&

@@ -457,6 +457,9 @@
     }

     protected void doActivation(int swtTraversal) {
+        if (swingControl.isDisposed()) {
+            return;
+        }
         if (!swingControl.isFocusControl()) {
             // We've lost focus, don't activate the underlying AWT window
             if (verboseFocusEvents) {
@@ -491,12 +494,12 @@
     protected class SwtEventFilter implements Listener {

         public void handleEvent(Event event) {
-            // Handle activation of the SWT.EMBEDDED composite. Track the currently active one.
+            // Handle activation of the SWT.EMBEDDED composite. Track the currently active one.
             if (event.widget == borderless) {
                 switch (event.type) {
                 case SWT.Activate:
                     // The lastSwtTraversal may change before it is used. Save its value for the asyncExecs
-                    final int swtTraversal = env.getLastSwtTraversal();
+                    final int swtTraversal = globalHandler.getLastSwtTraversal();

                     // We use asyncExec to defer the activation and focus setting in the underlying AWT frame.
                     // This allows proper handling of the case where focus is briefly
Index: src/org/eclipse/albireo/internal/GlobalFocusHandler.java
===================================================================
RCS file: src/org/eclipse/albireo/internal/GlobalFocusHandler.java
diff -N src/org/eclipse/albireo/internal/GlobalFocusHandler.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/albireo/internal/GlobalFocusHandler.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,261 @@
+package org.eclipse.albireo.internal;
+
+import java.awt.Component;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.KeyboardFocusManager;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.albireo.core.SwingControl;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+// Handler for global focus events. Maintains global state information like current and
+// previous active widgets. When actions based on global state need to be done, they are
+// implemented in this class. See also the FocusHandler class for additional handling based
+// on a single SwingControl.
+public class GlobalFocusHandler {
+    private static final String SAVED_FOCUS_OWNER_KEY = "org.eclipse.albireo.savedFocusOwner";
+    private final Display display;
+    private final SwtEventFilter swtEventFilter;
+    private final List listeners = new ArrayList();
+    private static final boolean verboseFocusEvents = FocusHandler.verboseFocusEvents;
+
+    public GlobalFocusHandler(Display display) {
+        this.display = display;
+        swtEventFilter = new SwtEventFilter();
+        display.addFilter(SWT.Activate, swtEventFilter);
+        display.addFilter(SWT.Deactivate, swtEventFilter);
+        display.addFilter(SWT.Traverse, swtEventFilter);
+    }
+
+    public int getCurrentSwtTraversal() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.currentSwtTraversal;
+    }
+
+    public Widget getActiveWidget() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.activeWidget;
+    }
+
+    public Shell getActiveShell() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.activeShell;
+    }
+
+    public SwingControl getActiveEmbedded() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.activeEmbedded;
+    }
+
+    public int getLastSwtTraversal() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.lastSwtTraversal;
+    }
+
+    public Widget getLastActiveWidget() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.lastActiveWidget;
+    }
+
+    public SwingControl getLastActiveEmbedded() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.lastActiveEmbedded;
+    }
+
+    public boolean getLastActiveFocusCleared() {
+        assert Display.getCurrent() != null; // On SWT event thread
+        return swtEventFilter.lastActiveFocusCleared;
+    }
+
+    public void setLastActiveFocusCleared(boolean lastActiveFocusCleared) {
+        assert Display.getCurrent() != null; // On SWT event thread
+        swtEventFilter.lastActiveFocusCleared = lastActiveFocusCleared;
+    }
+
+    public void addEventFilter(Listener filter) {
+        listeners.add(filter);
+    }
+
+    public void removeEventFilter(Listener filter) {
+        listeners.remove(filter);
+    }
+
+    protected void fireEvent(Event event) {
+        for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+            Listener listener = (Listener)iterator.next();
+            listener.handleEvent(event);
+        }
+    }
+
+    public void dispose() {
+        display.removeFilter(SWT.Activate, swtEventFilter);
+        display.removeFilter(SWT.Deactivate, swtEventFilter);
+        display.removeFilter(SWT.Traverse, swtEventFilter);
+    }
+
+    protected boolean isBorderlessSwingControl(Widget widget) {
+        return (widget instanceof SwingControl) && ((widget.getStyle() & SWT.EMBEDDED) != 0);
+    }
+
+    protected void clearFocusOwner(SwingControl swingControl) {
+        assert Display.getCurrent() != null; // On SWT event thread
+
+        if (!swingControl.isAWTPermanentFocusLossForced()) {
+            return;
+        }
+
+        // It appears safe to call getFocusOwner on SWT thread
+        final Component owner = ((Frame)swingControl.getAWTHierarchyRoot()).getFocusOwner();
+        if (owner != null) {
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    // Clear the AWT focus owner so that a permanent focus lost event is
+                    // generated. Where possible, we use the KeyboardFocusManager, but
+                    // it has no method to clear the focus owner within a particular frame,
+                    // if that frame is no longer active. In that case, we use a hack of
+                    // disabling and re-enabling the window's focus owner. The hack has
+                    // the drawback of a brief visual movement of the cursor (or other
+                    // focus indicator), so it is good to avoid it whenever possible, as
+                    // we do here.
+                    KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+                    if (owner == kfm.getFocusOwner()) {
+                        if (verboseFocusEvents) {
+                            trace("clearing focus thru kfm: " + owner);
+                        }
+                        kfm.clearGlobalFocusOwner();
+                    } else {
+                        if (verboseFocusEvents) {
+                            trace("clearing focus thru hack: " + owner);
+                        }
+                        owner.setEnabled(false);
+                        owner.setEnabled(true);
+                    }
+                }
+            });
+            swingControl.setData(SAVED_FOCUS_OWNER_KEY, owner);
+        }
+    }
+
+    protected void restoreFocusOwner(SwingControl swingControl) {
+        assert Display.getCurrent() != null; // On SWT event thread
+
+        final Component savedOwner = (Component)swingControl.getData(SAVED_FOCUS_OWNER_KEY);
+        if (savedOwner != null) {
+            swingControl.setData(SAVED_FOCUS_OWNER_KEY, null);
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    // Restore focus to any AWT component that lost focus due to
+                    // clearFocusOwner().
+                    if (verboseFocusEvents) {
+                        trace("restoring focus: " + savedOwner);
+                    }
+                    savedOwner.requestFocus();
+                }
+            });
+        }
+    }
+
+    private void trace(String msg) {
+        System.err.println(header() + ' ' + msg);
+    }
+    private String header() {
+        return "@" + System.currentTimeMillis() + " " + System.identityHashCode(this);
+    }
+
+    protected class SwtEventFilter implements Listener {
+
+        int currentSwtTraversal = SWT.TRAVERSE_NONE;
+        Widget activeWidget;
+        Shell activeShell;
+        SwingControl activeEmbedded;
+
+        int lastSwtTraversal = SWT.TRAVERSE_NONE;
+        Widget lastActiveWidget = null;
+        SwingControl lastActiveEmbedded = null;
+        boolean lastActiveFocusCleared = false;
+
+        public SwtEventFilter() {
+            activeWidget = display.getFocusControl();
+            activeShell = display.getActiveShell();
+            if (isBorderlessSwingControl(activeWidget)) {
+                activeEmbedded = (SwingControl)activeWidget;
+            }
+        }
+
+        public void handleEvent(Event event) {
+            Widget widget = event.widget;
+            switch (event.type) {
+            case SWT.Activate:
+                activeWidget = widget;
+
+                // Track the currently active shell. This is more reliable than
+                // depending on Display.getActiveShell() which sometimes returns an
+                // inactive shell.
+                if (widget instanceof Shell) {
+                    activeShell = (Shell)widget;
+                }
+
+                if (currentSwtTraversal != SWT.TRAVERSE_NONE) {
+                    lastSwtTraversal = currentSwtTraversal;
+                }
+                currentSwtTraversal = SWT.TRAVERSE_NONE;
+
+                // If we have moved from a SwingControl to another control in the same
+                // shell, clear its current focus owner so that a permanent focus
+                // lost event is generated.
+                if ((lastActiveEmbedded != null) && (lastActiveEmbedded != event.widget) &&
+                        !lastActiveFocusCleared &&
+                        (event.widget instanceof Control) &&  // (need a getShell() method)
+                        (lastActiveEmbedded.getShell() == ((Control)event.widget).getShell())) {
+                    clearFocusOwner(lastActiveEmbedded);
+                    lastActiveFocusCleared = true;
+                }
+
+                // If we have moved to a SwingControl, restore the current focus owner
+                // that was cleared above during a previous Activate event.
+                if (isBorderlessSwingControl(widget)) {
+                    activeEmbedded = (SwingControl)widget;
+                    restoreFocusOwner(activeEmbedded);
+                }
+                break;
+
+            case SWT.Deactivate:
+                if (activeWidget != null) {
+                    lastActiveWidget = activeWidget;
+                    activeWidget = null;
+                }
+
+                if (event.widget instanceof Shell) {
+                    activeShell = null;
+                }
+
+                if (isBorderlessSwingControl(widget)) {
+                    if (activeEmbedded != null) {
+                        lastActiveEmbedded = activeEmbedded;
+                        lastActiveFocusCleared = false;
+                        activeEmbedded = null;
+                    }
+                }
+
+                break;
+
+            case SWT.Traverse:
+                currentSwtTraversal = event.detail;
+
+                break;
+            }
+
+            // Propagate to any listeners
+            fireEvent(event);
+        }
+    }
+}
#P org.eclipse.albireo.examples.plugin
Index: plugin.xml
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.examples.plugin/plugin.xml,v
retrieving revision 1.20
diff -u -r1.20 plugin.xml
--- plugin.xml	14 May 2008 23:18:34 -0000	1.20
+++ plugin.xml	4 Aug 2008 20:21:43 -0000
@@ -99,6 +99,12 @@
             name="SWT Bug #58308">
       </view>
       <view
+            class="org.eclipse.albireo.examples.plugin.views.SWTBug60967"
+            id="org.eclipse.albireo.examples.plugin.60967"
+            category="org.eclipse.albireo"
+            name="SWT Bug #60967">
+      </view>
+      <view
             category="org.eclipse.albireo"
             class="org.eclipse.albireo.examples.plugin.views.RelayoutExampleView"
             id=" org.eclipse.albireo.examples.plugin.relayoutExample "
Index: src/org/eclipse/albireo/examples/plugin/views/SWTBug60967.java
===================================================================
RCS file: src/org/eclipse/albireo/examples/plugin/views/SWTBug60967.java
diff -N src/org/eclipse/albireo/examples/plugin/views/SWTBug60967.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/albireo/examples/plugin/views/SWTBug60967.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,87 @@
+package org.eclipse.albireo.examples.plugin.views;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+import org.eclipse.albireo.core.SwingControl;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * This view allows to check against the SWT bug #58308.
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=58308
+ */
+public class SWTBug60967 extends ViewPart {
+
+    public static String ID = "org.eclipse.albireo.examples.plugin.60967"; //$NON-NLS-1$
+
+    SwingControl swingControl;
+    JComboBox jComboBox1;
+
+    public void createPartControl(final Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        composite.setLayout(new FillLayout());
+
+        swingControl = new SwingControl(composite, SWT.NONE) {
+
+            protected JComponent createSwingComponent() {
+                JComponent container = new JPanel();
+                container.setLayout(new BorderLayout());
+                jComboBox1 = new JComboBox();
+                jComboBox1.setEditable(true);
+                jComboBox1.addFocusListener(new FocusListener() {
+                    public void focusGained(FocusEvent e) {
+                        System.out.println("gained");
+                    }
+                    public void focusLost(FocusEvent e) {
+                        System.out.println("lost");
+                    }
+                });
+                container.add(jComboBox1, BorderLayout.NORTH);
+                JButton button = new JButton("Press");
+                container.add(button, BorderLayout.SOUTH);
+                button.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        System.out.println("selection=" + jComboBox1.getSelectedItem());
+                    }
+                });
+                return container;
+            }
+
+            public Composite getLayoutAncestor() {
+                return getParent();
+            }
+        };
+        // Uncomment this line, to disable the Albireo workaround and thus demonstrate the bug
+        // swingControl.setAWTPermanentFocusLossForced(false);
+
+        Button swtButton = new Button(composite, SWT.PUSH);
+        swtButton.setText("SWT Button");
+
+        swtButton.addListener(SWT.Selection, new Listener() {
+            public void handleEvent(Event event) {
+                // If the workaround is successful, any changes to the combobox contents
+                // will be seen here when the SWT button is pushed.
+                System.out.println("selected=" + jComboBox1.getSelectedItem());
+            }
+        });
+    }
+
+    public void setFocus() {
+        swingControl.setFocus();
+    }
+
+}