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