]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTAWTComponent.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / SWTAWTComponent.java
index 06d8939114accc76a1993f6edca6f5bd851f6371..8031a93abb4f6728feb6a1afbfee586feece68ea 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2013 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
- *     Semantum Oy - workaround for Simantics issue #3518\r
- *******************************************************************************/\r
-package org.simantics.utils.ui;\r
-\r
-import java.awt.AWTEvent;\r
-import java.awt.Component;\r
-import java.awt.Container;\r
-import java.awt.EventQueue;\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.awt.event.WindowAdapter;\r
-import java.awt.event.WindowEvent;\r
-import java.lang.reflect.InvocationTargetException;\r
-import java.lang.reflect.Method;\r
-import java.util.concurrent.Semaphore;\r
-import java.util.concurrent.TimeUnit;\r
-import java.util.concurrent.atomic.AtomicBoolean;\r
-import java.util.function.Consumer;\r
-\r
-import javax.swing.JApplet;\r
-import javax.swing.SwingUtilities;\r
-import javax.swing.UIManager;\r
-import javax.swing.plaf.FontUIResource;\r
-\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.awt.SWT_AWT;\r
-import org.eclipse.swt.events.DisposeEvent;\r
-import org.eclipse.swt.events.DisposeListener;\r
-import org.eclipse.swt.graphics.Font;\r
-import org.eclipse.swt.graphics.FontData;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.simantics.utils.threads.AWTThread;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-import org.simantics.utils.threads.logger.ITask;\r
-import org.simantics.utils.threads.logger.ThreadLogger;\r
-import org.simantics.utils.ui.internal.Activator;\r
-import org.simantics.utils.ui.internal.awt.AwtEnvironment;\r
-import org.simantics.utils.ui.internal.awt.AwtFocusHandler;\r
-import org.simantics.utils.ui.internal.awt.CleanResizeListener;\r
-import org.simantics.utils.ui.internal.awt.EmbeddedChildFocusTraversalPolicy;\r
-import org.simantics.utils.ui.internal.awt.SwtFocusHandler;\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
- *        // OR:\r
- *\r
- *        // Set a callback for asynchronous completion in the AWT thread:\r
- *        embeddedComposite.populate(component -> {\r
-              // AWT components have been created for component\r
- *        });\r
- *\r
- *        // All 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 static class AwtContext {\r
-        private Frame frame;\r
-        private Component swingComponent;\r
-\r
-        AwtContext(Frame frame) {\r
-            assert frame != null;\r
-            this.frame = frame;\r
-        }\r
-\r
-        Frame getFrame() {\r
-            return frame;\r
-        }\r
-\r
-        void setSwingComponent(Component swingComponent) {\r
-            this.swingComponent = swingComponent;\r
-        }\r
-\r
-        Component getSwingComponent() {\r
-            return swingComponent;\r
-        }\r
-\r
-    }\r
-\r
-    private Font                    currentSystemFont;\r
-    private AwtContext              awtContext;\r
-    private AwtFocusHandler         awtHandler;\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 Consumer<SWTAWTComponent> populatedCallback;\r
-\r
-    private static AWTEventListener awtListener         = null;\r
-\r
-    private Listener settingsListener = new Listener() {\r
-        public void handleEvent(Event event) {\r
-            handleSettingsChange();\r
-        }\r
-    };\r
-\r
-    // This listener helps ensure that Swing popup menus are properly dismissed when\r
-    // a menu item off the SWT main menu bar is shown.\r
-    private final Listener menuListener = new Listener() {\r
-        public void handleEvent(Event event) {\r
-            assert awtHandler != null;\r
-            awtHandler.postHidePopups();\r
-        }\r
-    };\r
-\r
-    public SWTAWTComponent(Composite parent, int style) {\r
-        super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);\r
-        getDisplay().addListener(SWT.Settings, settingsListener);\r
-        setLayout(new FillLayout());\r
-        currentSystemFont = getFont();\r
-        this.addDisposeListener(new DisposeListener() {\r
-            @Override\r
-            public void widgetDisposed(DisposeEvent e) {\r
-                doDispose();\r
-            }\r
-        });\r
-    }\r
-\r
-    protected void doDispose() {\r
-        getDisplay().removeListener(SWT.Settings, settingsListener);\r
-        getDisplay().removeFilter(SWT.Show, menuListener);\r
-\r
-        ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                AwtContext ctx = awtContext;\r
-                if (ctx != null) {\r
-                    ctx.frame.dispose();\r
-                }\r
-                awtContext = null;\r
-                if (panel != null) {\r
-                    panel.removeAll();\r
-                    panel = null;\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    static class FocusRepairListener implements AWTEventListener {\r
-        @Override\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
-    }\r
-\r
-    /**\r
-     * Create a global AWTEventListener for focus management.\r
-     * This helps at least with Linux/GTK problems of transferring focus\r
-     * to workbench parts when clicking on AWT screen territory.\r
-     * \r
-     * NOTE: There is really no need to dispose this once it's been initialized.\r
-     * \r
-     * NOTE: must be invoked from AWT thread.\r
-     */\r
-    private static synchronized void initAWTEventListener() {\r
-        if (!AWTThread.getThreadAccess().currentThreadAccess())\r
-            throw new AssertionError("not invoked from AWT thread");\r
-        if (awtListener == null) {\r
-            awtListener = new FocusRepairListener();\r
-            Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);\r
-        }\r
-    }\r
-\r
-    protected Container getContainer() {\r
-        return panel;\r
-    }\r
-\r
-    public Component getAWTComponent() {\r
-        assert awtContext != null;\r
-        return awtContext.getSwingComponent();\r
-    }\r
-\r
-    /**\r
-     * This method must always be called from SWT thread. This method should be\r
-     * used with extreme care since it will block the calling thread (i.e. the\r
-     * SWT thread) while the AWT thread initializes itself by spinning and\r
-     * dispatching SWT events. This diminishes the possibility of deadlock\r
-     * (reported between AWT and SWT) but still all UI's are recommended to use\r
-     * the asynchronous non-blocking UI population offered by\r
-     * {@link #populate(Consumer)}\r
-     * \r
-     * @see #populate(Consumer)\r
-     */\r
-    public void syncPopulate() {\r
-        populate();\r
-        waitUntilPopulated();\r
-    }\r
-\r
-    /**\r
-     * This method must always be called from SWT thread. This will schedule the\r
-     * real AWT component creation into the AWT thread and call the provided\r
-     * asynchronous callback after the UI population is complete.\r
-     * This prevents the possibility of deadlocking.\r
-     */\r
-    public void populate(Consumer<SWTAWTComponent> callback) {\r
-        populate();\r
-        this.populatedCallback = callback;\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. It will not wait\r
-     * for AWT initialization to complete.\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
-    /**\r
-     * Returns the embedded AWT frame. The returned frame is the root of the AWT containment\r
-     * hierarchy for the embedded Swing component. This method can be called from \r
-     * any thread. \r
-     *    \r
-     * @return the embedded frame\r
-     */\r
-    public Frame getFrame() {\r
-        // Intentionally leaving out checkWidget() call. This may need to be called from within user's \r
-        // createSwingComponent() method. Accessing from a non-SWT thread is OK, but we still check\r
-        // for disposal\r
-        if (getDisplay() == null || isDisposed()) {\r
-            SWT.error(SWT.ERROR_WIDGET_DISPOSED);\r
-        }\r
-        AwtContext ctx = awtContext;\r
-        return (ctx != null) ? ctx.getFrame() : null;\r
-    }\r
-\r
-    private void createFrame() {\r
-        assert Display.getCurrent() != null;     // On SWT event thread\r
-\r
-        // Make sure Awt environment is initialized. \r
-        AwtEnvironment.getInstance(getDisplay());\r
-\r
-        if (awtContext != null) {\r
-            final Frame oldFrame = awtContext.getFrame();\r
-            // Schedule disposal of old frame on AWT thread so that there are no problems with\r
-            // already-scheduled operations that have not completed.\r
-            // Note: the implementation of Frame.dispose() would schedule the use of the AWT \r
-            // thread even if it was not done here, but it uses invokeAndWait() which is \r
-            // prone to deadlock (and not necessary for this case). \r
-            EventQueue.invokeLater(new Runnable() {\r
-                public void run() {\r
-                    oldFrame.dispose();\r
-                }\r
-            });\r
-        }\r
-        Frame frame = SWT_AWT.new_Frame(this);\r
-        awtContext = new AwtContext(frame);\r
-\r
-        // See Simantics issue #3518\r
-        workaroundJava7FocusProblem(frame);\r
-\r
-        // Glue the two frameworks together. Do this before anything is added to the frame\r
-        // so that all necessary listeners are in place.\r
-        createFocusHandlers();\r
-\r
-        // This listener clears garbage during resizing, making it looker much cleaner \r
-        addControlListener(new CleanResizeListener());\r
-    }\r
-\r
-    private void workaroundJava7FocusProblem(Frame frame) {\r
-        String ver = System.getProperty("java.version");\r
-        if (ver.startsWith("1.7") || ver.startsWith("1.8")) {\r
-            try {\r
-                frame.addWindowListener(new Java7FocusFixListener(this, frame));\r
-            } catch (SecurityException e) {\r
-                Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
-            } catch (NoSuchMethodException e) {\r
-                Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
-            }\r
-        }\r
-    }\r
-\r
-    static class Java7FocusFixListener extends WindowAdapter {\r
-\r
-        Method shellSetActiveControl;\r
-        Control control;\r
-        Frame frame;\r
-\r
-        public Java7FocusFixListener(Control control, Frame frame) throws NoSuchMethodException, SecurityException {\r
-            this.shellSetActiveControl = Shell.class.getDeclaredMethod("setActiveControl", Control.class);\r
-            this.frame = frame;\r
-            this.control = control;\r
-        }\r
-\r
-        @Override\r
-        public void windowActivated(WindowEvent e) {\r
-            SWTUtils.asyncExec(control, new Runnable() {\r
-                @Override\r
-                public void run() {\r
-                    if (control.isDisposed())\r
-                        return;\r
-                    if (control.getDisplay().getFocusControl() == control) {\r
-                        try {\r
-                            boolean accessible = shellSetActiveControl.isAccessible();\r
-                            if (!accessible)\r
-                                shellSetActiveControl.setAccessible(true);\r
-                            shellSetActiveControl.invoke(control.getShell(), control);\r
-                            if (!accessible)\r
-                                shellSetActiveControl.setAccessible(false);\r
-                        } catch (SecurityException e) {\r
-                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
-                        } catch (IllegalArgumentException e) {\r
-                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
-                        } catch (IllegalAccessException e) {\r
-                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
-                        } catch (InvocationTargetException e) {\r
-                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getCause().getMessage(), e.getCause()));\r
-                        }\r
-                    }\r
-                }\r
-            });\r
-        }\r
-\r
-    }\r
-\r
-    private void createFocusHandlers() {\r
-        assert awtContext != null;\r
-        assert Display.getCurrent() != null;     // On SWT event thread\r
-\r
-        Frame frame = awtContext.getFrame();\r
-        awtHandler = new AwtFocusHandler(frame);   \r
-        SwtFocusHandler swtHandler = new SwtFocusHandler(this);\r
-        awtHandler.setSwtHandler(swtHandler);\r
-        swtHandler.setAwtHandler(awtHandler);\r
-\r
-        // Ensure that AWT pop-ups are dismissed whenever a SWT menu is shown\r
-        getDisplay().addFilter(SWT.Show, menuListener);\r
-\r
-        EmbeddedChildFocusTraversalPolicy policy = new EmbeddedChildFocusTraversalPolicy(awtHandler);\r
-        frame.setFocusTraversalPolicy(policy);\r
-    }\r
-\r
-    private void scheduleComponentCreation() {\r
-        assert awtContext != null;\r
-\r
-        // Create AWT/Swing components on the AWT thread. This is \r
-        // especially necessary to avoid an AWT leak bug (6411042).\r
-        final AwtContext currentContext = awtContext;\r
-        ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                // Make sure AWT focus fix is in place.\r
-                initAWTEventListener();\r
-\r
-                panel = addRootPaneContainer(currentContext.getFrame());\r
-                panel.setLayout(new GridLayout(1,1,0,0));\r
-                try {\r
-                    Component swingComponent = createSwingComponent();\r
-                    currentContext.setSwingComponent(swingComponent);\r
-                    panel.getRootPane().getContentPane().add(swingComponent);\r
-                    //panel.add(swingComponent);\r
-                    setComponentFont();\r
-                } finally {\r
-                    // Needed to support #waitUntilPopulated\r
-                    populated.set(true);\r
-                    if (populationSemaphore != null)\r
-                        populationSemaphore.release();\r
-                    if (populatedCallback != null) {\r
-                        populatedCallback.accept(SWTAWTComponent.this);\r
-                        populatedCallback = null;\r
-                    }\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    /**\r
-     * Adds a root pane container to the embedded AWT frame. Override this to provide your own \r
-     * {@link javax.swing.RootPaneContainer} implementation. In most cases, it is not necessary\r
-     * to override this method.    \r
-     * <p>\r
-     * This method is called from the AWT event thread. \r
-     * <p> \r
-     * If you are defining your own root pane container, make sure that there is at least one\r
-     * heavyweight (AWT) component in the frame's containment hierarchy; otherwise, event \r
-     * processing will not work correctly. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522\r
-     * for more information.  \r
-     *   \r
-     * @param frame the frame to which the root pane container is added \r
-     * @return a non-null Swing component\r
-     */\r
-    protected JApplet addRootPaneContainer(Frame frame) {\r
-        assert EventQueue.isDispatchThread();    // On AWT event thread\r
-        assert frame != null;\r
-\r
-        // It is important to set up the proper top level components in the frame:\r
-        // 1) For Swing to work properly, Sun documents that there must be an implementor of \r
-        // javax.swing.RootPaneContainer at the top of the component hierarchy. \r
-        // 2) For proper event handling there must be a heavyweight \r
-        // an AWT frame must contain a heavyweight component (see \r
-        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522)\r
-        // 3) The Swing implementation further narrows the options by expecting that the \r
-        // top of the hierarchy be a JFrame, JDialog, JWindow, or JApplet. See javax.swing.PopupFactory.\r
-        // All this drives the choice of JApplet for the top level Swing component. It is the \r
-        // only single component that satisfies all the above. This does not imply that \r
-        // we have a true applet; in particular, there is no notion of an applet lifecycle in this\r
-        // context. \r
-        JApplet applet = new JApplet();\r
-        \r
-        // In JRE 1.4, the JApplet makes itself a focus cycle root. This\r
-        // interferes with the focus handling installed on the parent frame, so\r
-        // change it back to a non-root here. \r
-        // TODO: consider moving the focus policy from the Frame down to the JApplet\r
-        applet.setFocusCycleRoot(false);\r
-\r
-        frame.add(applet);\r
-\r
-        return applet;\r
-    }\r
-\r
-    /**\r
-     * Override this to customize what kind of AWT/Swing UI is created by this\r
-     * {@link SWTAWTComponent}.\r
-     * \r
-     * @return the AWT/Swing component created by this SWTAWT bridging control\r
-     * @thread AWT\r
-     */\r
-    protected abstract Component createSwingComponent();\r
-\r
-    private void setComponentFont() {\r
-        assert currentSystemFont != null;\r
-        assert EventQueue.isDispatchThread();    // On AWT event thread\r
-\r
-        Component swingComponent = (awtContext != null) ? awtContext.getSwingComponent() : null;\r
-        if ((swingComponent != null) && !currentSystemFont.getDevice().isDisposed()) {\r
-            FontData fontData = currentSystemFont.getFontData()[0];\r
-            \r
-            // AWT font sizes assume a 72 dpi resolution, always. The true screen resolution must be \r
-            // used to convert the platform font size into an AWT point size that matches when displayed. \r
-            int resolution = Toolkit.getDefaultToolkit().getScreenResolution();\r
-            int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0);\r
-            \r
-            // The style constants for SWT and AWT map exactly, and since they are int constants, they should\r
-            // never change. So, the SWT style is passed through as the AWT style. \r
-            java.awt.Font awtFont = new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize);\r
-\r
-            // Update the look and feel defaults to use new font.\r
-            updateLookAndFeel(awtFont);\r
-\r
-            // Allow subclasses to react to font change if necessary. \r
-            updateAwtFont(awtFont);\r
-\r
-            // Allow components to update their UI based on new font \r
-            // TODO: should the update method be called on the root pane instead?\r
-            Container contentPane = SwingUtilities.getRootPane(swingComponent).getContentPane();\r
-            SwingUtilities.updateComponentTreeUI(contentPane);\r
-        }\r
-    }\r
-\r
-    private void updateLookAndFeel(java.awt.Font awtFont) {\r
-        assert awtFont != null;\r
-        assert EventQueue.isDispatchThread();    // On AWT event thread\r
-\r
-        // The FontUIResource class marks the font as replaceable by the look and feel \r
-        // implementation if font settings are later changed. \r
-        FontUIResource fontResource = new FontUIResource(awtFont);\r
-\r
-        // Assign the new font to the relevant L&F font properties. These are \r
-        // the properties that are initially assigned to the system font\r
-        // under the Windows look and feel. \r
-        // TODO: It's possible that other platforms will need other assignments.\r
-        // TODO: This does not handle fonts other than the "system" font. \r
-        // Other fonts may change, and the Swing L&F may not be adjusting.\r
-\r
-        UIManager.put("Button.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("CheckBox.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("ComboBox.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("EditorPane.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("Label.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("List.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("Panel.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("ProgressBar.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("RadioButton.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("ScrollPane.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("TabbedPane.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("Table.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("TableHeader.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("TextField.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("TextPane.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("TitledBorder.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("ToggleButton.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("TreeFont.font", fontResource); //$NON-NLS-1$\r
-        UIManager.put("ViewportFont.font", fontResource); //$NON-NLS-1$\r
-    }\r
-\r
-    /**\r
-     * Performs custom updates to newly set fonts. This method is called whenever a change\r
-     * to the system font through the system settings (i.e. control panel) is detected.\r
-     * <p>\r
-     * This method is called from the AWT event thread.  \r
-     * <p>\r
-     * In most cases it is not necessary to override this method.  Normally, the implementation\r
-     * of this class will automatically propogate font changes to the embedded Swing components \r
-     * through Swing's Look and Feel support. However, if additional \r
-     * special processing is necessary, it can be done inside this method. \r
-     *    \r
-     * @param newFont New AWT font\r
-     */\r
-    protected void updateAwtFont(java.awt.Font newFont) {\r
-    }\r
-\r
-    private void handleSettingsChange() {\r
-        Font newFont = getDisplay().getSystemFont();\r
-        if (!newFont.equals(currentSystemFont)) { \r
-            currentSystemFont = newFont;\r
-            EventQueue.invokeLater(new Runnable() {\r
-                public void run() {\r
-                    setComponentFont();\r
-                }\r
-            });\r
-        }\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2013 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
+ *     Semantum Oy - workaround for Simantics issue #3518
+ *******************************************************************************/
+package org.simantics.utils.ui;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.GridLayout;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import javax.swing.JApplet;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.FontUIResource;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.awt.SWT_AWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.simantics.utils.threads.AWTThread;
+import org.simantics.utils.threads.ThreadUtils;
+import org.simantics.utils.threads.logger.ITask;
+import org.simantics.utils.threads.logger.ThreadLogger;
+import org.simantics.utils.ui.internal.Activator;
+import org.simantics.utils.ui.internal.awt.AwtEnvironment;
+import org.simantics.utils.ui.internal.awt.AwtFocusHandler;
+import org.simantics.utils.ui.internal.awt.CleanResizeListener;
+import org.simantics.utils.ui.internal.awt.EmbeddedChildFocusTraversalPolicy;
+import org.simantics.utils.ui.internal.awt.SwtFocusHandler;
+
+
+/**
+ * <pre>
+ *        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();
+ *
+ *        // OR:
+ *
+ *        // Set a callback for asynchronous completion in the AWT thread:
+ *        embeddedComposite.populate(component -> {
+              // AWT components have been created for component
+ *        });
+ *
+ *        // All methods assume all invocations are made from the SWT display thread.
+ * </pre>
+ * <p>
+ * 
+ * @author Tuukka Lehtonen
+ */
+public abstract class SWTAWTComponent extends Composite {
+
+    private static class AwtContext {
+        private Frame frame;
+        private Component swingComponent;
+
+        AwtContext(Frame frame) {
+            assert frame != null;
+            this.frame = frame;
+        }
+
+        Frame getFrame() {
+            return frame;
+        }
+
+        void setSwingComponent(Component swingComponent) {
+            this.swingComponent = swingComponent;
+        }
+
+        Component getSwingComponent() {
+            return swingComponent;
+        }
+
+    }
+
+    private Font                    currentSystemFont;
+    private AwtContext              awtContext;
+    private AwtFocusHandler         awtHandler;
+
+    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 Consumer<SWTAWTComponent> populatedCallback;
+
+    private static AWTEventListener awtListener         = null;
+
+    private Listener settingsListener = new Listener() {
+        public void handleEvent(Event event) {
+            handleSettingsChange();
+        }
+    };
+
+    // This listener helps ensure that Swing popup menus are properly dismissed when
+    // a menu item off the SWT main menu bar is shown.
+    private final Listener menuListener = new Listener() {
+        public void handleEvent(Event event) {
+            assert awtHandler != null;
+            awtHandler.postHidePopups();
+        }
+    };
+
+    public SWTAWTComponent(Composite parent, int style) {
+        super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);
+        getDisplay().addListener(SWT.Settings, settingsListener);
+        setLayout(new FillLayout());
+        currentSystemFont = getFont();
+        this.addDisposeListener(new DisposeListener() {
+            @Override
+            public void widgetDisposed(DisposeEvent e) {
+                doDispose();
+            }
+        });
+    }
+
+    protected void doDispose() {
+        getDisplay().removeListener(SWT.Settings, settingsListener);
+        getDisplay().removeFilter(SWT.Show, menuListener);
+
+        ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
+            @Override
+            public void run() {
+                AwtContext ctx = awtContext;
+                if (ctx != null) {
+                    ctx.frame.dispose();
+                }
+                awtContext = null;
+                if (panel != null) {
+                    panel.removeAll();
+                    panel = null;
+                }
+            }
+        });
+    }
+
+    static class FocusRepairListener implements AWTEventListener {
+        @Override
+        public void eventDispatched(AWTEvent e) {
+            if (e.getID() == MouseEvent.MOUSE_PRESSED) {
+                Object src = e.getSource();
+                if (src instanceof Component) {
+                    ((Component) src).requestFocus();
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a global AWTEventListener for focus management.
+     * This helps at least with Linux/GTK problems of transferring focus
+     * to workbench parts when clicking on AWT screen territory.
+     * 
+     * NOTE: There is really no need to dispose this once it's been initialized.
+     * 
+     * NOTE: must be invoked from AWT thread.
+     */
+    private static synchronized void initAWTEventListener() {
+        if (!AWTThread.getThreadAccess().currentThreadAccess())
+            throw new AssertionError("not invoked from AWT thread");
+        if (awtListener == null) {
+            awtListener = new FocusRepairListener();
+            Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);
+        }
+    }
+
+    protected Container getContainer() {
+        return panel;
+    }
+
+    public Component getAWTComponent() {
+        assert awtContext != null;
+        return awtContext.getSwingComponent();
+    }
+
+    /**
+     * This method must always be called from SWT thread. This method should be
+     * used with extreme care since it will block the calling thread (i.e. the
+     * SWT thread) while the AWT thread initializes itself by spinning and
+     * dispatching SWT events. This diminishes the possibility of deadlock
+     * (reported between AWT and SWT) but still all UI's are recommended to use
+     * the asynchronous non-blocking UI population offered by
+     * {@link #populate(Consumer)}
+     * 
+     * @see #populate(Consumer)
+     */
+    public void syncPopulate() {
+        populate();
+        waitUntilPopulated();
+    }
+
+    /**
+     * This method must always be called from SWT thread. This will schedule the
+     * real AWT component creation into the AWT thread and call the provided
+     * asynchronous callback after the UI population is complete.
+     * This prevents the possibility of deadlocking.
+     */
+    public void populate(Consumer<SWTAWTComponent> callback) {
+        populate();
+        this.populatedCallback = callback;
+    }
+
+    /**
+     * This method will create an AWT {@link Frame} through {@link SWT_AWT} and
+     * schedule AWT canvas initialization into the AWT thread. It will not wait
+     * for AWT initialization to complete.
+     */
+    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);
+        }
+    }
+
+    /**
+     * Returns the embedded AWT frame. The returned frame is the root of the AWT containment
+     * hierarchy for the embedded Swing component. This method can be called from 
+     * any thread. 
+     *    
+     * @return the embedded frame
+     */
+    public Frame getFrame() {
+        // Intentionally leaving out checkWidget() call. This may need to be called from within user's 
+        // createSwingComponent() method. Accessing from a non-SWT thread is OK, but we still check
+        // for disposal
+        if (getDisplay() == null || isDisposed()) {
+            SWT.error(SWT.ERROR_WIDGET_DISPOSED);
+        }
+        AwtContext ctx = awtContext;
+        return (ctx != null) ? ctx.getFrame() : null;
+    }
+
+    private void createFrame() {
+        assert Display.getCurrent() != null;     // On SWT event thread
+
+        // Make sure Awt environment is initialized. 
+        AwtEnvironment.getInstance(getDisplay());
+
+        if (awtContext != null) {
+            final Frame oldFrame = awtContext.getFrame();
+            // Schedule disposal of old frame on AWT thread so that there are no problems with
+            // already-scheduled operations that have not completed.
+            // Note: the implementation of Frame.dispose() would schedule the use of the AWT 
+            // thread even if it was not done here, but it uses invokeAndWait() which is 
+            // prone to deadlock (and not necessary for this case). 
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    oldFrame.dispose();
+                }
+            });
+        }
+        Frame frame = SWT_AWT.new_Frame(this);
+        awtContext = new AwtContext(frame);
+
+        // See Simantics issue #3518
+        workaroundJava7FocusProblem(frame);
+
+        // Glue the two frameworks together. Do this before anything is added to the frame
+        // so that all necessary listeners are in place.
+        createFocusHandlers();
+
+        // This listener clears garbage during resizing, making it looker much cleaner 
+        addControlListener(new CleanResizeListener());
+    }
+
+    private void workaroundJava7FocusProblem(Frame frame) {
+        String ver = System.getProperty("java.version");
+        if (ver.startsWith("1.7") || ver.startsWith("1.8")) {
+            try {
+                frame.addWindowListener(new Java7FocusFixListener(this, frame));
+            } catch (SecurityException e) {
+                Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
+            } catch (NoSuchMethodException e) {
+                Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
+            }
+        }
+    }
+
+    static class Java7FocusFixListener extends WindowAdapter {
+
+        Method shellSetActiveControl;
+        Control control;
+        Frame frame;
+
+        public Java7FocusFixListener(Control control, Frame frame) throws NoSuchMethodException, SecurityException {
+            this.shellSetActiveControl = Shell.class.getDeclaredMethod("setActiveControl", Control.class);
+            this.frame = frame;
+            this.control = control;
+        }
+
+        @Override
+        public void windowActivated(WindowEvent e) {
+            SWTUtils.asyncExec(control, new Runnable() {
+                @Override
+                public void run() {
+                    if (control.isDisposed())
+                        return;
+                    if (control.getDisplay().getFocusControl() == control) {
+                        try {
+                            boolean accessible = shellSetActiveControl.isAccessible();
+                            if (!accessible)
+                                shellSetActiveControl.setAccessible(true);
+                            shellSetActiveControl.invoke(control.getShell(), control);
+                            if (!accessible)
+                                shellSetActiveControl.setAccessible(false);
+                        } catch (SecurityException e) {
+                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
+                        } catch (IllegalArgumentException e) {
+                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
+                        } catch (IllegalAccessException e) {
+                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
+                        } catch (InvocationTargetException e) {
+                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getCause().getMessage(), e.getCause()));
+                        }
+                    }
+                }
+            });
+        }
+
+    }
+
+    private void createFocusHandlers() {
+        assert awtContext != null;
+        assert Display.getCurrent() != null;     // On SWT event thread
+
+        Frame frame = awtContext.getFrame();
+        awtHandler = new AwtFocusHandler(frame);   
+        SwtFocusHandler swtHandler = new SwtFocusHandler(this);
+        awtHandler.setSwtHandler(swtHandler);
+        swtHandler.setAwtHandler(awtHandler);
+
+        // Ensure that AWT pop-ups are dismissed whenever a SWT menu is shown
+        getDisplay().addFilter(SWT.Show, menuListener);
+
+        EmbeddedChildFocusTraversalPolicy policy = new EmbeddedChildFocusTraversalPolicy(awtHandler);
+        frame.setFocusTraversalPolicy(policy);
+    }
+
+    private void scheduleComponentCreation() {
+        assert awtContext != null;
+
+        // Create AWT/Swing components on the AWT thread. This is 
+        // especially necessary to avoid an AWT leak bug (6411042).
+        final AwtContext currentContext = awtContext;
+        ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
+            @Override
+            public void run() {
+                // Make sure AWT focus fix is in place.
+                initAWTEventListener();
+
+                panel = addRootPaneContainer(currentContext.getFrame());
+                panel.setLayout(new GridLayout(1,1,0,0));
+                try {
+                    Component swingComponent = createSwingComponent();
+                    currentContext.setSwingComponent(swingComponent);
+                    panel.getRootPane().getContentPane().add(swingComponent);
+                    //panel.add(swingComponent);
+                    setComponentFont();
+                } finally {
+                    // Needed to support #waitUntilPopulated
+                    populated.set(true);
+                    if (populationSemaphore != null)
+                        populationSemaphore.release();
+                    if (populatedCallback != null) {
+                        populatedCallback.accept(SWTAWTComponent.this);
+                        populatedCallback = null;
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Adds a root pane container to the embedded AWT frame. Override this to provide your own 
+     * {@link javax.swing.RootPaneContainer} implementation. In most cases, it is not necessary
+     * to override this method.    
+     * <p>
+     * This method is called from the AWT event thread. 
+     * <p> 
+     * If you are defining your own root pane container, make sure that there is at least one
+     * heavyweight (AWT) component in the frame's containment hierarchy; otherwise, event 
+     * processing will not work correctly. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522
+     * for more information.  
+     *   
+     * @param frame the frame to which the root pane container is added 
+     * @return a non-null Swing component
+     */
+    protected JApplet addRootPaneContainer(Frame frame) {
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+        assert frame != null;
+
+        // It is important to set up the proper top level components in the frame:
+        // 1) For Swing to work properly, Sun documents that there must be an implementor of 
+        // javax.swing.RootPaneContainer at the top of the component hierarchy. 
+        // 2) For proper event handling there must be a heavyweight 
+        // an AWT frame must contain a heavyweight component (see 
+        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522)
+        // 3) The Swing implementation further narrows the options by expecting that the 
+        // top of the hierarchy be a JFrame, JDialog, JWindow, or JApplet. See javax.swing.PopupFactory.
+        // All this drives the choice of JApplet for the top level Swing component. It is the 
+        // only single component that satisfies all the above. This does not imply that 
+        // we have a true applet; in particular, there is no notion of an applet lifecycle in this
+        // context. 
+        JApplet applet = new JApplet();
+        
+        // In JRE 1.4, the JApplet makes itself a focus cycle root. This
+        // interferes with the focus handling installed on the parent frame, so
+        // change it back to a non-root here. 
+        // TODO: consider moving the focus policy from the Frame down to the JApplet
+        applet.setFocusCycleRoot(false);
+
+        frame.add(applet);
+
+        return applet;
+    }
+
+    /**
+     * Override this to customize what kind of AWT/Swing UI is created by this
+     * {@link SWTAWTComponent}.
+     * 
+     * @return the AWT/Swing component created by this SWTAWT bridging control
+     * @thread AWT
+     */
+    protected abstract Component createSwingComponent();
+
+    private void setComponentFont() {
+        assert currentSystemFont != null;
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+
+        Component swingComponent = (awtContext != null) ? awtContext.getSwingComponent() : null;
+        if ((swingComponent != null) && !currentSystemFont.getDevice().isDisposed()) {
+            FontData fontData = currentSystemFont.getFontData()[0];
+            
+            // AWT font sizes assume a 72 dpi resolution, always. The true screen resolution must be 
+            // used to convert the platform font size into an AWT point size that matches when displayed. 
+            int resolution = Toolkit.getDefaultToolkit().getScreenResolution();
+            int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0);
+            
+            // The style constants for SWT and AWT map exactly, and since they are int constants, they should
+            // never change. So, the SWT style is passed through as the AWT style. 
+            java.awt.Font awtFont = new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize);
+
+            // Update the look and feel defaults to use new font.
+            updateLookAndFeel(awtFont);
+
+            // Allow subclasses to react to font change if necessary. 
+            updateAwtFont(awtFont);
+
+            // Allow components to update their UI based on new font 
+            // TODO: should the update method be called on the root pane instead?
+            Container contentPane = SwingUtilities.getRootPane(swingComponent).getContentPane();
+            SwingUtilities.updateComponentTreeUI(contentPane);
+        }
+    }
+
+    private void updateLookAndFeel(java.awt.Font awtFont) {
+        assert awtFont != null;
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+
+        // The FontUIResource class marks the font as replaceable by the look and feel 
+        // implementation if font settings are later changed. 
+        FontUIResource fontResource = new FontUIResource(awtFont);
+
+        // Assign the new font to the relevant L&F font properties. These are 
+        // the properties that are initially assigned to the system font
+        // under the Windows look and feel. 
+        // TODO: It's possible that other platforms will need other assignments.
+        // TODO: This does not handle fonts other than the "system" font. 
+        // Other fonts may change, and the Swing L&F may not be adjusting.
+
+        UIManager.put("Button.font", fontResource); //$NON-NLS-1$
+        UIManager.put("CheckBox.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ComboBox.font", fontResource); //$NON-NLS-1$
+        UIManager.put("EditorPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("Label.font", fontResource); //$NON-NLS-1$
+        UIManager.put("List.font", fontResource); //$NON-NLS-1$
+        UIManager.put("Panel.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ProgressBar.font", fontResource); //$NON-NLS-1$
+        UIManager.put("RadioButton.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ScrollPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TabbedPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("Table.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TableHeader.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TextField.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TextPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TitledBorder.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ToggleButton.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TreeFont.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ViewportFont.font", fontResource); //$NON-NLS-1$
+    }
+
+    /**
+     * Performs custom updates to newly set fonts. This method is called whenever a change
+     * to the system font through the system settings (i.e. control panel) is detected.
+     * <p>
+     * This method is called from the AWT event thread.  
+     * <p>
+     * In most cases it is not necessary to override this method.  Normally, the implementation
+     * of this class will automatically propogate font changes to the embedded Swing components 
+     * through Swing's Look and Feel support. However, if additional 
+     * special processing is necessary, it can be done inside this method. 
+     *    
+     * @param newFont New AWT font
+     */
+    protected void updateAwtFont(java.awt.Font newFont) {
+    }
+
+    private void handleSettingsChange() {
+        Font newFont = getDisplay().getSystemFont();
+        if (!newFont.equals(currentSystemFont)) { 
+            currentSystemFont = newFont;
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    setComponentFont();
+                }
+            });
+        }
+    }
+
+}