]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/example/SWTAWTComponent.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.swing / src / org / simantics / scenegraph / example / SWTAWTComponent.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.scenegraph.example;\r
13 \r
14 import java.awt.AWTEvent;\r
15 import java.awt.Component;\r
16 import java.awt.Container;\r
17 import java.awt.Frame;\r
18 import java.awt.GridLayout;\r
19 import java.awt.Toolkit;\r
20 import java.awt.event.AWTEventListener;\r
21 import java.awt.event.MouseEvent;\r
22 import java.util.concurrent.Semaphore;\r
23 import java.util.concurrent.TimeUnit;\r
24 import java.util.concurrent.atomic.AtomicBoolean;\r
25 \r
26 import javax.swing.JApplet;\r
27 \r
28 import org.eclipse.swt.SWT;\r
29 import org.eclipse.swt.awt.SWT_AWT;\r
30 import org.eclipse.swt.events.ControlAdapter;\r
31 import org.eclipse.swt.events.ControlEvent;\r
32 import org.eclipse.swt.graphics.GC;\r
33 import org.eclipse.swt.graphics.Rectangle;\r
34 import org.eclipse.swt.widgets.Composite;\r
35 import org.eclipse.swt.widgets.Display;\r
36 import org.simantics.utils.threads.AWTThread;\r
37 import org.simantics.utils.threads.ThreadUtils;\r
38 \r
39 \r
40 /**\r
41  * <pre>\r
42  *        embeddedComposite = new SWTAWTComposite(parent, SWT.NONE) {\r
43  *            protected JComponent createSwingComponent() {\r
44  *                scrollPane = new JScrollPane();\r
45  *                table = new JTable();\r
46  *                scrollPane.setViewportView(table);\r
47  *                return scrollPane;\r
48  *            }\r
49  *        };\r
50  *        // For asynchronous AWT UI population of the swing components:\r
51  *        embeddedComposite.populate();\r
52  *        // and optionally you can wait until the AWT UI population\r
53  *        // has finished:\r
54  *        embeddedComposite.waitUntilPopulated();\r
55  *\r
56  *        // OR:\r
57  *\r
58  *        // Do both things above in one call to block until the\r
59  *        // AWT UI population is complete:\r
60  *        embeddedComposite.syncPopulate();\r
61  *\r
62  *        // Both methods assume all invocations are made from the SWT display thread.\r
63  * </pre>\r
64  * <p>\r
65  * \r
66  * @author Tuukka Lehtonen\r
67  */\r
68 public abstract class SWTAWTComponent extends Composite {\r
69 \r
70     private Frame               frame;\r
71 \r
72     private Component           awtComponent;\r
73 \r
74     private JApplet             panel;\r
75 \r
76     private final AtomicBoolean populationStarted   = new AtomicBoolean(false);\r
77 \r
78     private final AtomicBoolean populated           = new AtomicBoolean(false);\r
79 \r
80     private final Semaphore     populationSemaphore = new Semaphore(0);\r
81 \r
82     private static AWTEventListener awtListener = null;\r
83     \r
84     public SWTAWTComponent(Composite parent, int style) {\r
85         super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);\r
86     }\r
87     \r
88     /**\r
89      * Create a global AWTEventListener for focus management.\r
90      * NOTE: There is really no need to dispose this once it's been initialized\r
91      * \r
92      */\r
93     private synchronized void initAWTEventListener() {\r
94         if(awtListener == null) {\r
95                 awtListener = new AWTEventListener() {\r
96                                 public void eventDispatched(AWTEvent e) {\r
97                                         if(e.getID() == MouseEvent.MOUSE_PRESSED) {\r
98                                                 Object src = e.getSource();\r
99                                                 if(src instanceof Component) {\r
100                                                         ((Component)src).requestFocus();\r
101                                                 }\r
102                                         }\r
103                                 }};\r
104                         // Execute in AWT thread..\r
105                 ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
106                                 @Override\r
107                                 public void run() {\r
108                                 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);\r
109                                 }\r
110                 });\r
111         }\r
112     }\r
113 \r
114     protected Container getContainer() {\r
115         return panel;\r
116     }\r
117 \r
118     public Component getAWTComponent() {\r
119         return awtComponent;\r
120     }\r
121 \r
122     @Override\r
123     public void dispose() {\r
124         if (!isDisposed()) {\r
125             frame.dispose();\r
126             super.dispose();\r
127         }\r
128     }\r
129 \r
130     /**\r
131      * This method must always be called from SWT thread. This prevents the\r
132      * possibility of deadlock (reported between AWT and SWT) by servicing SWT\r
133      * events while waiting for AWT initialization\r
134      */\r
135     public void syncPopulate() {\r
136         populate();\r
137         waitUntilPopulated();\r
138     }\r
139 \r
140     /**\r
141      * This method will create an AWT {@link Frame} through {@link SWT_AWT} and\r
142      * schedule AWT canvas initialization into the AWT thread. The AWT thread initialization will release\r
143      */\r
144     public void populate() {\r
145         if (!populationStarted.compareAndSet(false, true))\r
146             throw new IllegalStateException(this + ".populate was invoked multiple times");\r
147 \r
148         checkWidget();\r
149         //ITask task = ThreadLogger.getInstance().begin("createFrame");\r
150         createFrame();\r
151         //task.finish();\r
152         scheduleComponentCreation();\r
153     }\r
154 \r
155     public void waitUntilPopulated() {\r
156         if (populated.get())\r
157             return;\r
158 \r
159         try {\r
160             boolean done = false;\r
161             while (!done) {\r
162                 done = populationSemaphore.tryAcquire(10, TimeUnit.MILLISECONDS);\r
163                 while (!done && getDisplay().readAndDispatch()) {\r
164                     /*\r
165                      * Note: readAndDispatch can cause this to be disposed.\r
166                      */\r
167                     if(isDisposed()) return;\r
168                     done = populationSemaphore.tryAcquire();\r
169                 }\r
170             }\r
171         } catch (InterruptedException e) {\r
172             throw new Error("EmbeddedSwingComposite population interrupted for class " + this, e);\r
173         }\r
174     }\r
175 \r
176     private void createFrame() {\r
177         /*\r
178          * Set a Windows specific AWT property that prevents heavyweight\r
179          * components from erasing their background. Note that this is a global\r
180          * property and cannot be scoped. It might not be suitable for your\r
181          * application.\r
182          */\r
183         System.setProperty("sun.awt.noerasebackground", "true");\r
184 \r
185         if (frame==null)\r
186             frame = SWT_AWT.new_Frame(this);\r
187 \r
188         // This listener clears garbage during resizing, making it looker much cleaner\r
189         addControlListener(new CleanResizeListener());\r
190         initAWTEventListener();\r
191     }\r
192 \r
193     private void scheduleComponentCreation() {\r
194         // Create AWT/Swing components on the AWT thread. This is\r
195         // especially necessary to avoid an AWT leak bug (6411042).\r
196         ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
197             @Override\r
198             public void run() {\r
199                 panel = new JApplet();\r
200                 panel.setLayout(new GridLayout(1,1,0,0));\r
201                 frame.add(panel);\r
202                 try {\r
203                     awtComponent = createSwingComponent();\r
204                     panel.add(awtComponent);\r
205                 } finally {\r
206                     // Needed to support #waitUntilPopulated\r
207                     populated.set(true);\r
208                     if (populationSemaphore != null)\r
209                         populationSemaphore.release();\r
210                 }\r
211             }\r
212         });\r
213     }\r
214 \r
215     protected abstract Component createSwingComponent();\r
216 \r
217     private static class CleanResizeListener extends ControlAdapter {\r
218         private Rectangle oldRect = null;\r
219 \r
220         @Override\r
221         public void controlResized(ControlEvent e) {\r
222             assert e != null;\r
223             assert Display.getCurrent() != null; // On SWT event thread\r
224 \r
225             // Prevent garbage from Swing lags during resize. Fill exposed areas\r
226             // with background color.\r
227             Composite composite = (Composite) e.widget;\r
228             //Rectangle newRect = composite.getBounds();\r
229             //newRect = composite.getDisplay().map(composite.getParent(), composite, newRect);\r
230             Rectangle newRect = composite.getClientArea();\r
231             if (oldRect != null) {\r
232                 int heightDelta = newRect.height - oldRect.height;\r
233                 int widthDelta = newRect.width - oldRect.width;\r
234                 if ((heightDelta > 0) || (widthDelta > 0)) {\r
235                     GC gc = new GC(composite);\r
236                     try {\r
237                         gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta);\r
238                         gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height);\r
239                     } finally {\r
240                         gc.dispose();\r
241                     }\r
242                 }\r
243             }\r
244             oldRect = newRect;\r
245         }\r
246     }\r
247 \r
248 }\r