1 /*******************************************************************************
2 * Copyright (c) 2007 SAS Institute.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * SAS Institute - initial API and implementation
10 *******************************************************************************/
11 package org.simantics.utils.ui.internal.awt;
13 import java.awt.Component;
14 import java.awt.Container;
15 import java.awt.EventQueue;
16 import java.awt.FocusTraversalPolicy;
17 import java.awt.Frame;
18 import java.awt.Window;
19 import java.awt.event.ContainerEvent;
20 import java.awt.event.ContainerListener;
21 import java.awt.event.FocusEvent;
22 import java.awt.event.FocusListener;
23 import java.awt.event.WindowEvent;
24 import java.awt.event.WindowFocusListener;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
29 import javax.swing.JPopupMenu;
30 import javax.swing.text.Caret;
31 import javax.swing.text.JTextComponent;
34 @SuppressWarnings({"rawtypes", "unchecked"})
35 public class AwtFocusHandler implements FocusListener, ContainerListener,
39 private SwtFocusHandler swtHandler;
40 private RecursiveContainerListener containerListener;
41 private boolean awtHasFocus = false;
42 private Component currentComponent = null;
44 public AwtFocusHandler(Frame frame) {
48 this.containerListener = new RecursiveContainerListener(this);
49 frame.addContainerListener(this.containerListener);
50 frame.addWindowFocusListener(this);
53 public void setSwtHandler(SwtFocusHandler handler) {
54 assert handler != null;
55 assert swtHandler == null; // this method is meant to be called once
61 * Invoked from {@link SwtFocusHandler} DisposeListener.
62 * Only intended to be invoked once.
64 public void dispose() {
66 frame.removeWindowFocusListener(this);
67 frame.removeContainerListener(containerListener);
69 currentComponent = null;
73 // assert frame != null;
74 // assert !awtHasFocus;
75 assert EventQueue.isDispatchThread(); // On AWT event thread
79 FocusTraversalPolicy policy = frame.getFocusTraversalPolicy();
81 if (policy instanceof EmbeddedChildFocusTraversalPolicy) {
82 EmbeddedChildFocusTraversalPolicy embeddedPolicy = (EmbeddedChildFocusTraversalPolicy) policy;
83 component = embeddedPolicy.getCurrentComponent(frame);
85 // TODO: direction based?
86 component = policy.getDefaultComponent(frame);
88 if (component != null) {
89 // System.out.println("Requesting focus for component: " + component);
90 component.requestFocus();
91 // TODO: else case error? If not, consider moving flag setting below into this if
97 * Moves focus back to the next SWT component
99 void transferFocusNext() {
100 assert swtHandler != null;
104 swtHandler.gainFocusNext();
108 * Moves focus back to the previous SWT component
110 void transferFocusPrevious() {
111 assert swtHandler != null;
115 swtHandler.gainFocusPrevious();
118 boolean awtHasFocus() {
122 Component getCurrentComponent() {
123 return currentComponent;
126 // ..................... Listener implementations
128 public void focusGained(FocusEvent e) {
130 assert EventQueue.isDispatchThread(); // On AWT event thread
132 // System.out.println("gained (awt). component = " + e.getComponent() + ", opposite = " + e.getOppositeComponent());
133 currentComponent = e.getComponent();
136 public void focusLost(FocusEvent e) {
137 // System.out.println("component focus lost (awt). opposite = " + e.getOppositeComponent());
139 // Intentionally leaving currentComponent set. When window focus is lost,
140 // it will be needed.
143 public void componentAdded(ContainerEvent e) {
145 assert EventQueue.isDispatchThread(); // On AWT event thread
147 e.getChild().addFocusListener(this);
150 public void componentRemoved(ContainerEvent e) {
152 assert EventQueue.isDispatchThread(); // On AWT event thread
154 e.getChild().removeFocusListener(this);
157 public void windowGainedFocus(WindowEvent e) {
158 assert EventQueue.isDispatchThread(); // On AWT event thread
159 // System.out.println("WindowFocusListener.windowGainedFocus");
163 public void windowLostFocus(WindowEvent e) {
165 assert swtHandler != null;
166 assert EventQueue.isDispatchThread(); // On AWT event thread
168 // System.out.println("WindowFocusListener.windowLostFocus");
170 // Dismiss any popup menus that are
171 // open when losing focus. This prevents situations where
172 // multiple popup menus are visible at the same time. In JDK 1.4 and earlier,
173 // the dismissal is not done automatically. In JDK 1.5, this code is
174 // unnecessary, but it doesn't seem to hurt anything.
175 // TODO: verify this is OK on other windowing systems
176 // TODO: disable in post-1.4 environments
177 /* boolean popupShown = */hidePopups();
179 // If focus is being lost to the parent SWT composite, then
180 // grab it back for AWT and return. Normally the parent SWT composite will
181 // do this for us, but it will not see a focus gained event when focus
182 // is transferred to it from its AWT frame child.
183 // This happens, for example, if an AWT control has focus and the
184 // tab of a containing (already active) view is clicked.
186 // However, don't grab back focus if a popup was hidden above. The popup
187 // area will not be properly redrawn (the popup, or part of it, will
188 // appear to be still there.
189 //if (!popupShown && swtHandler.hasFocus()) {
190 // System.out.println("**** Taking back focus: " + e);
191 // This seems to have side effects, so it's commented out for now.
192 // (Sometimes, it forces the workbench window to the foreground when another
193 // program's window is selected.)
194 // TODO: find an alternate approach to reassert focus
199 // On a normal change of focus, Swing will turn off any selection
200 // in a text field to help indicate focus is lost. This won't happen
201 // automatically when transferring to SWT, so turn off the selection
203 if (currentComponent instanceof JTextComponent) {
204 Caret caret = ((JTextComponent)currentComponent).getCaret();
206 caret.setSelectionVisible(false);
212 // Returns true if any popup has been hidden
213 private boolean hidePopups() {
214 boolean result = false;
215 List popups = new ArrayList();
216 assert EventQueue.isDispatchThread(); // On AWT event thread
217 Window frame = this.frame;
221 // Look for popups inside the frame's component hierarchy.
222 // Lightweight popups will be found here.
223 findContainedPopups(frame, popups);
225 // Also look for popups in the frame's window hierachy.
226 // Heavyweight popups will be found here.
227 findOwnedPopups(frame, popups);
229 // System.out.println("Hiding popups, count=" + popups.size());
230 for (Iterator iter = popups.iterator(); iter.hasNext();) {
231 Component popup = (Component)iter.next();
232 if (popup.isVisible()) {
234 popup.setVisible(false);
240 private void findOwnedPopups(Window window, List popups) {
241 assert window != null;
242 assert EventQueue.isDispatchThread(); // On AWT event thread
244 Window[] ownedWindows = window.getOwnedWindows();
245 for (int i = 0; i < ownedWindows.length; i++) {
246 findContainedPopups(ownedWindows[i], popups);
247 findOwnedPopups(ownedWindows[i], popups);
251 private void findContainedPopups(Container container, List popups) {
252 assert container != null;
253 assert popups != null;
254 assert EventQueue.isDispatchThread(); // On AWT event thread
256 Component[] components = container.getComponents();
257 for (int i = 0; i < components.length; i++) {
258 Component c = components[i];
259 // JPopupMenu is a container, so check for it first
260 if (c instanceof JPopupMenu) {
262 } else if (c instanceof Container) {
263 findContainedPopups((Container)c, popups);
268 public void postHidePopups() {
269 EventQueue.invokeLater(new Runnable() {