--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007 SAS Institute.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * SAS Institute - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.utils.ui.internal.awt;\r
+\r
+import java.awt.EventQueue;\r
+\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.ControlAdapter;\r
+import org.eclipse.swt.events.ControlEvent;\r
+import org.eclipse.swt.events.DisposeEvent;\r
+import org.eclipse.swt.events.DisposeListener;\r
+import org.eclipse.swt.events.FocusEvent;\r
+import org.eclipse.swt.events.FocusListener;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.KeyListener;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.Shell;\r
+\r
+public class SwtFocusHandler implements FocusListener, KeyListener {\r
+\r
+ private Composite composite;\r
+ private final Display display;\r
+ private AwtFocusHandler awtHandler;\r
+\r
+ public SwtFocusHandler(Composite composite) {\r
+ assert composite != null;\r
+ assert Display.getCurrent() != null; // On SWT event thread\r
+ \r
+ this.composite = composite;\r
+ display = composite.getDisplay();\r
+ composite.addFocusListener(this);\r
+ composite.addKeyListener(this);\r
+ }\r
+\r
+ public void setAwtHandler(AwtFocusHandler handler) {\r
+ assert handler != null;\r
+ assert awtHandler == null; // this method is meant to be called once\r
+ assert composite != null;\r
+ assert Display.getCurrent() != null; // On SWT event thread \r
+ \r
+ awtHandler = handler;\r
+ \r
+ // Dismiss Swing popups when the main window is moved. (It would be \r
+ // better to dismiss popups whenever the titlebar is clicked, but \r
+ // there does not seem to be a way.)\r
+ final ControlAdapter controlAdapter = new ControlAdapter() {\r
+ public void controlMoved(ControlEvent e) {\r
+ assert awtHandler != null;\r
+ awtHandler.postHidePopups();\r
+ }\r
+ };\r
+ final Shell shell = composite.getShell();\r
+ shell.addControlListener(controlAdapter);\r
+ \r
+ // Cleanup listeners on dispose\r
+ composite.addDisposeListener(new DisposeListener() {\r
+ public void widgetDisposed(DisposeEvent e) {\r
+ // Remove listener from shell before nullifying awtHandler\r
+ shell.removeControlListener(controlAdapter);\r
+ awtHandler.dispose();\r
+ awtHandler = null;\r
+ composite = null;\r
+ }\r
+ });\r
+ }\r
+ \r
+ void gainFocusNext() {\r
+ traverse(SWT.TRAVERSE_TAB_NEXT);\r
+ }\r
+ \r
+ void gainFocusPrevious() {\r
+ traverse(SWT.TRAVERSE_TAB_PREVIOUS);\r
+ }\r
+ \r
+ private void traverse(final int traversal) {\r
+ //assert composite != null;\r
+ if (composite == null)\r
+ return;\r
+\r
+ // Tab from the containing SWT component while \r
+ // running on the SWT thread\r
+ Runnable r = new Runnable() {\r
+ public void run() {\r
+ composite.traverse(traversal);\r
+ }\r
+ };\r
+ display.asyncExec(r);\r
+ }\r
+\r
+// boolean hasFocus() {\r
+// assert composite != null;\r
+// \r
+// // This will return true if the composite has focus, or if any\r
+// // foreign (e.g. AWT) child of the composite has focus.\r
+// if (display.isDisposed()) {\r
+// return false;\r
+// }\r
+// final boolean[] result = new boolean[1];\r
+// display.syncExec(new Runnable() {\r
+// public void run() {\r
+// result[0] = (!composite.isDisposed() &&\r
+// (display.getFocusControl() == composite));\r
+// }\r
+// });\r
+// return result[0];\r
+// }\r
+\r
+ // ..................... Listener implementations\r
+ \r
+ public void focusGained(FocusEvent e) {\r
+ assert awtHandler != null;\r
+ assert Display.getCurrent() != null; // On SWT event thread\r
+\r
+ // System.out.println("Gained: " + e.toString() + " (" + e.widget.getClass().getName() + ")");\r
+ EventQueue.invokeLater(new Runnable() {\r
+ public void run() {\r
+ // composite DisposeListener may have nullified this meanwhile!\r
+ // Not a bug.\r
+ if (awtHandler != null)\r
+ awtHandler.gainFocus();\r
+ }\r
+ });\r
+ }\r
+ \r
+ public void focusLost(FocusEvent e) {\r
+ // System.out.println("Lost: " + e.toString() + " (" + e.widget.getClass().getName() + ")");\r
+ }\r
+\r
+ public void keyPressed(KeyEvent e) {\r
+ assert Display.getCurrent() != null; // On SWT event thread\r
+\r
+ // If the embedded swing root pane has no components to receive focus, \r
+ // then there will be cases where the parent SWT composite will keep \r
+ // focus. (For example, when tabbing into the root pane container). \r
+ // By default, in these cases, the focus is swallowed by the Composite\r
+ // and never escapes. This code allows tab and back-tab to do the \r
+ // proper traversal to other SWT components from the composite.\r
+ // TODO: other keys?\r
+ if (e.keyCode == SWT.TAB) {\r
+ // TODO: In some cases, this gobbles up all the tabs, even from AWT children. Find a more selective way. \r
+ /*if (e.stateMask == SWT.NONE) {\r
+ traverse(SWT.TRAVERSE_TAB_NEXT);\r
+ } else if (e.stateMask == SWT.SHIFT) {\r
+ traverse(SWT.TRAVERSE_TAB_PREVIOUS);\r
+ }*/\r
+ }\r
+ }\r
+\r
+ public void keyReleased(KeyEvent e) {\r
+ }\r
+\r
+\r
+}\r