1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.scenegraph.example;
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;
26 import javax.swing.JApplet;
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;
42 * embeddedComposite = new SWTAWTComposite(parent, SWT.NONE) {
43 * protected JComponent createSwingComponent() {
44 * scrollPane = new JScrollPane();
45 * table = new JTable();
46 * scrollPane.setViewportView(table);
50 * // For asynchronous AWT UI population of the swing components:
51 * embeddedComposite.populate();
52 * // and optionally you can wait until the AWT UI population
54 * embeddedComposite.waitUntilPopulated();
58 * // Do both things above in one call to block until the
59 * // AWT UI population is complete:
60 * embeddedComposite.syncPopulate();
62 * // Both methods assume all invocations are made from the SWT display thread.
66 * @author Tuukka Lehtonen
68 public abstract class SWTAWTComponent extends Composite {
72 private Component awtComponent;
74 private JApplet panel;
76 private final AtomicBoolean populationStarted = new AtomicBoolean(false);
78 private final AtomicBoolean populated = new AtomicBoolean(false);
80 private final Semaphore populationSemaphore = new Semaphore(0);
82 private static AWTEventListener awtListener = null;
84 public SWTAWTComponent(Composite parent, int style) {
85 super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);
89 * Create a global AWTEventListener for focus management.
90 * NOTE: There is really no need to dispose this once it's been initialized
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();
104 // Execute in AWT thread..
105 ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
108 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);
114 protected Container getContainer() {
118 public Component getAWTComponent() {
123 public void dispose() {
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
135 public void syncPopulate() {
137 waitUntilPopulated();
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
144 public void populate() {
145 if (!populationStarted.compareAndSet(false, true))
146 throw new IllegalStateException(this + ".populate was invoked multiple times");
149 //ITask task = ThreadLogger.getInstance().begin("createFrame");
152 scheduleComponentCreation();
155 public void waitUntilPopulated() {
160 boolean done = false;
162 done = populationSemaphore.tryAcquire(10, TimeUnit.MILLISECONDS);
163 while (!done && getDisplay().readAndDispatch()) {
165 * Note: readAndDispatch can cause this to be disposed.
167 if(isDisposed()) return;
168 done = populationSemaphore.tryAcquire();
171 } catch (InterruptedException e) {
172 throw new Error("EmbeddedSwingComposite population interrupted for class " + this, e);
176 private void createFrame() {
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
183 System.setProperty("sun.awt.noerasebackground", "true");
186 frame = SWT_AWT.new_Frame(this);
188 // This listener clears garbage during resizing, making it looker much cleaner
189 addControlListener(new CleanResizeListener());
190 initAWTEventListener();
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() {
199 panel = new JApplet();
200 panel.setLayout(new GridLayout(1,1,0,0));
203 awtComponent = createSwingComponent();
204 panel.add(awtComponent);
206 // Needed to support #waitUntilPopulated
208 if (populationSemaphore != null)
209 populationSemaphore.release();
215 protected abstract Component createSwingComponent();
217 private static class CleanResizeListener extends ControlAdapter {
218 private Rectangle oldRect = null;
221 public void controlResized(ControlEvent e) {
223 assert Display.getCurrent() != null; // On SWT event thread
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);
237 gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta);
238 gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height);