/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.scenegraph.example; import java.awt.AWTEvent; import java.awt.Component; import java.awt.Container; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.MouseEvent; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JApplet; import org.eclipse.swt.SWT; import org.eclipse.swt.awt.SWT_AWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.simantics.utils.threads.AWTThread; import org.simantics.utils.threads.ThreadUtils; /** *
 *        embeddedComposite = new SWTAWTComposite(parent, SWT.NONE) {
 *            protected JComponent createSwingComponent() {
 *                scrollPane = new JScrollPane();
 *                table = new JTable();
 *                scrollPane.setViewportView(table);
 *                return scrollPane;
 *            }
 *        };
 *        // For asynchronous AWT UI population of the swing components:
 *        embeddedComposite.populate();
 *        // and optionally you can wait until the AWT UI population
 *        // has finished:
 *        embeddedComposite.waitUntilPopulated();
 *
 *        // OR:
 *
 *        // Do both things above in one call to block until the
 *        // AWT UI population is complete:
 *        embeddedComposite.syncPopulate();
 *
 *        // Both methods assume all invocations are made from the SWT display thread.
 * 
*

* * @author Tuukka Lehtonen */ public abstract class SWTAWTComponent extends Composite { private Frame frame; private Component awtComponent; private JApplet panel; private final AtomicBoolean populationStarted = new AtomicBoolean(false); private final AtomicBoolean populated = new AtomicBoolean(false); private final Semaphore populationSemaphore = new Semaphore(0); private static AWTEventListener awtListener = null; public SWTAWTComponent(Composite parent, int style) { super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED); } /** * Create a global AWTEventListener for focus management. * NOTE: There is really no need to dispose this once it's been initialized * */ private synchronized void initAWTEventListener() { if(awtListener == null) { awtListener = new AWTEventListener() { public void eventDispatched(AWTEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { Object src = e.getSource(); if(src instanceof Component) { ((Component)src).requestFocus(); } } }}; // Execute in AWT thread.. ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { @Override public void run() { Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK); } }); } } protected Container getContainer() { return panel; } public Component getAWTComponent() { return awtComponent; } @Override public void dispose() { if (!isDisposed()) { frame.dispose(); super.dispose(); } } /** * This method must always be called from SWT thread. This prevents the * possibility of deadlock (reported between AWT and SWT) by servicing SWT * events while waiting for AWT initialization */ public void syncPopulate() { populate(); waitUntilPopulated(); } /** * This method will create an AWT {@link Frame} through {@link SWT_AWT} and * schedule AWT canvas initialization into the AWT thread. The AWT thread initialization will release */ public void populate() { if (!populationStarted.compareAndSet(false, true)) throw new IllegalStateException(this + ".populate was invoked multiple times"); checkWidget(); //ITask task = ThreadLogger.getInstance().begin("createFrame"); createFrame(); //task.finish(); scheduleComponentCreation(); } public void waitUntilPopulated() { if (populated.get()) return; try { boolean done = false; while (!done) { done = populationSemaphore.tryAcquire(10, TimeUnit.MILLISECONDS); while (!done && getDisplay().readAndDispatch()) { /* * Note: readAndDispatch can cause this to be disposed. */ if(isDisposed()) return; done = populationSemaphore.tryAcquire(); } } } catch (InterruptedException e) { throw new Error("EmbeddedSwingComposite population interrupted for class " + this, e); } } private void createFrame() { /* * Set a Windows specific AWT property that prevents heavyweight * components from erasing their background. Note that this is a global * property and cannot be scoped. It might not be suitable for your * application. */ System.setProperty("sun.awt.noerasebackground", "true"); if (frame==null) frame = SWT_AWT.new_Frame(this); // This listener clears garbage during resizing, making it looker much cleaner addControlListener(new CleanResizeListener()); initAWTEventListener(); } private void scheduleComponentCreation() { // Create AWT/Swing components on the AWT thread. This is // especially necessary to avoid an AWT leak bug (6411042). ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { @Override public void run() { panel = new JApplet(); panel.setLayout(new GridLayout(1,1,0,0)); frame.add(panel); try { awtComponent = createSwingComponent(); panel.add(awtComponent); } finally { // Needed to support #waitUntilPopulated populated.set(true); if (populationSemaphore != null) populationSemaphore.release(); } } }); } protected abstract Component createSwingComponent(); private static class CleanResizeListener extends ControlAdapter { private Rectangle oldRect = null; @Override public void controlResized(ControlEvent e) { assert e != null; assert Display.getCurrent() != null; // On SWT event thread // Prevent garbage from Swing lags during resize. Fill exposed areas // with background color. Composite composite = (Composite) e.widget; //Rectangle newRect = composite.getBounds(); //newRect = composite.getDisplay().map(composite.getParent(), composite, newRect); Rectangle newRect = composite.getClientArea(); if (oldRect != null) { int heightDelta = newRect.height - oldRect.height; int widthDelta = newRect.width - oldRect.width; if ((heightDelta > 0) || (widthDelta > 0)) { GC gc = new GC(composite); try { gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta); gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height); } finally { gc.dispose(); } } } oldRect = newRect; } } }