]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/example/SWTAWTComponent.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.swing / src / org / simantics / scenegraph / example / SWTAWTComponent.java
diff --git a/bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/example/SWTAWTComponent.java b/bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/example/SWTAWTComponent.java
new file mode 100644 (file)
index 0000000..ae5c684
--- /dev/null
@@ -0,0 +1,248 @@
+/*******************************************************************************\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