]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/internal/awt/AwtFocusHandler.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / internal / awt / AwtFocusHandler.java
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
7  *
8  * Contributors:
9  *     SAS Institute - initial API and implementation
10  *******************************************************************************/
11 package org.simantics.utils.ui.internal.awt;
12
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;
28
29 import javax.swing.JPopupMenu;
30 import javax.swing.text.Caret;
31 import javax.swing.text.JTextComponent;
32
33
34 @SuppressWarnings({"rawtypes", "unchecked"})
35 public class AwtFocusHandler implements FocusListener, ContainerListener, 
36                                       WindowFocusListener {
37
38     private Frame frame;
39     private SwtFocusHandler swtHandler;
40     private RecursiveContainerListener containerListener;
41     private boolean awtHasFocus = false;
42     private Component currentComponent = null;
43
44     public AwtFocusHandler(Frame frame) {
45         assert frame != null;
46
47         this.frame = frame;
48         this.containerListener = new RecursiveContainerListener(this);
49         frame.addContainerListener(this.containerListener);
50         frame.addWindowFocusListener(this);
51     }
52
53     public void setSwtHandler(SwtFocusHandler handler) {
54         assert handler != null;
55         assert swtHandler == null;  // this method is meant to be called once
56
57         swtHandler = handler;
58     }
59
60     /**
61      * Invoked from {@link SwtFocusHandler} DisposeListener.
62      * Only intended to be invoked once.
63      */
64     public void dispose() {
65         assert frame != null;
66         frame.removeWindowFocusListener(this);
67         frame.removeContainerListener(containerListener);
68         frame = null;
69         currentComponent = null;
70     }
71
72     void gainFocus() {
73         // assert frame != null;
74         // assert !awtHasFocus;
75         assert EventQueue.isDispatchThread();    // On AWT event thread
76         if (frame == null)
77             return;
78
79         FocusTraversalPolicy policy = frame.getFocusTraversalPolicy();
80         Component component;
81         if (policy instanceof EmbeddedChildFocusTraversalPolicy) {
82             EmbeddedChildFocusTraversalPolicy embeddedPolicy = (EmbeddedChildFocusTraversalPolicy) policy; 
83             component = embeddedPolicy.getCurrentComponent(frame);
84         } else {
85             // TODO: direction based?
86             component = policy.getDefaultComponent(frame);
87         }
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
92         }
93         awtHasFocus = true;
94     }
95     
96     /**
97      * Moves focus back to the next SWT component
98      */
99     void transferFocusNext() {
100         assert swtHandler != null;
101         assert awtHasFocus;
102         
103         awtHasFocus = false;
104         swtHandler.gainFocusNext();
105     }
106     
107     /**
108      * Moves focus back to the previous SWT component
109      */
110     void transferFocusPrevious() {
111         assert swtHandler != null;
112         assert awtHasFocus;
113         
114         awtHasFocus = false;
115         swtHandler.gainFocusPrevious();
116     }
117     
118     boolean awtHasFocus() {
119         return awtHasFocus;
120     }
121
122     Component getCurrentComponent() {
123         return currentComponent;
124     }
125     
126     // ..................... Listener implementations
127
128     public void focusGained(FocusEvent e) {
129         assert e != null;
130         assert EventQueue.isDispatchThread();    // On AWT event thread
131         
132         // System.out.println("gained (awt). component = " + e.getComponent() + ", opposite = " + e.getOppositeComponent());
133         currentComponent  = e.getComponent();
134     }
135
136     public void focusLost(FocusEvent e) {
137         // System.out.println("component focus lost (awt). opposite = " + e.getOppositeComponent());
138         
139         // Intentionally leaving currentComponent set. When window focus is lost, 
140         // it will be needed. 
141     }
142
143     public void componentAdded(ContainerEvent e) {
144         assert e != null;
145         assert EventQueue.isDispatchThread();    // On AWT event thread
146         
147         e.getChild().addFocusListener(this);
148     }
149
150     public void componentRemoved(ContainerEvent e) {
151         assert e != null;
152         assert EventQueue.isDispatchThread();    // On AWT event thread
153         
154         e.getChild().removeFocusListener(this);
155     }
156     
157     public void windowGainedFocus(WindowEvent e) {
158         assert EventQueue.isDispatchThread();    // On AWT event thread
159         // System.out.println("WindowFocusListener.windowGainedFocus");
160         awtHasFocus = true;
161     }
162
163     public void windowLostFocus(WindowEvent e) {
164         assert e != null;
165         assert swtHandler != null;
166         assert EventQueue.isDispatchThread();    // On AWT event thread
167         
168         // System.out.println("WindowFocusListener.windowLostFocus");
169         
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();
178         
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.
185         //
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
195             // gainFocus();
196             // return;
197         //}
198         
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
202         // manually.
203         if (currentComponent instanceof JTextComponent) {
204             Caret caret = ((JTextComponent)currentComponent).getCaret();
205             if (caret != null) {
206                 caret.setSelectionVisible(false);
207             }
208         }
209         awtHasFocus = false;
210     }
211
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;
218         if (frame == null)
219             return result;
220
221         // Look for popups inside the frame's component hierarchy. 
222         // Lightweight popups will be found here. 
223         findContainedPopups(frame, popups);
224         
225         // Also look for popups in the frame's window hierachy. 
226         // Heavyweight popups will be found here.
227         findOwnedPopups(frame, popups);
228         
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()) {
233                 result = true;
234                 popup.setVisible(false);
235             }
236         }
237         return result;
238     }
239
240     private void findOwnedPopups(Window window, List popups) {
241         assert window != null;
242         assert EventQueue.isDispatchThread();    // On AWT event thread
243         
244         Window[] ownedWindows = window.getOwnedWindows();
245         for (int i = 0; i < ownedWindows.length; i++) {
246             findContainedPopups(ownedWindows[i], popups);
247             findOwnedPopups(ownedWindows[i], popups);
248         }
249     }
250
251     private void findContainedPopups(Container container, List popups) {
252         assert container != null;
253         assert popups != null;
254         assert EventQueue.isDispatchThread();    // On AWT event thread
255         
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) {
261                 popups.add(c);
262             } else if (c instanceof Container) {
263                 findContainedPopups((Container)c, popups);
264             }
265         }
266     }
267
268     public void postHidePopups() {
269         EventQueue.invokeLater(new Runnable() {
270             public void run() {
271                 hidePopups();
272             }
273         });
274     }
275
276 }