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