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