]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/AwtDialogListener.java
25728b8e220510aadc63cc8683cf7daa74ee5690
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / awt / AwtDialogListener.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.awt;\r
12 \r
13 import java.awt.AWTEvent;\r
14 import java.awt.Dialog;\r
15 import java.awt.EventQueue;\r
16 import java.awt.Toolkit;\r
17 import java.awt.Window;\r
18 import java.awt.event.AWTEventListener;\r
19 import java.awt.event.ComponentEvent;\r
20 import java.awt.event.ComponentListener;\r
21 import java.awt.event.WindowEvent;\r
22 import java.util.ArrayList;\r
23 import java.util.List;\r
24 \r
25 import org.eclipse.swt.widgets.Display;\r
26 \r
27 /**\r
28  * A listener that insures the proper modal behavior of Swing dialogs when running\r
29  * within a SWT environment. When initialized, it blocks and unblocks SWT input\r
30  * as modal Swing dialogs are shown and hidden. \r
31  */\r
32 @SuppressWarnings({"rawtypes", "unchecked"})\r
33 class AwtDialogListener implements AWTEventListener, ComponentListener {\r
34     \r
35     // modalDialogs should be accessed only from the AWT thread, so no\r
36     // synchronization is needed. \r
37     private final List modalDialogs = new ArrayList();\r
38     private final Display display;\r
39     \r
40     /**\r
41      * Registers this object as an AWT event listener so that Swing dialogs have the \r
42      * proper modal behavior in the containing SWT environment. This is called automatically\r
43      * when you construct a {@link EmbeddedSwingComposite}, and it\r
44      * need not be called separately in that case.  \r
45      * @param shell \r
46      */\r
47     AwtDialogListener(Display display) {\r
48         assert display != null;\r
49         \r
50         this.display = display;\r
51         Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.WINDOW_EVENT_MASK);\r
52     }\r
53     \r
54     private void handleRemovedDialog(Dialog awtDialog, boolean removeListener) {\r
55         assert awtDialog != null;\r
56         assert modalDialogs != null;\r
57         assert display != null;\r
58         assert EventQueue.isDispatchThread();    // On AWT event thread\r
59         \r
60         // System.out.println("Remove dialog: " + awtDialog);\r
61         if (removeListener) {\r
62             awtDialog.removeComponentListener(this);\r
63         }\r
64         // Note: there is no isModal() check here because the dialog might \r
65         // have been changed from modal to non-modal after it was opened. In this case\r
66         // the currently visible dialog would still act modal and we'd need to unblock\r
67         // SWT here when it goes away.\r
68         if (modalDialogs.remove(awtDialog)) {\r
69             display.asyncExec(new Runnable() {\r
70                 public void run() {\r
71                     SwtInputBlocker.unblock();\r
72                 }\r
73             });            \r
74         }\r
75     }\r
76 \r
77     private void handleAddedDialog(final Dialog awtDialog) {\r
78         assert awtDialog != null;\r
79         assert modalDialogs != null;\r
80         assert EventQueue.isDispatchThread();    // On AWT event thread\r
81         \r
82         // System.out.println("Add dialog: " + awtDialog);\r
83         if (modalDialogs.contains(awtDialog) || !awtDialog.isModal() || !awtDialog.isVisible()) {\r
84             return;\r
85         }\r
86         modalDialogs.add(awtDialog);\r
87         awtDialog.addComponentListener(this);\r
88         display.asyncExec(new Runnable() {\r
89             public void run() {\r
90                 SwtInputBlocker.block();\r
91             }\r
92         });        \r
93     }\r
94     \r
95     void requestFocus() {\r
96         // TODO: this does not always bring the dialog to the top \r
97         // under some Linux desktops/window managers (e.g. metacity under GNOME).\r
98         EventQueue.invokeLater(new Runnable() {\r
99             public void run() {\r
100                 assert modalDialogs != null;\r
101                 \r
102                 int size = modalDialogs.size();\r
103                 if (size > 0) {\r
104                     final Dialog awtDialog = (Dialog)modalDialogs.get(size - 1);\r
105 \r
106                     // In one case, a call to requestFocus() alone does not \r
107                     // bring the AWT dialog to the top. This happens if the \r
108                     // dialog is given a null parent frame. When opened, the dialog\r
109                     // can be hidden by the SWT window even when it obtains focus.\r
110                     // Calling toFront() solves the problem, but...\r
111                     //\r
112                     // There are still problems if the Metal look and feel is in use.\r
113                     // The SWT window will hide the dialog the first time it is \r
114                     // selected. Once the dialog is brought back to the front by \r
115                     // the user, there is no further problem. \r
116                     //\r
117                     // Why? It looks like SWT is not being notified of lost focus when \r
118                     // the Metal dialog first opens; subsequently, when focus is regained, the \r
119                     // focus gain event is not posted to the SwtInputBlocker.  \r
120                     //\r
121                     // The workaround is to use Windows look and feel, rather than Metal.\r
122                     // System.out.println("Bringing to front");\r
123 \r
124                     awtDialog.requestFocus();\r
125                     awtDialog.toFront();\r
126                 }\r
127             }\r
128         });\r
129     }\r
130 \r
131     private void handleOpenedWindow(WindowEvent event) {\r
132         assert event != null;\r
133         assert EventQueue.isDispatchThread();    // On AWT event thread\r
134         \r
135         Window window = event.getWindow();\r
136         if (window instanceof Dialog) {\r
137             handleAddedDialog((Dialog)window);\r
138         }\r
139     }\r
140     \r
141     private void handleClosedWindow(WindowEvent event) {\r
142         assert event != null;\r
143         assert EventQueue.isDispatchThread();    // On AWT event thread\r
144         \r
145         // Dispose-based close\r
146         Window window = event.getWindow();\r
147         if (window instanceof Dialog) {\r
148             // Remove dialog and component listener\r
149             handleRemovedDialog((Dialog)window, true);\r
150         }\r
151     }\r
152 \r
153     private void handleClosingWindow(WindowEvent event) {\r
154         assert event != null;\r
155         assert EventQueue.isDispatchThread();    // On AWT event thread\r
156         \r
157         // System-based close \r
158         Window window = event.getWindow();\r
159         if (window instanceof Dialog) {\r
160             final Dialog dialog = (Dialog) window;\r
161             // Defer until later. Bad things happen if \r
162             // handleRemovedDialog() is called directly from \r
163             // this event handler. The Swing dialog does not close\r
164             // properly and its modality remains in effect.\r
165             EventQueue.invokeLater(new Runnable() {\r
166                 public void run() {\r
167                     // Remove dialog and component listener\r
168                     handleRemovedDialog(dialog, true);\r
169                 }\r
170             });\r
171         }\r
172     }\r
173     \r
174     public void eventDispatched(AWTEvent event) {\r
175         assert event != null;\r
176         assert EventQueue.isDispatchThread();    // On AWT event thread\r
177         \r
178         switch (event.getID()) {\r
179         case WindowEvent.WINDOW_OPENED:\r
180             handleOpenedWindow((WindowEvent)event);\r
181             break;\r
182             \r
183         case WindowEvent.WINDOW_CLOSED:\r
184             handleClosedWindow((WindowEvent)event);\r
185             break;\r
186 \r
187         case WindowEvent.WINDOW_CLOSING:\r
188             handleClosingWindow((WindowEvent)event);\r
189             break;\r
190 \r
191         default:\r
192             break;\r
193         }\r
194     }\r
195 \r
196     public void componentHidden(ComponentEvent e) {\r
197         assert e != null;\r
198         assert EventQueue.isDispatchThread();    // On AWT event thread\r
199         \r
200         // System.out.println("Component hidden");\r
201         Object obj = e.getSource();\r
202         if (obj instanceof Dialog) {\r
203             // Remove dialog but keep listener in place so that we know if/when it is set visible\r
204             handleRemovedDialog((Dialog)obj, false);\r
205         }\r
206     }\r
207 \r
208     public void componentShown(ComponentEvent e) {\r
209         assert e != null;\r
210         assert EventQueue.isDispatchThread();    // On AWT event thread\r
211         \r
212         // System.out.println("Component shown");\r
213         Object obj = e.getSource();\r
214         if (obj instanceof Dialog) {\r
215             handleAddedDialog((Dialog)obj);\r
216         }\r
217     }\r
218 \r
219     public void componentResized(ComponentEvent e) {\r
220     }\r
221 \r
222     public void componentMoved(ComponentEvent e) {\r
223     }\r
224         \r
225 }\r