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