--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.scenegraph.example;\r
+\r
+import java.awt.AWTEvent;\r
+import java.awt.Component;\r
+import java.awt.Container;\r
+import java.awt.Frame;\r
+import java.awt.GridLayout;\r
+import java.awt.Toolkit;\r
+import java.awt.event.AWTEventListener;\r
+import java.awt.event.MouseEvent;\r
+import java.util.concurrent.Semaphore;\r
+import java.util.concurrent.TimeUnit;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import javax.swing.JApplet;\r
+\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.awt.SWT_AWT;\r
+import org.eclipse.swt.events.ControlAdapter;\r
+import org.eclipse.swt.events.ControlEvent;\r
+import org.eclipse.swt.graphics.GC;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.simantics.utils.threads.AWTThread;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+\r
+/**\r
+ * <pre>\r
+ * embeddedComposite = new SWTAWTComposite(parent, SWT.NONE) {\r
+ * protected JComponent createSwingComponent() {\r
+ * scrollPane = new JScrollPane();\r
+ * table = new JTable();\r
+ * scrollPane.setViewportView(table);\r
+ * return scrollPane;\r
+ * }\r
+ * };\r
+ * // For asynchronous AWT UI population of the swing components:\r
+ * embeddedComposite.populate();\r
+ * // and optionally you can wait until the AWT UI population\r
+ * // has finished:\r
+ * embeddedComposite.waitUntilPopulated();\r
+ *\r
+ * // OR:\r
+ *\r
+ * // Do both things above in one call to block until the\r
+ * // AWT UI population is complete:\r
+ * embeddedComposite.syncPopulate();\r
+ *\r
+ * // Both methods assume all invocations are made from the SWT display thread.\r
+ * </pre>\r
+ * <p>\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public abstract class SWTAWTComponent extends Composite {\r
+\r
+ private Frame frame;\r
+\r
+ private Component awtComponent;\r
+\r
+ private JApplet panel;\r
+\r
+ private final AtomicBoolean populationStarted = new AtomicBoolean(false);\r
+\r
+ private final AtomicBoolean populated = new AtomicBoolean(false);\r
+\r
+ private final Semaphore populationSemaphore = new Semaphore(0);\r
+\r
+ private static AWTEventListener awtListener = null;\r
+ \r
+ public SWTAWTComponent(Composite parent, int style) {\r
+ super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);\r
+ }\r
+ \r
+ /**\r
+ * Create a global AWTEventListener for focus management.\r
+ * NOTE: There is really no need to dispose this once it's been initialized\r
+ * \r
+ */\r
+ private synchronized void initAWTEventListener() {\r
+ if(awtListener == null) {\r
+ awtListener = new AWTEventListener() {\r
+ public void eventDispatched(AWTEvent e) {\r
+ if(e.getID() == MouseEvent.MOUSE_PRESSED) {\r
+ Object src = e.getSource();\r
+ if(src instanceof Component) {\r
+ ((Component)src).requestFocus();\r
+ }\r
+ }\r
+ }};\r
+ // Execute in AWT thread..\r
+ ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);\r
+ }\r
+ });\r
+ }\r
+ }\r
+\r
+ protected Container getContainer() {\r
+ return panel;\r
+ }\r
+\r
+ public Component getAWTComponent() {\r
+ return awtComponent;\r
+ }\r
+\r
+ @Override\r
+ public void dispose() {\r
+ if (!isDisposed()) {\r
+ frame.dispose();\r
+ super.dispose();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This method must always be called from SWT thread. This prevents the\r
+ * possibility of deadlock (reported between AWT and SWT) by servicing SWT\r
+ * events while waiting for AWT initialization\r
+ */\r
+ public void syncPopulate() {\r
+ populate();\r
+ waitUntilPopulated();\r
+ }\r
+\r
+ /**\r
+ * This method will create an AWT {@link Frame} through {@link SWT_AWT} and\r
+ * schedule AWT canvas initialization into the AWT thread. The AWT thread initialization will release\r
+ */\r
+ public void populate() {\r
+ if (!populationStarted.compareAndSet(false, true))\r
+ throw new IllegalStateException(this + ".populate was invoked multiple times");\r
+\r
+ checkWidget();\r
+ //ITask task = ThreadLogger.getInstance().begin("createFrame");\r
+ createFrame();\r
+ //task.finish();\r
+ scheduleComponentCreation();\r
+ }\r
+\r
+ public void waitUntilPopulated() {\r
+ if (populated.get())\r
+ return;\r
+\r
+ try {\r
+ boolean done = false;\r
+ while (!done) {\r
+ done = populationSemaphore.tryAcquire(10, TimeUnit.MILLISECONDS);\r
+ while (!done && getDisplay().readAndDispatch()) {\r
+ /*\r
+ * Note: readAndDispatch can cause this to be disposed.\r
+ */\r
+ if(isDisposed()) return;\r
+ done = populationSemaphore.tryAcquire();\r
+ }\r
+ }\r
+ } catch (InterruptedException e) {\r
+ throw new Error("EmbeddedSwingComposite population interrupted for class " + this, e);\r
+ }\r
+ }\r
+\r
+ private void createFrame() {\r
+ /*\r
+ * Set a Windows specific AWT property that prevents heavyweight\r
+ * components from erasing their background. Note that this is a global\r
+ * property and cannot be scoped. It might not be suitable for your\r
+ * application.\r
+ */\r
+ System.setProperty("sun.awt.noerasebackground", "true");\r
+\r
+ if (frame==null)\r
+ frame = SWT_AWT.new_Frame(this);\r
+\r
+ // This listener clears garbage during resizing, making it looker much cleaner\r
+ addControlListener(new CleanResizeListener());\r
+ initAWTEventListener();\r
+ }\r
+\r
+ private void scheduleComponentCreation() {\r
+ // Create AWT/Swing components on the AWT thread. This is\r
+ // especially necessary to avoid an AWT leak bug (6411042).\r
+ ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ panel = new JApplet();\r
+ panel.setLayout(new GridLayout(1,1,0,0));\r
+ frame.add(panel);\r
+ try {\r
+ awtComponent = createSwingComponent();\r
+ panel.add(awtComponent);\r
+ } finally {\r
+ // Needed to support #waitUntilPopulated\r
+ populated.set(true);\r
+ if (populationSemaphore != null)\r
+ populationSemaphore.release();\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+ protected abstract Component createSwingComponent();\r
+\r
+ private static class CleanResizeListener extends ControlAdapter {\r
+ private Rectangle oldRect = null;\r
+\r
+ @Override\r
+ public void controlResized(ControlEvent e) {\r
+ assert e != null;\r
+ assert Display.getCurrent() != null; // On SWT event thread\r
+\r
+ // Prevent garbage from Swing lags during resize. Fill exposed areas\r
+ // with background color.\r
+ Composite composite = (Composite) e.widget;\r
+ //Rectangle newRect = composite.getBounds();\r
+ //newRect = composite.getDisplay().map(composite.getParent(), composite, newRect);\r
+ Rectangle newRect = composite.getClientArea();\r
+ if (oldRect != null) {\r
+ int heightDelta = newRect.height - oldRect.height;\r
+ int widthDelta = newRect.width - oldRect.width;\r
+ if ((heightDelta > 0) || (widthDelta > 0)) {\r
+ GC gc = new GC(composite);\r
+ try {\r
+ gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta);\r
+ gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height);\r
+ } finally {\r
+ gc.dispose();\r
+ }\r
+ }\r
+ }\r
+ oldRect = newRect;\r
+ }\r
+ }\r
+\r
+}\r