1 /*******************************************************************************
\r
2 * Copyright (c) 2007 SAS Institute.
\r
3 * All rights reserved. This program and the accompanying materials
\r
4 * are made available under the terms of the Eclipse Public License v1.0
\r
5 * which accompanies this distribution, and is available at
\r
6 * http://www.eclipse.org/legal/epl-v10.html
\r
9 * SAS Institute - initial API and implementation
\r
10 *******************************************************************************/
\r
11 package org.simantics.utils.ui.awt;
\r
13 import java.awt.EventQueue;
\r
15 import org.eclipse.swt.SWT;
\r
16 import org.eclipse.swt.events.ControlAdapter;
\r
17 import org.eclipse.swt.events.ControlEvent;
\r
18 import org.eclipse.swt.events.DisposeEvent;
\r
19 import org.eclipse.swt.events.DisposeListener;
\r
20 import org.eclipse.swt.events.FocusEvent;
\r
21 import org.eclipse.swt.events.FocusListener;
\r
22 import org.eclipse.swt.events.KeyEvent;
\r
23 import org.eclipse.swt.events.KeyListener;
\r
24 import org.eclipse.swt.widgets.Composite;
\r
25 import org.eclipse.swt.widgets.Display;
\r
26 import org.eclipse.swt.widgets.Shell;
\r
28 public class SwtFocusHandler implements FocusListener, KeyListener {
\r
30 private Composite composite;
\r
31 private final Display display;
\r
32 private AwtFocusHandler awtHandler;
\r
34 public SwtFocusHandler(Composite composite) {
\r
35 assert composite != null;
\r
36 assert Display.getCurrent() != null; // On SWT event thread
\r
38 this.composite = composite;
\r
39 display = composite.getDisplay();
\r
40 composite.addFocusListener(this);
\r
41 composite.addKeyListener(this);
\r
44 public void setAwtHandler(AwtFocusHandler handler) {
\r
45 assert handler != null;
\r
46 assert awtHandler == null; // this method is meant to be called once
\r
47 assert composite != null;
\r
48 assert Display.getCurrent() != null; // On SWT event thread
\r
50 awtHandler = handler;
\r
52 // Dismiss Swing popups when the main window is moved. (It would be
\r
53 // better to dismiss popups whenever the titlebar is clicked, but
\r
54 // there does not seem to be a way.)
\r
55 final ControlAdapter controlAdapter = new ControlAdapter() {
\r
56 public void controlMoved(ControlEvent e) {
\r
57 assert awtHandler != null;
\r
58 awtHandler.postHidePopups();
\r
61 final Shell shell = composite.getShell();
\r
62 shell.addControlListener(controlAdapter);
\r
64 // Cleanup listeners on dispose
\r
65 composite.addDisposeListener(new DisposeListener() {
\r
66 public void widgetDisposed(DisposeEvent e) {
\r
67 // Remove listener from shell before nullifying awtHandler
\r
68 shell.removeControlListener(controlAdapter);
\r
69 awtHandler.dispose();
\r
76 void gainFocusNext() {
\r
77 traverse(SWT.TRAVERSE_TAB_NEXT);
\r
80 void gainFocusPrevious() {
\r
81 traverse(SWT.TRAVERSE_TAB_PREVIOUS);
\r
84 private void traverse(final int traversal) {
\r
85 //assert composite != null;
\r
86 if (composite == null)
\r
89 // Tab from the containing SWT component while
\r
90 // running on the SWT thread
\r
91 Runnable r = new Runnable() {
\r
93 composite.traverse(traversal);
\r
96 display.asyncExec(r);
\r
99 // boolean hasFocus() {
\r
100 // assert composite != null;
\r
102 // // This will return true if the composite has focus, or if any
\r
103 // // foreign (e.g. AWT) child of the composite has focus.
\r
104 // if (display.isDisposed()) {
\r
107 // final boolean[] result = new boolean[1];
\r
108 // display.syncExec(new Runnable() {
\r
109 // public void run() {
\r
110 // result[0] = (!composite.isDisposed() &&
\r
111 // (display.getFocusControl() == composite));
\r
114 // return result[0];
\r
117 // ..................... Listener implementations
\r
119 public void focusGained(FocusEvent e) {
\r
120 assert awtHandler != null;
\r
121 assert Display.getCurrent() != null; // On SWT event thread
\r
123 // System.out.println("Gained: " + e.toString() + " (" + e.widget.getClass().getName() + ")");
\r
124 EventQueue.invokeLater(new Runnable() {
\r
125 public void run() {
\r
126 // composite DisposeListener may have nullified this meanwhile!
\r
128 if (awtHandler != null)
\r
129 awtHandler.gainFocus();
\r
134 public void focusLost(FocusEvent e) {
\r
135 // System.out.println("Lost: " + e.toString() + " (" + e.widget.getClass().getName() + ")");
\r
138 public void keyPressed(KeyEvent e) {
\r
139 assert Display.getCurrent() != null; // On SWT event thread
\r
141 // If the embedded swing root pane has no components to receive focus,
\r
142 // then there will be cases where the parent SWT composite will keep
\r
143 // focus. (For example, when tabbing into the root pane container).
\r
144 // By default, in these cases, the focus is swallowed by the Composite
\r
145 // and never escapes. This code allows tab and back-tab to do the
\r
146 // proper traversal to other SWT components from the composite.
\r
147 // TODO: other keys?
\r
148 if (e.keyCode == SWT.TAB) {
\r
149 // TODO: In some cases, this gobbles up all the tabs, even from AWT children. Find a more selective way.
\r
150 /*if (e.stateMask == SWT.NONE) {
\r
151 traverse(SWT.TRAVERSE_TAB_NEXT);
\r
152 } else if (e.stateMask == SWT.SHIFT) {
\r
153 traverse(SWT.TRAVERSE_TAB_PREVIOUS);
\r
158 public void keyReleased(KeyEvent e) {
\r