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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.scenegraph.example;
\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
26 import javax.swing.JApplet;
\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
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
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
54 * embeddedComposite.waitUntilPopulated();
\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
62 * // Both methods assume all invocations are made from the SWT display thread.
\r
66 * @author Tuukka Lehtonen
\r
68 public abstract class SWTAWTComponent extends Composite {
\r
70 private Frame frame;
\r
72 private Component awtComponent;
\r
74 private JApplet panel;
\r
76 private final AtomicBoolean populationStarted = new AtomicBoolean(false);
\r
78 private final AtomicBoolean populated = new AtomicBoolean(false);
\r
80 private final Semaphore populationSemaphore = new Semaphore(0);
\r
82 private static AWTEventListener awtListener = null;
\r
84 public SWTAWTComponent(Composite parent, int style) {
\r
85 super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);
\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
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
104 // Execute in AWT thread..
\r
105 ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
\r
107 public void run() {
\r
108 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);
\r
114 protected Container getContainer() {
\r
118 public Component getAWTComponent() {
\r
119 return awtComponent;
\r
123 public void dispose() {
\r
124 if (!isDisposed()) {
\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
135 public void syncPopulate() {
\r
137 waitUntilPopulated();
\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
144 public void populate() {
\r
145 if (!populationStarted.compareAndSet(false, true))
\r
146 throw new IllegalStateException(this + ".populate was invoked multiple times");
\r
149 //ITask task = ThreadLogger.getInstance().begin("createFrame");
\r
152 scheduleComponentCreation();
\r
155 public void waitUntilPopulated() {
\r
156 if (populated.get())
\r
160 boolean done = false;
\r
162 done = populationSemaphore.tryAcquire(10, TimeUnit.MILLISECONDS);
\r
163 while (!done && getDisplay().readAndDispatch()) {
\r
165 * Note: readAndDispatch can cause this to be disposed.
\r
167 if(isDisposed()) return;
\r
168 done = populationSemaphore.tryAcquire();
\r
171 } catch (InterruptedException e) {
\r
172 throw new Error("EmbeddedSwingComposite population interrupted for class " + this, e);
\r
176 private void createFrame() {
\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
183 System.setProperty("sun.awt.noerasebackground", "true");
\r
186 frame = SWT_AWT.new_Frame(this);
\r
188 // This listener clears garbage during resizing, making it looker much cleaner
\r
189 addControlListener(new CleanResizeListener());
\r
190 initAWTEventListener();
\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
198 public void run() {
\r
199 panel = new JApplet();
\r
200 panel.setLayout(new GridLayout(1,1,0,0));
\r
203 awtComponent = createSwingComponent();
\r
204 panel.add(awtComponent);
\r
206 // Needed to support #waitUntilPopulated
\r
207 populated.set(true);
\r
208 if (populationSemaphore != null)
\r
209 populationSemaphore.release();
\r
215 protected abstract Component createSwingComponent();
\r
217 private static class CleanResizeListener extends ControlAdapter {
\r
218 private Rectangle oldRect = null;
\r
221 public void controlResized(ControlEvent e) {
\r
223 assert Display.getCurrent() != null; // On SWT event thread
\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
237 gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta);
\r
238 gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height);
\r