--- old/./build.gradle 2018-07-10 15:04:46.812098600 +0530 +++ new/./build.gradle 2018-07-10 15:04:45.843975700 +0530 @@ -615,14 +615,18 @@ // Determine whether the javafx.* modules are present in the JDK. To do this, -// we will execute "java --list-modules" and search for javafx.base +// we will execute "java --list-modules" and search for javafx.base. +// Also, check if jdk.unsupported.desktop module is present in JDK to ensure +// javafx.swing do not use internal JDK classes directly. ext.HAS_JAVAFX_MODULES = false; +ext.HAS_UNSUPPORTED_DESKTOP = false; def inStream2 = new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder(JAVA, "--list-modules").start().getInputStream())); try { String v; while ((v = inStream2.readLine()) != null) { v = v.trim(); if (v.startsWith("javafx.base")) ext.HAS_JAVAFX_MODULES = true; + if (v.startsWith("jdk.unsupported.desktop")) ext.HAS_UNSUPPORTED_DESKTOP = true; } } finally { inStream2.close(); @@ -1298,6 +1302,7 @@ logger.quiet("minimum jdk version: ${jfxBuildJdkVersionMin}") logger.quiet("minimum jdk build number: ${jfxBuildJdkBuildnumMin}") logger.quiet("HAS_JAVAFX_MODULES: $HAS_JAVAFX_MODULES") +logger.quiet("HAS_UNSUPPORTED_DESKTOP: $HAS_UNSUPPORTED_DESKTOP") logger.quiet("STUB_RUNTIME: $STUB_RUNTIME") logger.quiet("CONF: $CONF") logger.quiet("NUM_COMPILE_THREADS: $NUM_COMPILE_THREADS") @@ -2330,6 +2335,21 @@ } } + sourceSets.main.java.srcDirs += "$buildDir/gensrc/java" + sourceSets.main.java { + if (!HAS_UNSUPPORTED_DESKTOP) { + exclude("com/sun/javafx/embed/swing/newimpl/**") + } + } + task checkJDKUnsupportedModule(type: Copy, description: "copy module-info file to gensrc") { + from "src/main/module-info/module-info.java" + into "$buildDir/gensrc/java/" + filter { line-> + !HAS_UNSUPPORTED_DESKTOP && line.contains('jdk.unsupported.desktop') ? null : line + } + } + compileJava.dependsOn checkJDKUnsupportedModule + compileJava.options.compilerArgs.addAll(qualExportsSwing) } @@ -4777,7 +4797,7 @@ def copySourceFilesTask = project.task("copySourceFiles$t.capital", type: Copy, dependsOn: copyLibFilesTask) { if (copySources) { from "${project.projectDir}/src/main/java" - if (project.name.equals("base")) { + if (project.name.equals("base") || project.name.equals("swing")) { from "${project.projectDir}/build/gensrc/java" } if (project.name.equals("web")) { @@ -4788,6 +4808,10 @@ } into "${modulesSrcDir}/${moduleName}" include "**/*.java" + if (!HAS_UNSUPPORTED_DESKTOP) { + exclude("com/sun/javafx/embed/swing/newimpl/**") + } + if (project.hasProperty("sourceFilter")) { filter(project.sourceFilter) } --- old/modules/javafx.swing/src/main/java/module-info.java 2018-07-10 15:04:52.073766800 +0530 +++ /dev/null 2018-07-10 15:04:52.000000000 +0530 @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * Defines APIs for the JavaFX / Swing interop support included with the - * JavaFX UI toolkit, including {@link javafx.embed.swing.SwingNode} (for - * embedding Swing inside a JavaFX application) and - * {@link javafx.embed.swing.JFXPanel} (for embedding JavaFX inside a Swing - * application). - * - * @moduleGraph - * @since 9 - */ -module javafx.swing { - requires java.datatransfer; - requires javafx.base; - - requires transitive java.desktop; - requires transitive javafx.graphics; - - exports javafx.embed.swing; - - exports com.sun.javafx.embed.swing to - javafx.graphics; -} --- /dev/null 2018-07-10 15:04:52.000000000 +0530 +++ new/modules/javafx.swing/src/main/module-info/module-info.java 2018-07-10 15:04:50.702592700 +0530 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Defines APIs for the JavaFX / Swing interop support included with the + * JavaFX UI toolkit, including {@link javafx.embed.swing.SwingNode} (for + * embedding Swing inside a JavaFX application) and + * {@link javafx.embed.swing.JFXPanel} (for embedding JavaFX inside a Swing + * application). + * + * @moduleGraph + * @since 9 + */ +module javafx.swing { + requires java.datatransfer; + requires javafx.base; + + requires transitive java.desktop; + requires static jdk.unsupported.desktop; + requires transitive javafx.graphics; + + exports javafx.embed.swing; + + exports com.sun.javafx.embed.swing to + javafx.graphics; +} --- /dev/null 2018-07-10 15:04:57.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/InteropFactory.java 2018-07-10 15:04:56.350809900 +0530 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.lang.reflect.Constructor; + +public abstract class InteropFactory { + private static InteropFactory instance = null; + private static boolean verbose = false; + + static { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + verbose = Boolean.valueOf( + System.getProperty("javafx.embed.swing.verbose")); + return null; + } + }); + } + + private static final String[] factoryNames = { + "com.sun.javafx.embed.swing.newimpl.InteropFactoryN", + "com.sun.javafx.embed.swing.oldimpl.InteropFactoryO" + }; + + public synchronized static InteropFactory getInstance() throws Exception { + if (instance != null) return instance; + + Class factoryClass = null; + for (String factoryName : factoryNames) { + try { + factoryClass = Class.forName(factoryName); + Constructor cons = factoryClass.getConstructor(); + instance = cons.newInstance(); + return instance; + } catch (Exception e) { + System.err.println("InteropFactory: cannot load " + factoryName); + if (verbose) { + e.printStackTrace(); + } + } + } + throw new Exception("No swing interop factory can be loaded"); + } + + public abstract SwingNodeInterop createSwingNodeImpl(); + public abstract JFXPanelInterop createJFXPanelImpl(); + public abstract FXDnDInterop createFXDnDImpl(); + public abstract SwingFXUtilsImplInterop createSwingFXUtilsImpl(); +} + --- /dev/null 2018-07-10 15:05:01.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/newimpl/InteropFactoryN.java 2018-07-10 15:04:59.846753800 +0530 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.newimpl; + +import com.sun.javafx.embed.swing.InteropFactory; +import com.sun.javafx.embed.swing.SwingNodeInterop; +import com.sun.javafx.embed.swing.JFXPanelInterop; +import com.sun.javafx.embed.swing.FXDnDInterop; +import com.sun.javafx.embed.swing.SwingFXUtilsImplInterop; + +public class InteropFactoryN extends InteropFactory { + + public InteropFactoryN() throws Exception { + Class swiopClass = Class.forName("jdk.swing.interop.LightweightFrameWrapper"); + } + + @Override + public SwingNodeInterop createSwingNodeImpl() { + return new SwingNodeInteropN(); + } + + @Override + public JFXPanelInterop createJFXPanelImpl() { + return new JFXPanelInteropN(); + } + + @Override + public FXDnDInterop createFXDnDImpl() { + return new FXDnDInteropN(); + } + + @Override + public SwingFXUtilsImplInterop createSwingFXUtilsImpl() { + return new SwingFXUtilsImplInteropN(); + } +} --- /dev/null 2018-07-10 15:05:04.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/oldimpl/InteropFactoryO.java 2018-07-10 15:05:03.391704000 +0530 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.oldimpl; + +import com.sun.javafx.embed.swing.InteropFactory; +import com.sun.javafx.embed.swing.SwingNodeInterop; +import com.sun.javafx.embed.swing.JFXPanelInterop; +import com.sun.javafx.embed.swing.FXDnDInterop; +import com.sun.javafx.embed.swing.SwingFXUtilsImplInterop; + +public class InteropFactoryO extends InteropFactory { + + public InteropFactoryO() throws Exception{ + Class qeSwingClass = Class.forName("sun.swing.JLightweightFrame"); + } + + @Override + public SwingNodeInterop createSwingNodeImpl() { + return new SwingNodeInteropO(); + } + + @Override + public JFXPanelInterop createJFXPanelImpl() { + return new JFXPanelInteropO(); + } + + @Override + public FXDnDInterop createFXDnDImpl() { + return new FXDnDInteropO(); + } + + @Override + public SwingFXUtilsImplInterop createSwingFXUtilsImpl() { + return new SwingFXUtilsImplInteropO(); + } +} --- /dev/null 2018-07-10 15:05:08.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingNodeInterop.java 2018-07-10 15:05:07.381710700 +0530 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import javafx.scene.Node; +import javafx.event.EventHandler; +import java.awt.AWTEvent; +import com.sun.javafx.geom.BaseBounds; +import com.sun.javafx.geom.transform.BaseTransform; +import com.sun.javafx.sg.prism.NGNode; +import javax.swing.JComponent; +import java.awt.event.MouseWheelEvent; +import javafx.scene.input.ScrollEvent; +import java.awt.event.WindowFocusListener; +import javafx.stage.Window; +import javafx.scene.Scene; +import com.sun.javafx.embed.swing.DisposerRecord; +import javafx.geometry.Point2D; +import java.util.concurrent.locks.ReentrantLock; + +import javafx.embed.swing.SwingNode; + +public abstract class SwingNodeInterop { + + public abstract Object createLightweightFrame(); + public abstract Object getLightweightFrame(); + public abstract void disposeFrame(Object frame); + + public abstract java.awt.event.MouseEvent createMouseEvent(Object lwFrame, + int swingID, long swingWhen, int swingModifiers, + int relX, int relY, int absX, int absY, + int clickCount, boolean swingPopupTrigger, + int swingButton); + + public abstract MouseWheelEvent createMouseWheelEvent(Object lwFrame, + int swingModifiers, int x, int y, int wheelRotation); + + public abstract java.awt.event.KeyEvent createKeyEvent(Object lwFrame, + int swingID, long swingWhen, + int swingModifiers, + int swingKeyCode, char swingChar); + + public abstract AWTEvent createUngrabEvent(Object lwFrame); + + public abstract void overrideNativeWindowHandle(long handle, Runnable closeWindow); + public abstract void addWindowFocusListener(Object frame, WindowFocusListener l); + public abstract void notifyDisplayChanged(Object frame, double scaleX, double scaleY); + public abstract void setHostBounds(Object frame, int windowX, int windowY, + int windowW, int windowH); + public abstract void setContent(Object frame, Object content); + public abstract void setVisible(Object frame, boolean visible); + public abstract void setBounds(Object frame, int frameX, int frameY, int frameW, int frameH); + public abstract void emulateActivation(Object frame, boolean activate); + + public abstract Object createSwingNodeContent(JComponent content, SwingNode node); + + public abstract DisposerRecord createSwingNodeDisposer(Object lwFrame); + +} --- /dev/null 2018-07-10 15:05:11.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/newimpl/SwingNodeInteropN.java 2018-07-10 15:05:10.812146300 +0530 @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.newimpl; + +import javafx.geometry.Point2D; +import javafx.beans.InvalidationListener; +import javafx.beans.value.ChangeListener; +import javafx.stage.Window; +import javafx.scene.Scene; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.ScrollEvent; +import java.awt.event.MouseWheelEvent; +import javafx.event.EventHandler; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.awt.Component; +import java.awt.AWTEvent; +import java.awt.Cursor; +import java.nio.IntBuffer; +import java.awt.event.WindowFocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.InvalidDnDOperationException; +import javax.swing.JComponent; +import javax.swing.Timer; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import com.sun.javafx.stage.WindowHelper; +import com.sun.javafx.stage.FocusUngrabEvent; +import com.sun.javafx.sg.prism.NGNode; +import com.sun.javafx.sg.prism.NGExternalNode; +import com.sun.javafx.geom.BaseBounds; +import com.sun.javafx.geom.transform.BaseTransform; +import com.sun.javafx.scene.DirtyBits; +import com.sun.javafx.embed.swing.DisposerRecord; +import javafx.embed.swing.SwingNode; +import com.sun.javafx.PlatformUtil; +import com.sun.javafx.scene.NodeHelper; + +import com.sun.javafx.util.Utils; +import jdk.swing.interop.LightweightFrameWrapper; +import jdk.swing.interop.LightweightContentWrapper; +import jdk.swing.interop.DragSourceContextWrapper; +import com.sun.javafx.embed.swing.SwingNodeInterop; +import com.sun.javafx.embed.swing.FXDnD; +import com.sun.javafx.embed.swing.SwingCursors; +import com.sun.javafx.embed.swing.SwingNodeHelper; + +public class SwingNodeInteropN extends SwingNodeInterop { + + private volatile LightweightFrameWrapper lwFrame; + + /** + * Calls LightweightFrameWrapper.notifyDisplayChanged. + * Must be called on EDT only. + */ + private static OptionalMethod jlfNotifyDisplayChanged; + private static Class lwFrameWrapperClass = null; + private static native void overrideNativeWindowHandle(Class lwFrameWrapperClass, long handle, + Runnable closeWindow); + static { + jlfNotifyDisplayChanged = new OptionalMethod<>(LightweightFrameWrapper.class, + "notifyDisplayChanged", Double.TYPE, Double.TYPE); + if (!jlfNotifyDisplayChanged.isSupported()) { + jlfNotifyDisplayChanged = new OptionalMethod<>( + LightweightFrameWrapper.class,"notifyDisplayChanged", Integer.TYPE); + } + + try { + lwFrameWrapperClass = Class.forName("jdk.swing.interop.LightweightFrameWrapper"); + } catch (Throwable t) {} + + Utils.loadNativeSwingLibrary(); + } + + public LightweightFrameWrapper createLightweightFrame() { + lwFrame = new LightweightFrameWrapper(); + return lwFrame; + } + + public LightweightFrameWrapper getLightweightFrame() { return lwFrame; } + + public MouseEvent createMouseEvent(Object frame, + int swingID, long swingWhen, int swingModifiers, + int relX, int relY, int absX, int absY, + int clickCount, boolean swingPopupTrigger, + int swingButton) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + return lwFrame.createMouseEvent(lwFrame, swingID, + swingWhen, swingModifiers, relX, relY, absX, absY, + clickCount, swingPopupTrigger, swingButton); + } + + public MouseWheelEvent createMouseWheelEvent(Object frame, + int swingModifiers, int x, int y, int wheelRotation) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + return lwFrame.createMouseWheelEvent(lwFrame, + swingModifiers, x, y, wheelRotation); + } + + public java.awt.event.KeyEvent createKeyEvent(Object frame, + int swingID, long swingWhen, + int swingModifiers, + int swingKeyCode, char swingChar) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + return lwFrame.createKeyEvent(lwFrame, swingID, + swingWhen, swingModifiers, swingKeyCode, + swingChar); + } + + public AWTEvent createUngrabEvent(Object frame) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + return lwFrame.createUngrabEvent(lwFrame); + } + + public void overrideNativeWindowHandle(long handle, Runnable closeWindow) { + overrideNativeWindowHandle(lwFrameWrapperClass, handle, closeWindow); + } + + public void notifyDisplayChanged(Object frame, double scaleX, double scaleY) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + if (jlfNotifyDisplayChanged.isIntegerApi()) { + jlfNotifyDisplayChanged.invoke(lwFrame, + (int) Math.round(scaleX)); + } else { + jlfNotifyDisplayChanged.invoke(lwFrame, + scaleX, scaleY); + } + } + + public void setHostBounds(Object frame, int windowX, int windowY, int windowW, int windowH) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + jlfSetHostBounds.invoke(lwFrame, windowX, windowY, windowW, windowH); + } + + public void setContent(Object frame, Object cnt) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + LightweightContentWrapper content = (LightweightContentWrapper)cnt; + lwFrame.setContent(content); + } + + public void setVisible(Object frame, boolean visible) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + lwFrame.setVisible(visible); + } + + public void setBounds(Object frame, int frameX, int frameY, int frameW, int frameH) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + lwFrame.setBounds(frameX, frameY, frameW, frameH); + } + + public LightweightContentWrapper createSwingNodeContent(JComponent content, SwingNode node) { + return (LightweightContentWrapper)new SwingNodeContent(content, node); + } + + public DisposerRecord createSwingNodeDisposer(Object frame) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + return new SwingNodeDisposer(lwFrame); + } + + private static final class OptionalMethod { + private final Method method; + private final boolean isIntegerAPI; + + OptionalMethod(Class cls, String name, Class... args) { + Method m; + try { + m = cls.getMethod(name, args); + } catch (NoSuchMethodException ignored) { + // This means we're running with older JDK, simply skip the call + m = null; + } catch (Throwable ex) { + throw new RuntimeException("Error when calling " + cls.getName() + ".getMethod('" + name + "').", ex); + } + method = m; + isIntegerAPI = args != null && args.length > 0 && + args[0] == Integer.TYPE; + } + + public boolean isSupported() { + return method != null; + } + + public boolean isIntegerApi() { + return isIntegerAPI; + } + + public Object invoke(T object, Object... args) { + if (method != null) { + try { + return method.invoke(object, args); + } catch (Throwable ex) { + throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex); + } + } else { + return null; + } + } + } + + /** + * Calls LightweightFrameWrapper.setHostBounds. + * Must be called on EDT only. + */ + private static final OptionalMethod jlfSetHostBounds = + new OptionalMethod<>(LightweightFrameWrapper.class, "setHostBounds", + Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); + + public void emulateActivation(Object frame, boolean activate) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + lwFrame.emulateActivation(activate); + } + + public void disposeFrame(Object frame) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + lwFrame.dispose(); + } + + public void addWindowFocusListener(Object frame, WindowFocusListener l) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)frame; + lwFrame.addWindowFocusListener(l); + } + + private static class SwingNodeDisposer implements DisposerRecord { + LightweightFrameWrapper lwFrame; + + SwingNodeDisposer(LightweightFrameWrapper ref) { + this.lwFrame = ref; + } + public void dispose() { + if (lwFrame != null) { + lwFrame.dispose(); + lwFrame = null; + } + } + } + + private static class SwingNodeContent extends LightweightContentWrapper { + private JComponent comp; + private volatile FXDnD dnd; + private WeakReference swingNodeRef; + + SwingNodeContent(JComponent comp, SwingNode swingNode) { + this.comp = comp; + this.swingNodeRef = new WeakReference(swingNode); + } + @Override + public JComponent getComponent() { + return comp; + } + @Override + public void paintLock() { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.getPaintLock(swingNode).lock(); + } + } + @Override + public void paintUnlock() { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.getPaintLock(swingNode).unlock(); + } + } + + @Override + public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) { + imageBufferReset(data, x, y, width, height, linestride, 1); + } + //@Override + public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scale, scale); + } + } + @Override + public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scaleX, scaleY); + } + } + @Override + public void imageReshaped(int x, int y, int width, int height) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setImageBounds(swingNode, x, y, width, height); + } + } + @Override + public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.repaintDirtyRegion(swingNode, dirtyX, dirtyY, dirtyWidth, dirtyHeight); + } + } + @Override + public void focusGrabbed() { + SwingNodeHelper.runOnFxThread(() -> { + // On X11 grab is limited to a single XDisplay connection, + // so we can't delegate it to another GUI toolkit. + if (PlatformUtil.isLinux()) return; + + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + Scene scene = swingNode.getScene(); + if (scene != null && + scene.getWindow() != null && + WindowHelper.getPeer(scene.getWindow()) != null) { + WindowHelper.getPeer(scene.getWindow()).grabFocus(); + SwingNodeHelper.setGrabbed(swingNode, true); + } + } + }); + } + @Override + public void focusUngrabbed() { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + Scene scene = swingNode.getScene(); + SwingNodeHelper.ungrabFocus(swingNode, false); + } + }); + } + @Override + public void preferredSizeChanged(final int width, final int height) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setSwingPrefWidth(swingNode, width); + SwingNodeHelper.setSwingPrefHeight(swingNode, height); + NodeHelper.notifyLayoutBoundsChanged(swingNode); + } + }); + } + @Override + public void maximumSizeChanged(final int width, final int height) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setSwingMaxWidth(swingNode, width); + SwingNodeHelper.setSwingMaxHeight(swingNode, height); + NodeHelper.notifyLayoutBoundsChanged(swingNode); + } + }); + } + @Override + public void minimumSizeChanged(final int width, final int height) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setSwingMinWidth(swingNode, width); + SwingNodeHelper.setSwingMinHeight(swingNode, height); + NodeHelper.notifyLayoutBoundsChanged(swingNode); + } + }); + } + + //@Override + public void setCursor(Cursor cursor) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor)); + } + }); + } + + private void initDnD() { + // This is a part of AWT API, so the method may be invoked on any thread + synchronized (SwingNodeContent.this) { + if (this.dnd == null) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + this.dnd = new FXDnD(swingNode); + } + } + } + } + + @Override + public synchronized T createDragGestureRecognizer( + Class abstractRecognizerClass, + DragSource ds, Component c, int srcActions, + DragGestureListener dgl) + { + initDnD(); + return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl); + } + + @Override + public DragSourceContextWrapper createDragSourceContext(DragGestureEvent dge) throws InvalidDnDOperationException + { + initDnD(); + return (DragSourceContextWrapper)dnd.createDragSourceContext(dge); + } + + @Override + public void addDropTarget(DropTarget dt) { + initDnD(); + dnd.addDropTarget(dt); + } + + @Override + public void removeDropTarget(DropTarget dt) { + initDnD(); + dnd.removeDropTarget(dt); + } + } + +} --- /dev/null 2018-07-10 15:05:16.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/oldimpl/SwingNodeInteropO.java 2018-07-10 15:05:14.586125500 +0530 @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.oldimpl; + +import javafx.geometry.Point2D; +import javafx.beans.InvalidationListener; +import javafx.beans.value.ChangeListener; +import javafx.stage.Window; +import javafx.scene.Scene; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.ScrollEvent; +import java.awt.event.MouseWheelEvent; +import javafx.event.EventHandler; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.awt.Component; +import java.awt.AWTEvent; +import java.awt.Cursor; +import java.nio.IntBuffer; +import java.awt.event.WindowFocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.peer.DragSourceContextPeer; +import javax.swing.JComponent; +import javax.swing.Timer; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import com.sun.javafx.stage.WindowHelper; +import com.sun.javafx.stage.FocusUngrabEvent; +import com.sun.javafx.sg.prism.NGNode; +import com.sun.javafx.sg.prism.NGExternalNode; +import com.sun.javafx.geom.BaseBounds; +import com.sun.javafx.geom.transform.BaseTransform; +import com.sun.javafx.scene.DirtyBits; +import com.sun.javafx.embed.swing.DisposerRecord; +import javafx.embed.swing.SwingNode; +import com.sun.javafx.PlatformUtil; +import com.sun.javafx.scene.NodeHelper; + +import sun.awt.UngrabEvent; +import sun.swing.JLightweightFrame; +import sun.swing.LightweightContent; +import com.sun.javafx.embed.swing.SwingNodeInterop; +import com.sun.javafx.embed.swing.FXDnD; +import com.sun.javafx.embed.swing.SwingCursors; +import com.sun.javafx.embed.swing.SwingEvents; +import com.sun.javafx.embed.swing.SwingNodeHelper; + +public class SwingNodeInteropO extends SwingNodeInterop { + + private volatile JLightweightFrame lwFrame; + + /** + * Calls LightweightFrameWrapper.notifyDisplayChanged. + * Must be called on EDT only. + */ + private static OptionalMethod jlfNotifyDisplayChanged; + private static OptionalMethod jlfOverrideNativeWindowHandle; + + static { + jlfNotifyDisplayChanged = new OptionalMethod<>(JLightweightFrame.class, + "notifyDisplayChanged", Double.TYPE, Double.TYPE); + if (!jlfNotifyDisplayChanged.isSupported()) { + jlfNotifyDisplayChanged = new OptionalMethod<>( + JLightweightFrame.class,"notifyDisplayChanged", Integer.TYPE); + } + + jlfOverrideNativeWindowHandle = new OptionalMethod<>(JLightweightFrame.class, + "overrideNativeWindowHandle", Long.TYPE, Runnable.class); + } + + public Object createLightweightFrame() { + lwFrame = new JLightweightFrame(); + return lwFrame; + } + + public JLightweightFrame getLightweightFrame() { return lwFrame; } + + public MouseEvent createMouseEvent(Object frame, + int swingID, long swingWhen, int swingModifiers, + int relX, int relY, int absX, int absY, + int clickCount, boolean swingPopupTrigger, + int swingButton) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + return new java.awt.event.MouseEvent(lwFrame, swingID, + swingWhen, swingModifiers, relX, relY, absX, absY, + clickCount, swingPopupTrigger, swingButton); + } + + public MouseWheelEvent createMouseWheelEvent(Object frame, + int swingModifiers, int x, int y, int wheelRotation) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + return new MouseWheelEvent(lwFrame, java.awt.event.MouseEvent.MOUSE_WHEEL, + System.currentTimeMillis(), swingModifiers, + x, y, 0, 0, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, + 1, wheelRotation); + } + + public java.awt.event.KeyEvent createKeyEvent(Object frame, + int swingID, long swingWhen, + int swingModifiers, + int swingKeyCode, char swingChar) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + return new java.awt.event.KeyEvent(lwFrame, swingID, + swingWhen, swingModifiers, swingKeyCode, + swingChar); + } + + public AWTEvent createUngrabEvent(Object frame) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + return new UngrabEvent(lwFrame); + } + + public void overrideNativeWindowHandle(long handle, Runnable closeWindow) { + jlfOverrideNativeWindowHandle.invoke(lwFrame, handle, closeWindow); + } + + public void notifyDisplayChanged(Object frame, double scaleX, double scaleY) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + if (jlfNotifyDisplayChanged.isIntegerApi()) { + jlfNotifyDisplayChanged.invoke(lwFrame, (int) scaleX); + } else { + jlfNotifyDisplayChanged.invoke(lwFrame, scaleX, scaleY); + } + } + + public void setHostBounds(Object frame, int windowX, int windowY, int windowW, int windowH) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + jlfSetHostBounds.invoke(lwFrame, windowX, windowY, windowW, windowH); + } + + public void setContent(Object frame, Object cnt) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + LightweightContent content = (LightweightContent)cnt; + lwFrame.setContent(content); + } + + public void setVisible(Object frame, boolean visible) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + lwFrame.setVisible(visible); + } + + public void setBounds(Object frame, int frameX, int frameY, int frameW, int frameH) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + lwFrame.setBounds(frameX, frameY, frameW, frameH); + } + + public SwingNodeContent createSwingNodeContent(JComponent content, SwingNode node) { + return new SwingNodeContent(content, node); + } + + public DisposerRecord createSwingNodeDisposer(Object frame) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + return new SwingNodeDisposer(lwFrame); + } + + private static final class OptionalMethod { + private final Method method; + private final boolean isIntegerAPI; + + OptionalMethod(Class cls, String name, Class... args) { + Method m; + try { + m = cls.getMethod(name, args); + } catch (NoSuchMethodException ignored) { + // This means we're running with older JDK, simply skip the call + m = null; + } catch (Throwable ex) { + throw new RuntimeException("Error when calling " + cls.getName() + ".getMethod('" + name + "').", ex); + } + method = m; + isIntegerAPI = args != null && args.length > 0 && + args[0] == Integer.TYPE; + } + + public boolean isSupported() { + return method != null; + } + + public boolean isIntegerApi() { + return isIntegerAPI; + } + + public Object invoke(T object, Object... args) { + if (method != null) { + try { + return method.invoke(object, args); + } catch (Throwable ex) { + throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex); + } + } else { + return null; + } + } + } + + /** + * Calls LightweightFrameWrapper.setHostBounds. + * Must be called on EDT only. + */ + private static final OptionalMethod jlfSetHostBounds = + new OptionalMethod<>(JLightweightFrame.class, "setHostBounds", + Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); + + public void emulateActivation(Object frame, boolean activate) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + lwFrame.emulateActivation(activate); + } + + public void disposeFrame(Object frame) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + lwFrame.dispose(); + } + + public void addWindowFocusListener(Object frame, WindowFocusListener l) { + JLightweightFrame lwFrame = (JLightweightFrame)frame; + lwFrame.addWindowFocusListener(l); + } + + private static class SwingNodeDisposer implements DisposerRecord { + JLightweightFrame lwFrame; + + SwingNodeDisposer(JLightweightFrame ref) { + this.lwFrame = ref; + } + public void dispose() { + if (lwFrame != null) { + lwFrame.dispose(); + lwFrame = null; + } + } + } + + private static class SwingNodeContent implements LightweightContent { + private JComponent comp; + private volatile FXDnD dnd; + private WeakReference swingNodeRef; + + SwingNodeContent(JComponent comp, SwingNode swingNode) { + this.comp = comp; + this.swingNodeRef = new WeakReference(swingNode); + } + @Override + public JComponent getComponent() { + return comp; + } + @Override + public void paintLock() { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.getPaintLock(swingNode).lock(); + } + } + @Override + public void paintUnlock() { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.getPaintLock(swingNode).unlock(); + } + } + + @Override + public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) { + imageBufferReset(data, x, y, width, height, linestride, 1); + } + //@Override + public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scale, scale); + } + } + @Override + public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setImageBuffer(swingNode, data, x, y, width, height, linestride, scaleX, scaleY); + } + } + @Override + public void imageReshaped(int x, int y, int width, int height) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setImageBounds(swingNode, x, y, width, height); + } + } + @Override + public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.repaintDirtyRegion(swingNode, dirtyX, dirtyY, dirtyWidth, dirtyHeight); + } + } + @Override + public void focusGrabbed() { + SwingNodeHelper.runOnFxThread(() -> { + // On X11 grab is limited to a single XDisplay connection, + // so we can't delegate it to another GUI toolkit. + if (PlatformUtil.isLinux()) return; + + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + Scene scene = swingNode.getScene(); + if (scene != null && + scene.getWindow() != null && + WindowHelper.getPeer(scene.getWindow()) != null) { + WindowHelper.getPeer(scene.getWindow()).grabFocus(); + SwingNodeHelper.setGrabbed(swingNode, true); + } + } + }); + } + @Override + public void focusUngrabbed() { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + Scene scene = swingNode.getScene(); + SwingNodeHelper.ungrabFocus(swingNode, false); + } + }); + } + @Override + public void preferredSizeChanged(final int width, final int height) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setSwingPrefWidth(swingNode, width); + SwingNodeHelper.setSwingPrefHeight(swingNode, height); + NodeHelper.notifyLayoutBoundsChanged(swingNode); + } + }); + } + @Override + public void maximumSizeChanged(final int width, final int height) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setSwingMaxWidth(swingNode, width); + SwingNodeHelper.setSwingMaxHeight(swingNode, height); + NodeHelper.notifyLayoutBoundsChanged(swingNode); + } + }); + } + @Override + public void minimumSizeChanged(final int width, final int height) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + SwingNodeHelper.setSwingMinWidth(swingNode, width); + SwingNodeHelper.setSwingMinHeight(swingNode, height); + NodeHelper.notifyLayoutBoundsChanged(swingNode); + } + }); + } + + //@Override + public void setCursor(Cursor cursor) { + SwingNodeHelper.runOnFxThread(() -> { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor)); + } + }); + } + + private void initDnD() { + // This is a part of AWT API, so the method may be invoked on any thread + synchronized (SwingNodeContent.this) { + if (this.dnd == null) { + SwingNode swingNode = swingNodeRef.get(); + if (swingNode != null) { + this.dnd = new FXDnD(swingNode); + } + } + } + } + + //@Override + public synchronized T createDragGestureRecognizer( + Class abstractRecognizerClass, + DragSource ds, Component c, int srcActions, + DragGestureListener dgl) + { + initDnD(); + return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl); + } + + //@Override + public DragSourceContextPeer createDragSourceContext(DragGestureEvent dge) throws InvalidDnDOperationException + { + initDnD(); + return (DragSourceContextPeer)dnd.createDragSourceContext(dge); + } + + //@Override + public void addDropTarget(DropTarget dt) { + initDnD(); + dnd.addDropTarget(dt); + } + + //@Override + public void removeDropTarget(DropTarget dt) { + initDnD(); + dnd.removeDropTarget(dt); + } + } + +} --- /dev/null 2018-07-10 15:05:19.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/JFXPanelInterop.java 2018-07-10 15:05:18.453116500 +0530 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import javafx.embed.swing.JFXPanel; +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.awt.Window; + +public abstract class JFXPanelInterop { + public abstract void postEvent(JFXPanel panel, AWTEvent e); + + public abstract boolean isUngrabEvent(AWTEvent event); + + public abstract long setMask(); + + public abstract void grab(Toolkit toolkit, Window w); + + public abstract void ungrab(Toolkit toolkit, Window w); +} --- /dev/null 2018-07-10 15:05:23.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/newimpl/JFXPanelInteropN.java 2018-07-10 15:05:21.783539500 +0530 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.newimpl; + +import javafx.embed.swing.JFXPanel; +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.awt.Window; + +import jdk.swing.interop.SwingInterOpUtils; +import com.sun.javafx.embed.swing.JFXPanelInterop; + +public class JFXPanelInteropN extends JFXPanelInterop { + public void postEvent(JFXPanel panel, AWTEvent e) { + SwingInterOpUtils.postEvent(panel, e); + } + + public boolean isUngrabEvent(AWTEvent event) { + return SwingInterOpUtils.isUngrabEvent(event); + } + + public long setMask() { + return SwingInterOpUtils.GRAB_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK; + } + + public void grab(Toolkit toolkit, Window w) { + SwingInterOpUtils.grab(toolkit, w); + } + + public void ungrab(Toolkit toolkit, Window w) { + SwingInterOpUtils.ungrab(toolkit, w); + } +} --- /dev/null 2018-07-10 15:05:27.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/oldimpl/JFXPanelInteropO.java 2018-07-10 15:05:25.740541900 +0530 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.oldimpl; + +import javafx.embed.swing.JFXPanel; +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.awt.Window; + +import sun.awt.AppContext; +import sun.awt.SunToolkit; +import com.sun.javafx.embed.swing.JFXPanelInterop; + +public class JFXPanelInteropO extends JFXPanelInterop { + public void postEvent(JFXPanel panel, AWTEvent e) { + AppContext context = SunToolkit.targetToAppContext(panel); + if (context != null) { + SunToolkit.postEvent(context, e); + } + } + + public boolean isUngrabEvent(AWTEvent event) { + return event instanceof sun.awt.UngrabEvent; + } + + public long setMask() { + return SunToolkit.GRAB_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK; + } + + public void grab(Toolkit toolkit, Window w) { + if (toolkit instanceof SunToolkit) { + ((SunToolkit)toolkit).grab(w); + } + } + + public void ungrab(Toolkit toolkit, Window w) { + if (toolkit instanceof SunToolkit) { + ((SunToolkit)toolkit).ungrab(w); + } + } +} --- /dev/null 2018-07-10 15:05:30.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/FXDnDInterop.java 2018-07-10 15:05:29.280491500 +0530 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import java.awt.Component; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import javafx.embed.swing.SwingNode; + +public abstract class FXDnDInterop { + public abstract Component findComponentAt(Object frame, int x, int y, + boolean ignoreEnabled); + + public abstract boolean isCompEqual(Component c, Object frame); + + public abstract int convertModifiersToDropAction(int modifiers, + int supportedActions); + + public abstract Object createDragSourceContext(DragGestureEvent dge); + + public abstract T + createDragGestureRecognizer(DragSource ds, Component c, int srcActions, + DragGestureListener dgl); + + public abstract void addDropTarget(DropTarget dt, SwingNode node); + + public abstract void removeDropTarget(DropTarget dt, SwingNode node); + + public abstract void setNode(SwingNode node); +} --- /dev/null 2018-07-10 15:05:34.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/newimpl/FXDnDInteropN.java 2018-07-10 15:05:32.661420800 +0530 @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.newimpl; + +import com.sun.javafx.tk.Toolkit; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Point; +import java.awt.SecondaryLoop; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; +import javafx.application.Platform; +import javafx.event.EventHandler; +import javafx.event.EventType; +import javafx.scene.input.DataFormat; +import javafx.scene.input.DragEvent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TransferMode; + +import com.sun.javafx.embed.swing.FXDnDInterop; +import com.sun.javafx.embed.swing.CachingTransferable; +import com.sun.javafx.embed.swing.SwingEvents; +import com.sun.javafx.embed.swing.SwingDnD; +import com.sun.javafx.embed.swing.FXDnD; +import com.sun.javafx.embed.swing.SwingNodeHelper; +import javafx.embed.swing.SwingNode; +import jdk.swing.interop.LightweightFrameWrapper; +import jdk.swing.interop.SwingInterOpUtils; +import jdk.swing.interop.DragSourceContextWrapper; +import jdk.swing.interop.DropTargetContextWrapper; + +public class FXDnDInteropN extends FXDnDInterop { + + public Component findComponentAt(Object frame, int x, int y, + boolean ignoreEnabled) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper) frame; + return lwFrame.findComponentAt(lwFrame, x, y, false); + } + + public boolean isCompEqual(Component c, Object frame) { + LightweightFrameWrapper lwFrame = (LightweightFrameWrapper) frame; + return lwFrame.isCompEqual(c,lwFrame); + } + + public int convertModifiersToDropAction(int modifiers, + int supportedActions) { + return DragSourceContextWrapper.convertModifiersToDropAction(modifiers, + supportedActions); + } + + public Object createDragSourceContext(DragGestureEvent dge) + throws InvalidDnDOperationException { + return new FXDragSourceContextPeer(dge); + } + + public T createDragGestureRecognizer( + DragSource ds, Component c, int srcActions, + DragGestureListener dgl) { + return (T) new FXDragGestureRecognizer(ds, c, srcActions, dgl); + } + + private void runOnFxThread(Runnable runnable) { + if (Platform.isFxApplicationThread()) { + runnable.run(); + } else { + Platform.runLater(runnable); + } + } + + private SwingNode getNode() { + return node; + } + + public void setNode(SwingNode swnode) { + node = swnode; + } + + private SwingNode node = null; + + /** + * Utility class that operates on Maps with Components as keys. + * Useful when processing mouse events to choose an object from the map + * based on the component located at the given coordinates. + */ + private class ComponentMapper { + public int x, y; + public T object = null; + + private ComponentMapper(Map map, int xArg, int yArg) { + this.x = xArg; + this.y = yArg; + + final LightweightFrameWrapper lwFrame = (LightweightFrameWrapper)SwingNodeHelper.getLightweightFrame(node); + Component c = lwFrame.findComponentAt(lwFrame, x, y, false); + if (c == null) return; + + synchronized (c.getTreeLock()) { + do { + object = map.get(c); + } while (object == null && (c = c.getParent()) != null); + + if (object != null) { + // The object is either a DropTarget or a DragSource, so: + //assert c == object.getComponent(); + + // Translate x, y from lwFrame to component coordinates + while ((lwFrame.isCompEqual(c,lwFrame)) && c != null) { + x -= c.getX(); + y -= c.getY(); + c = c.getParent(); + } + } + } + } + } + + public ComponentMapper mapComponent(Map map, int x, int y) { + return new ComponentMapper(map, x, y); + } + + /////////////////////////////////////////////////////////////////////////// + // DRAG SOURCE IMPLEMENTATION + /////////////////////////////////////////////////////////////////////////// + + + private boolean isDragSourceListenerInstalled = false; + + // To keep track of where the DnD gesture actually started + private MouseEvent pressEvent = null; + private long pressTime = 0; + + private volatile SecondaryLoop loop; + + private final Map recognizers = new HashMap<>(); + + // Note that we don't really use the MouseDragGestureRecognizer facilities, + // however some code in JDK may expect a descendant of this class rather + // than a generic DragGestureRecognizer. So we inherit from it. + private class FXDragGestureRecognizer extends MouseDragGestureRecognizer { + FXDragGestureRecognizer(DragSource ds, Component c, int srcActions, + DragGestureListener dgl) + { + super(ds, c, srcActions, dgl); + + if (c != null) recognizers.put(c, this); + } + + @Override public void setComponent(Component c) { + final Component old = getComponent(); + if (old != null) recognizers.remove(old); + super.setComponent(c); + if (c != null) recognizers.put(c, this); + } + + protected void registerListeners() { + runOnFxThread(() -> { + if (!isDragSourceListenerInstalled) { + node.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); + node.addEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); + node.addEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); + + isDragSourceListenerInstalled = true; + } + }); + } + + protected void unregisterListeners() { + runOnFxThread(() -> { + if (isDragSourceListenerInstalled) { + node.removeEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); + node.removeEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); + node.removeEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); + + isDragSourceListenerInstalled = false; + } + }); + } + + private void fireEvent(int x, int y, long evTime, int modifiers) { + // In theory we should register all the events that trigger the gesture (like PRESS, DRAG, DRAG, BINGO!) + // But we can live with this hack for now. + appendEvent(new java.awt.event.MouseEvent(getComponent(), java.awt.event.MouseEvent.MOUSE_PRESSED, + evTime, modifiers, x, y, 0, false)); + + // Also, the modifiers here should've actually come from the last known mouse event (last MOVE or DRAG). + // But we're OK with using the initial PRESS modifiers for now + int initialAction = DragSourceContextWrapper.convertModifiersToDropAction( + modifiers, getSourceActions()); + + fireDragGestureRecognized(initialAction, new java.awt.Point(x, y)); + } + } + + // Invoked on EDT + private void fireEvent(int x, int y, long evTime, int modifiers) { + ComponentMapper mapper = mapComponent(recognizers, x, y); + + final FXDragGestureRecognizer r = mapper.object; + if (r != null) { + r.fireEvent(mapper.x, mapper.y, evTime, modifiers); + } else { + // No recognizer, no DnD, no startDrag, so release the FX loop now + SwingNodeHelper.leaveFXNestedLoop(this); + } + } + + private MouseEvent getInitialGestureEvent() { + return pressEvent; + } + + private final EventHandler onMousePressHandler = (event) -> { + // It would be nice to maintain a list of all the events that initiate + // a DnD gesture (see a comment in FXDragGestureRecognizer.fireEvent(). + // For now, we simply use the initial PRESS event for this purpose. + pressEvent = event; + pressTime = System.currentTimeMillis(); + }; + + + private volatile FXDragSourceContextPeer activeDSContextPeer; + + private final EventHandler onDragStartHandler = (event) -> { + // Call to AWT and determine the active DragSourceContextPeer + activeDSContextPeer = null; + final MouseEvent firstEv = getInitialGestureEvent(); + SwingNodeHelper.runOnEDTAndWait(FXDnDInteropN.this, () -> fireEvent( + (int)firstEv.getX(), (int)firstEv.getY(), pressTime, + SwingEvents.fxMouseModsToMouseMods(firstEv))); + if (activeDSContextPeer == null) return; + + // Since we're going to start DnD, consume the event. + event.consume(); + + Dragboard db = getNode().startDragAndDrop(SwingDnD.dropActionsToTransferModes( + activeDSContextPeer.sourceActions).toArray(new TransferMode[1])); + + // At this point the activeDSContextPeer.transferable contains all the data from AWT + Map fxData = new HashMap<>(); + for (String mt : activeDSContextPeer.transferable.getMimeTypes()) { + DataFormat f = DataFormat.lookupMimeType(mt); + //TODO: what to do if f == null? + if (f != null) fxData.put(f, activeDSContextPeer.transferable.getData(mt)); + } + + final boolean hasContent = db.setContent(fxData); + if (!hasContent) { + // No data, no DnD, no onDragDoneHandler, so release the AWT loop now + if (!FXDnD.fxAppThreadIsDispatchThread) { + loop.exit(); + } + } + }; + + private final EventHandler onDragDoneHandler = (event) -> { + event.consume(); + + // Release FXDragSourceContextPeer.startDrag() + if (!FXDnD.fxAppThreadIsDispatchThread) { + loop.exit(); + } + + if (activeDSContextPeer != null) { + final TransferMode mode = event.getTransferMode(); + activeDSContextPeer.dragDone( + mode == null ? 0 : SwingDnD.transferModeToDropAction(mode), + (int)event.getX(), (int)event.getY()); + } + }; + + + private final class FXDragSourceContextPeer extends DragSourceContextWrapper { + private volatile int sourceActions = 0; + + private final CachingTransferable transferable = new CachingTransferable(); + + @Override public void startSecondaryEventLoop(){ + Toolkit.getToolkit().enterNestedEventLoop(this); + } + @Override public void quitSecondaryEventLoop(){ + assert !Platform.isFxApplicationThread(); + Platform.runLater(() -> Toolkit.getToolkit().exitNestedEventLoop(FXDragSourceContextPeer.this, null)); + } + + @Override protected void setNativeCursor(Cursor c, int cType) { + //TODO + } + + + private void dragDone(int operation, int x, int y) { + dragDropFinished(operation != 0, operation, x, y); + } + + FXDragSourceContextPeer(DragGestureEvent dge) { + super(dge); + } + + + // It's Map actually, but javac complains if the type isn't erased... + @Override protected void startDrag(Transferable trans, long[] formats, Map formatMap) + { + activeDSContextPeer = this; + + // NOTE: we ignore the formats[] and the formatMap altogether. + // AWT provides those to allow for more flexible representations of + // e.g. text data (in various formats, encodings, etc.) However, FX + // code isn't ready to handle those (e.g. it can't digest a + // StringReader as data, etc.) So instead we perform our internal + // translation. + // Note that fetchData == true. FX doesn't support delayed data + // callbacks yet anyway, so we have to fetch all the data from AWT upfront. + transferable.updateData(trans, true); + + sourceActions = getDragSourceContext().getSourceActions(); + + // Release the FX nested loop to allow onDragDetected to start the actual DnD operation, + // and then start an AWT nested loop to wait until DnD finishes. + if (!FXDnD.fxAppThreadIsDispatchThread) { + loop = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); + SwingNodeHelper.leaveFXNestedLoop(FXDnDInteropN.this); + if (!loop.enter()) { + // An error occured, but there's little we can do here... + } + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + // DROP TARGET IMPLEMENTATION + /////////////////////////////////////////////////////////////////////////// + + + private boolean isDropTargetListenerInstalled = false; + private volatile FXDropTargetContextPeer activeDTContextPeer = null; + private final Map dropTargets = new HashMap<>(); + + private final EventHandler onDragEnteredHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + int action = activeDTContextPeer.postDropTargetEvent(event); + + // If AWT doesn't accept anything, let parent nodes handle the event + if (action != 0) event.consume(); + }; + + private final EventHandler onDragExitedHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + activeDTContextPeer.postDropTargetEvent(event); + + activeDTContextPeer = null; + }; + + private final EventHandler onDragOverHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + int action = activeDTContextPeer.postDropTargetEvent(event); + + // If AWT doesn't accept anything, let parent nodes handle the event + if (action != 0) { + // NOTE: in FX the acceptTransferModes() may ONLY be called from DRAG_OVER. + // If the AWT app always reports NONE and suddenly decides to accept the + // data in its DRAG_DROPPED handler, this just won't work. There's no way + // to workaround this other than by modifing the AWT application code. + event.acceptTransferModes(SwingDnD.dropActionsToTransferModes(action).toArray(new TransferMode[1])); + event.consume(); + } + }; + + private final EventHandler onDragDroppedHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + int action = activeDTContextPeer.postDropTargetEvent(event); + + if (action != 0) { + // NOTE: the dropAction is ignored since we use the action last + // reported from the DRAG_OVER handler. + // + // We might want to: + // + // assert activeDTContextPeer.dropAction == onDragDroppedHandler.currentAction; + // + // and maybe print a diagnostic message if they differ. + event.setDropCompleted(activeDTContextPeer.success); + + event.consume(); + } + + activeDTContextPeer = null; + }; + + private final class FXDropTargetContextPeer extends DropTargetContextWrapper { + + private int targetActions = DnDConstants.ACTION_NONE; + private int currentAction = DnDConstants.ACTION_NONE; + private DropTarget dt = null; + private DropTargetContext ctx = null; + + private final CachingTransferable transferable = new CachingTransferable(); + + // Drop result + private boolean success = false; + private int dropAction = 0; + + @Override public synchronized void setTargetActions(int actions) { targetActions = actions; } + @Override public synchronized int getTargetActions() { return targetActions; } + + @Override public synchronized DropTarget getDropTarget() { return dt; } + + @Override public synchronized boolean isTransferableJVMLocal() { return false; } + + @Override public synchronized DataFlavor[] getTransferDataFlavors() { return transferable.getTransferDataFlavors(); } + @Override public synchronized Transferable getTransferable() { return transferable; } + + @Override public synchronized void acceptDrag(int dragAction) { currentAction = dragAction; } + @Override public synchronized void rejectDrag() { currentAction = DnDConstants.ACTION_NONE; } + + @Override public synchronized void acceptDrop(int dropAction) { this.dropAction = dropAction; } + @Override public synchronized void rejectDrop() { dropAction = DnDConstants.ACTION_NONE; } + + @Override public synchronized void dropComplete(boolean success) { this.success = success; } + + + private int postDropTargetEvent(DragEvent event) + { + ComponentMapper mapper = mapComponent(dropTargets, (int)event.getX(), (int)event.getY()); + + final EventType fxEvType = event.getEventType(); + + Dragboard db = event.getDragboard(); + transferable.updateData(db, DragEvent.DRAG_DROPPED.equals(fxEvType)); + + final int sourceActions = SwingDnD.transferModesToDropActions(db.getTransferModes()); + final int userAction = event.getTransferMode() == null ? DnDConstants.ACTION_NONE + : SwingDnD.transferModeToDropAction(event.getTransferMode()); + + // A target for the AWT DnD event + DropTarget target = mapper.object != null ? mapper.object : dt; + + SwingNodeHelper.runOnEDTAndWait(FXDnDInteropN.this, () -> { + if (target != dt) { + if (ctx != null) { + this.reset(ctx); + } + ctx = null; + + currentAction = dropAction = DnDConstants.ACTION_NONE; + } + + if (target != null) { + if (ctx == null) { + ctx = target.getDropTargetContext(); + this.setDropTargetContext(ctx, + FXDropTargetContextPeer.this); + } + + DropTargetListener dtl = (DropTargetListener)target; + + if (DragEvent.DRAG_DROPPED.equals(fxEvType)) { + DropTargetDropEvent awtEvent = new DropTargetDropEvent( + ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); + + dtl.drop(awtEvent); + } else { + DropTargetDragEvent awtEvent = new DropTargetDragEvent( + ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); + + if (DragEvent.DRAG_OVER.equals(fxEvType)) dtl.dragOver(awtEvent); + else if (DragEvent.DRAG_ENTERED.equals(fxEvType)) dtl.dragEnter(awtEvent); + else if (DragEvent.DRAG_EXITED.equals(fxEvType)) dtl.dragExit(awtEvent); + } + } + + dt = mapper.object; + if (dt == null) { + // FIXME: once we switch to JDK 9 as the boot JDK + // we need to re-implement the following using + // available API. + /* + if (ctx != null) ctx.removeNotify(); + */ + ctx = null; + + currentAction = dropAction = DnDConstants.ACTION_NONE; + } + if (DragEvent.DRAG_DROPPED.equals(fxEvType) || DragEvent.DRAG_EXITED.equals(fxEvType)) { + // This must be done to ensure that the data isn't being + // cached in AWT. Otherwise subsequent DnD operations will + // see the old data only. + // FIXME: once we switch to JDK 9 as the boot JDK + // we need to re-implement the following using + // available API. + /* + if (ctx != null) ctx.removeNotify(); + */ + ctx = null; + } + + SwingNodeHelper.leaveFXNestedLoop(FXDnDInteropN.this); + }); + + if (DragEvent.DRAG_DROPPED.equals(fxEvType)) return dropAction; + + return currentAction; + } + } + + public void addDropTarget(DropTarget dt, SwingNode node) { + dropTargets.put(dt.getComponent(), dt); + Platform.runLater(() -> { + if (!isDropTargetListenerInstalled) { + node.addEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); + node.addEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); + node.addEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); + node.addEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); + + isDropTargetListenerInstalled = true; + } + }); + } + + public void removeDropTarget(DropTarget dt, SwingNode node) { + dropTargets.remove(dt.getComponent()); + Platform.runLater(() -> { + if (isDropTargetListenerInstalled && dropTargets.isEmpty()) { + node.removeEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); + node.removeEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); + node.removeEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); + node.removeEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); + + isDropTargetListenerInstalled = false; + } + }); + } +} --- /dev/null 2018-07-10 15:05:37.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/oldimpl/FXDnDInteropO.java 2018-07-10 15:05:36.630924800 +0530 @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.oldimpl; + +import com.sun.javafx.tk.Toolkit; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Point; +import java.awt.SecondaryLoop; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.dnd.peer.DropTargetContextPeer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; +import javafx.application.Platform; +import javafx.event.EventHandler; +import javafx.event.EventType; +import javafx.scene.input.DataFormat; +import javafx.scene.input.DragEvent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TransferMode; +import sun.awt.AWTAccessor; +import sun.awt.dnd.SunDragSourceContextPeer; +import sun.swing.JLightweightFrame; + +import com.sun.javafx.embed.swing.FXDnDInterop; +import com.sun.javafx.embed.swing.CachingTransferable; +import com.sun.javafx.embed.swing.SwingDnD; +import com.sun.javafx.embed.swing.FXDnD; +import com.sun.javafx.embed.swing.SwingEvents; +import com.sun.javafx.embed.swing.SwingNodeHelper; +import javafx.embed.swing.SwingNode; + +public class FXDnDInteropO extends FXDnDInterop { + + public Component findComponentAt(Object frame, int x, int y, + boolean ignoreEnabled) { + JLightweightFrame lwFrame = (JLightweightFrame) frame; + return AWTAccessor.getContainerAccessor().findComponentAt(lwFrame, + x, y, false); + } + + public boolean isCompEqual(Component c, Object frame) { + JLightweightFrame lwFrame = (JLightweightFrame) frame; + return c != lwFrame; + } + + public int convertModifiersToDropAction(int modifiers, + int supportedActions) { + return SunDragSourceContextPeer.convertModifiersToDropAction(modifiers, + supportedActions); + } + + public T createDragGestureRecognizer( + DragSource ds, Component c, int srcActions, + DragGestureListener dgl) { + return (T) new FXDragGestureRecognizer(ds, c, srcActions, dgl); + } + + public DragSourceContextPeer createDragSourceContext(DragGestureEvent dge) + throws InvalidDnDOperationException { + return new FXDragSourceContextPeer(dge); + } + + private void runOnFxThread(Runnable runnable) { + if (Platform.isFxApplicationThread()) { + runnable.run(); + } else { + Platform.runLater(runnable); + } + } + + private SwingNode getNode() { + return node; + } + + public void setNode(SwingNode swnode) { + node = swnode; + } + + private SwingNode node = null; + + /** + * Utility class that operates on Maps with Components as keys. + * Useful when processing mouse events to choose an object from the map + * based on the component located at the given coordinates. + */ + private class ComponentMapper { + public int x, y; + public T object = null; + + private ComponentMapper(Map map, int xArg, int yArg) { + this.x = xArg; + this.y = yArg; + + final JLightweightFrame lwFrame = (JLightweightFrame)SwingNodeHelper.getLightweightFrame(node); + Component c = AWTAccessor.getContainerAccessor().findComponentAt(lwFrame, x, y, false); + if (c == null) return; + + synchronized (c.getTreeLock()) { + do { + object = map.get(c); + } while (object == null && (c = c.getParent()) != null); + + if (object != null) { + // The object is either a DropTarget or a DragSource, so: + //assert c == object.getComponent(); + + // Translate x, y from lwFrame to component coordinates + while (c != lwFrame && c != null) { + x -= c.getX(); + y -= c.getY(); + c = c.getParent(); + } + } + } + } + } + + public ComponentMapper mapComponent(Map map, int x, int y) { + return new ComponentMapper(map, x, y); + } + + /////////////////////////////////////////////////////////////////////////// + // DRAG SOURCE IMPLEMENTATION + /////////////////////////////////////////////////////////////////////////// + + + private boolean isDragSourceListenerInstalled = false; + + // To keep track of where the DnD gesture actually started + private MouseEvent pressEvent = null; + private long pressTime = 0; + + private volatile SecondaryLoop loop; + + private final Map recognizers = new HashMap<>(); + + // Note that we don't really use the MouseDragGestureRecognizer facilities, + // however some code in JDK may expect a descendant of this class rather + // than a generic DragGestureRecognizer. So we inherit from it. + private class FXDragGestureRecognizer extends MouseDragGestureRecognizer { + FXDragGestureRecognizer(DragSource ds, Component c, int srcActions, + DragGestureListener dgl) + { + super(ds, c, srcActions, dgl); + + if (c != null) recognizers.put(c, this); + } + + @Override public void setComponent(Component c) { + final Component old = getComponent(); + if (old != null) recognizers.remove(old); + super.setComponent(c); + if (c != null) recognizers.put(c, this); + } + + protected void registerListeners() { + runOnFxThread(() -> { + if (!isDragSourceListenerInstalled) { + node.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); + node.addEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); + node.addEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); + + isDragSourceListenerInstalled = true; + } + }); + } + + protected void unregisterListeners() { + runOnFxThread(() -> { + if (isDragSourceListenerInstalled) { + node.removeEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); + node.removeEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); + node.removeEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); + + isDragSourceListenerInstalled = false; + } + }); + } + + private void fireEvent(int x, int y, long evTime, int modifiers) { + // In theory we should register all the events that trigger the gesture (like PRESS, DRAG, DRAG, BINGO!) + // But we can live with this hack for now. + appendEvent(new java.awt.event.MouseEvent(getComponent(), java.awt.event.MouseEvent.MOUSE_PRESSED, + evTime, modifiers, x, y, 0, false)); + + // Also, the modifiers here should've actually come from the last known mouse event (last MOVE or DRAG). + // But we're OK with using the initial PRESS modifiers for now + int initialAction = SunDragSourceContextPeer.convertModifiersToDropAction( + modifiers, getSourceActions()); + + fireDragGestureRecognized(initialAction, new java.awt.Point(x, y)); + } + } + + // Invoked on EDT + private void fireEvent(int x, int y, long evTime, int modifiers) { + ComponentMapper mapper = mapComponent(recognizers, x, y); + + final FXDragGestureRecognizer r = mapper.object; + if (r != null) { + r.fireEvent(mapper.x, mapper.y, evTime, modifiers); + } else { + // No recognizer, no DnD, no startDrag, so release the FX loop now + SwingNodeHelper.leaveFXNestedLoop(this); + } + } + + private MouseEvent getInitialGestureEvent() { + return pressEvent; + } + + private final EventHandler onMousePressHandler = (event) -> { + // It would be nice to maintain a list of all the events that initiate + // a DnD gesture (see a comment in FXDragGestureRecognizer.fireEvent(). + // For now, we simply use the initial PRESS event for this purpose. + pressEvent = event; + pressTime = System.currentTimeMillis(); + }; + + + private volatile FXDragSourceContextPeer activeDSContextPeer; + + private final EventHandler onDragStartHandler = (event) -> { + // Call to AWT and determine the active DragSourceContextPeer + activeDSContextPeer = null; + final MouseEvent firstEv = getInitialGestureEvent(); + SwingNodeHelper.runOnEDTAndWait(FXDnDInteropO.this, () -> fireEvent( + (int)firstEv.getX(), (int)firstEv.getY(), pressTime, + SwingEvents.fxMouseModsToMouseMods(firstEv))); + if (activeDSContextPeer == null) return; + + // Since we're going to start DnD, consume the event. + event.consume(); + + Dragboard db = getNode().startDragAndDrop(SwingDnD.dropActionsToTransferModes( + activeDSContextPeer.sourceActions).toArray(new TransferMode[1])); + + // At this point the activeDSContextPeer.transferable contains all the data from AWT + Map fxData = new HashMap<>(); + for (String mt : activeDSContextPeer.transferable.getMimeTypes()) { + DataFormat f = DataFormat.lookupMimeType(mt); + //TODO: what to do if f == null? + if (f != null) fxData.put(f, activeDSContextPeer.transferable.getData(mt)); + } + + final boolean hasContent = db.setContent(fxData); + if (!hasContent) { + // No data, no DnD, no onDragDoneHandler, so release the AWT loop now + if (!FXDnD.fxAppThreadIsDispatchThread) { + loop.exit(); + } + } + }; + + private final EventHandler onDragDoneHandler = (event) -> { + event.consume(); + + // Release FXDragSourceContextPeer.startDrag() + if (!FXDnD.fxAppThreadIsDispatchThread) { + loop.exit(); + } + + if (activeDSContextPeer != null) { + final TransferMode mode = event.getTransferMode(); + activeDSContextPeer.dragDone( + mode == null ? 0 : SwingDnD.transferModeToDropAction(mode), + (int)event.getX(), (int)event.getY()); + } + }; + + + private final class FXDragSourceContextPeer extends SunDragSourceContextPeer { + private volatile int sourceActions = 0; + + private final CachingTransferable transferable = new CachingTransferable(); + + @Override public void startSecondaryEventLoop(){ + Toolkit.getToolkit().enterNestedEventLoop(this); + } + @Override public void quitSecondaryEventLoop(){ + assert !Platform.isFxApplicationThread(); + Platform.runLater(() -> Toolkit.getToolkit().exitNestedEventLoop(FXDragSourceContextPeer.this, null)); + } + + @Override protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) { + //TODO + } + + + private void dragDone(int operation, int x, int y) { + dragDropFinished(operation != 0, operation, x, y); + } + + FXDragSourceContextPeer(DragGestureEvent dge) { + super(dge); + } + + + // It's Map actually, but javac complains if the type isn't erased... + @Override protected void startDrag(Transferable trans, long[] formats, Map formatMap) + { + activeDSContextPeer = this; + + // NOTE: we ignore the formats[] and the formatMap altogether. + // AWT provides those to allow for more flexible representations of + // e.g. text data (in various formats, encodings, etc.) However, FX + // code isn't ready to handle those (e.g. it can't digest a + // StringReader as data, etc.) So instead we perform our internal + // translation. + // Note that fetchData == true. FX doesn't support delayed data + // callbacks yet anyway, so we have to fetch all the data from AWT upfront. + transferable.updateData(trans, true); + + sourceActions = getDragSourceContext().getSourceActions(); + + // Release the FX nested loop to allow onDragDetected to start the actual DnD operation, + // and then start an AWT nested loop to wait until DnD finishes. + if (!FXDnD.fxAppThreadIsDispatchThread) { + loop = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); + SwingNodeHelper.leaveFXNestedLoop(FXDnDInteropO.this); + if (!loop.enter()) { + // An error occured, but there's little we can do here... + } + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + // DROP TARGET IMPLEMENTATION + /////////////////////////////////////////////////////////////////////////// + + + private boolean isDropTargetListenerInstalled = false; + private volatile FXDropTargetContextPeer activeDTContextPeer = null; + private final Map dropTargets = new HashMap<>(); + + private final EventHandler onDragEnteredHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + int action = activeDTContextPeer.postDropTargetEvent(event); + + // If AWT doesn't accept anything, let parent nodes handle the event + if (action != 0) event.consume(); + }; + + private final EventHandler onDragExitedHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + activeDTContextPeer.postDropTargetEvent(event); + + activeDTContextPeer = null; + }; + + private final EventHandler onDragOverHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + int action = activeDTContextPeer.postDropTargetEvent(event); + + // If AWT doesn't accept anything, let parent nodes handle the event + if (action != 0) { + // NOTE: in FX the acceptTransferModes() may ONLY be called from DRAG_OVER. + // If the AWT app always reports NONE and suddenly decides to accept the + // data in its DRAG_DROPPED handler, this just won't work. There's no way + // to workaround this other than by modifing the AWT application code. + event.acceptTransferModes(SwingDnD.dropActionsToTransferModes(action).toArray(new TransferMode[1])); + event.consume(); + } + }; + + private final EventHandler onDragDroppedHandler = (event) -> { + if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); + + int action = activeDTContextPeer.postDropTargetEvent(event); + + if (action != 0) { + // NOTE: the dropAction is ignored since we use the action last + // reported from the DRAG_OVER handler. + // + // We might want to: + // + // assert activeDTContextPeer.dropAction == onDragDroppedHandler.currentAction; + // + // and maybe print a diagnostic message if they differ. + event.setDropCompleted(activeDTContextPeer.success); + + event.consume(); + } + + activeDTContextPeer = null; + }; + + private final class FXDropTargetContextPeer implements DropTargetContextPeer { + + private int targetActions = DnDConstants.ACTION_NONE; + private int currentAction = DnDConstants.ACTION_NONE; + private DropTarget dt = null; + private DropTargetContext ctx = null; + + private final CachingTransferable transferable = new CachingTransferable(); + + // Drop result + private boolean success = false; + private int dropAction = 0; + + @Override public synchronized void setTargetActions(int actions) { targetActions = actions; } + @Override public synchronized int getTargetActions() { return targetActions; } + + @Override public synchronized DropTarget getDropTarget() { return dt; } + + @Override public synchronized boolean isTransferableJVMLocal() { return false; } + + @Override public synchronized DataFlavor[] getTransferDataFlavors() { return transferable.getTransferDataFlavors(); } + @Override public synchronized Transferable getTransferable() { return transferable; } + + @Override public synchronized void acceptDrag(int dragAction) { currentAction = dragAction; } + @Override public synchronized void rejectDrag() { currentAction = DnDConstants.ACTION_NONE; } + + @Override public synchronized void acceptDrop(int dropAction) { this.dropAction = dropAction; } + @Override public synchronized void rejectDrop() { dropAction = DnDConstants.ACTION_NONE; } + + @Override public synchronized void dropComplete(boolean success) { this.success = success; } + + + private int postDropTargetEvent(DragEvent event) + { + ComponentMapper mapper = mapComponent(dropTargets, (int)event.getX(), (int)event.getY()); + + final EventType fxEvType = event.getEventType(); + + Dragboard db = event.getDragboard(); + transferable.updateData(db, DragEvent.DRAG_DROPPED.equals(fxEvType)); + + final int sourceActions = SwingDnD.transferModesToDropActions(db.getTransferModes()); + final int userAction = event.getTransferMode() == null ? DnDConstants.ACTION_NONE + : SwingDnD.transferModeToDropAction(event.getTransferMode()); + + // A target for the AWT DnD event + DropTarget target = mapper.object != null ? mapper.object : dt; + + SwingNodeHelper.runOnEDTAndWait(FXDnDInteropO.this, () -> { + if (target != dt) { + if (ctx != null) { + AWTAccessor.getDropTargetContextAccessor().reset(ctx); + } + ctx = null; + + currentAction = dropAction = DnDConstants.ACTION_NONE; + } + + if (target != null) { + if (ctx == null) { + ctx = target.getDropTargetContext(); + AWTAccessor.getDropTargetContextAccessor() + .setDropTargetContextPeer(ctx, FXDropTargetContextPeer.this); + } + + DropTargetListener dtl = (DropTargetListener)target; + + if (DragEvent.DRAG_DROPPED.equals(fxEvType)) { + DropTargetDropEvent awtEvent = new DropTargetDropEvent( + ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); + + dtl.drop(awtEvent); + } else { + DropTargetDragEvent awtEvent = new DropTargetDragEvent( + ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); + + if (DragEvent.DRAG_OVER.equals(fxEvType)) dtl.dragOver(awtEvent); + else if (DragEvent.DRAG_ENTERED.equals(fxEvType)) dtl.dragEnter(awtEvent); + else if (DragEvent.DRAG_EXITED.equals(fxEvType)) dtl.dragExit(awtEvent); + } + } + + dt = mapper.object; + if (dt == null) { + // FIXME: once we switch to JDK 9 as the boot JDK + // we need to re-implement the following using + // available API. + /* + if (ctx != null) ctx.removeNotify(); + */ + ctx = null; + + currentAction = dropAction = DnDConstants.ACTION_NONE; + } + if (DragEvent.DRAG_DROPPED.equals(fxEvType) || DragEvent.DRAG_EXITED.equals(fxEvType)) { + // This must be done to ensure that the data isn't being + // cached in AWT. Otherwise subsequent DnD operations will + // see the old data only. + // FIXME: once we switch to JDK 9 as the boot JDK + // we need to re-implement the following using + // available API. + /* + if (ctx != null) ctx.removeNotify(); + */ + ctx = null; + } + + SwingNodeHelper.leaveFXNestedLoop(FXDnDInteropO.this); + }); + + if (DragEvent.DRAG_DROPPED.equals(fxEvType)) return dropAction; + + return currentAction; + } + } + + public void addDropTarget(DropTarget dt, SwingNode node) { + dropTargets.put(dt.getComponent(), dt); + Platform.runLater(() -> { + if (!isDropTargetListenerInstalled) { + node.addEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); + node.addEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); + node.addEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); + node.addEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); + + isDropTargetListenerInstalled = true; + } + }); + } + + public void removeDropTarget(DropTarget dt, SwingNode node) { + dropTargets.remove(dt.getComponent()); + Platform.runLater(() -> { + if (isDropTargetListenerInstalled && dropTargets.isEmpty()) { + node.removeEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); + node.removeEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); + node.removeEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); + node.removeEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); + + isDropTargetListenerInstalled = false; + } + }); + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingNode.java 2018-07-10 15:05:41.012481200 +0530 +++ new/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingNode.java 2018-07-10 15:05:40.199378000 +0530 @@ -53,7 +53,6 @@ import java.awt.dnd.DragSource; import java.awt.dnd.DropTarget; import java.awt.dnd.InvalidDnDOperationException; -import java.awt.dnd.peer.DragSourceContextPeer; import java.awt.event.InputEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.WindowEvent; @@ -80,12 +79,14 @@ import com.sun.javafx.PlatformUtil; import com.sun.javafx.embed.swing.SwingNodeHelper; import com.sun.javafx.scene.NodeHelper; -import sun.awt.UngrabEvent; -import sun.swing.JLightweightFrame; -import sun.swing.LightweightContent; +import com.sun.javafx.embed.swing.SwingEvents; +import com.sun.javafx.embed.swing.SwingCursors; import static javafx.stage.WindowEvent.WINDOW_HIDDEN; +import com.sun.javafx.embed.swing.InteropFactory; +import com.sun.javafx.embed.swing.SwingNodeInterop; + /** * This class is used to embed a Swing content into a JavaFX application. * The content to be displayed is specified with the {@link #setContent} method @@ -129,8 +130,16 @@ */ public class SwingNode extends Node { private static boolean isThreadMerged; + private SwingNodeInterop swiop; + private static InteropFactory instance = null; static { + try { + instance = InteropFactory.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + AccessController.doPrivileged(new PrivilegedAction() { public Object run() { isThreadMerged = Boolean.valueOf( @@ -139,6 +148,7 @@ } }); + // This is used by classes in different packages to get access to // private and package private methods. SwingNodeHelper.setSwingNodeAccessor(new SwingNodeHelper.SwingNodeAccessor() { @@ -162,12 +172,81 @@ public boolean doComputeContains(Node node, double localX, double localY) { return ((SwingNode) node).doComputeContains(localX, localY); } + + @Override + public Object getLightweightFrame(SwingNode node) { + return node.getLightweightFrame(); + } + + @Override + public ReentrantLock getPaintLock(SwingNode node) { + return node.getPaintLock(); + } + + @Override + public void setImageBuffer(SwingNode node, final int[] data, + final int x, final int y, + final int w, final int h, final int linestride, + final double scaleX, final double scaleY) { + node.setImageBuffer(data, x, y, w, h, linestride, scaleX, scaleY); + } + + @Override + public void setImageBounds(SwingNode node, final int x, final int y, + final int w, final int h) { + node.setImageBounds(x, y, w, h); + } + + @Override + public void repaintDirtyRegion(SwingNode node, final int dirtyX, final int dirtyY, + final int dirtyWidth, final int dirtyHeight) { + node.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight); + } + + @Override + public void ungrabFocus(SwingNode node, boolean postUngrabEvent) { + node.ungrabFocus(postUngrabEvent); + } + + @Override + public void setSwingPrefWidth(SwingNode node, int swingPrefWidth) { + node.swingPrefWidth = swingPrefWidth; + } + + @Override + public void setSwingPrefHeight(SwingNode node, int swingPrefHeight) { + node.swingPrefHeight = swingPrefHeight; + } + + @Override + public void setSwingMaxWidth(SwingNode node, int swingMaxWidth) { + node.swingMaxWidth = swingMaxWidth; + } + + @Override + public void setSwingMaxHeight(SwingNode node, int swingMaxHeight) { + node.swingMaxHeight = swingMaxHeight; + } + + @Override + public void setSwingMinWidth(SwingNode node, int swingMinWidth) { + node.swingMinWidth = swingMinWidth; + } + + @Override + public void setSwingMinHeight(SwingNode node, int swingMinHeight) { + node.swingMinHeight = swingMinHeight; + } + + @Override + public void setGrabbed(SwingNode node, boolean grab) { + node.grabbed = grab; + } }); } private double fxWidth; private double fxHeight; - private int swingPrefWidth; private int swingPrefHeight; private int swingMaxWidth; @@ -176,13 +255,17 @@ private int swingMinHeight; private volatile JComponent content; - private volatile JLightweightFrame lwFrame; - final JLightweightFrame getLightweightFrame() { return lwFrame; } + private volatile Object lwFrame; + private final Object getLightweightFrame() { return lwFrame; } private NGExternalNode peer; private final ReentrantLock paintLock = new ReentrantLock(); + private ReentrantLock getPaintLock() { + return paintLock; + } + private boolean skipBackwardUnrgabNotification; private boolean grabbed; // lwframe initiated grab private Timer deactivate; // lwFrame deactivate delay for Linux @@ -196,6 +279,7 @@ * Constructs a new instance of {@code SwingNode}. */ public SwingNode() { + swiop = instance.createSwingNodeImpl(); setFocusTraversable(true); setEventHandler(MouseEvent.ANY, new SwingMouseEventHandler()); setEventHandler(KeyEvent.ANY, new SwingKeyEventHandler()); @@ -216,14 +300,14 @@ TKStage tk = WindowHelper.getPeer(w); if (tk != null) { if (isThreadMerged) { - jlfOverrideNativeWindowHandle.invoke(lwFrame, 0L, null); + swiop.overrideNativeWindowHandle(0L, null); } else { // Postpone actual window closing to ensure that // a native window handler is valid on a Swing side tk.postponeClose(); - SwingFXUtils.runOnEDT(() -> { - jlfOverrideNativeWindowHandle.invoke(lwFrame, 0L, - (Runnable) () -> SwingFXUtils.runOnFxThread( + SwingNodeHelper.runOnEDT(() -> { + swiop.overrideNativeWindowHandle(0L, + (Runnable) () -> SwingNodeHelper.runOnFxThread( () -> tk.closePostponed())); }); } @@ -252,7 +336,7 @@ rawHandle = tkStage.getRawHandle(); } } - jlfOverrideNativeWindowHandle.invoke(lwFrame, rawHandle, null); + swiop.overrideNativeWindowHandle(rawHandle, null); } } @@ -271,7 +355,7 @@ public void setContent(final JComponent content) { this.content = content; - SwingFXUtils.runOnEDT(() -> setContentImpl(content)); + SwingNodeHelper.runOnEDT(() -> setContentImpl(content)); } /** @@ -330,66 +414,39 @@ } } - /** - * Calls JLightweightFrame.notifyDisplayChanged. - * Must be called on EDT only. - */ - private static OptionalMethod jlfNotifyDisplayChanged; - private static OptionalMethod jlfOverrideNativeWindowHandle; - - static { - jlfNotifyDisplayChanged = new OptionalMethod<>(JLightweightFrame.class, - "notifyDisplayChanged", Double.TYPE, Double.TYPE); - if (!jlfNotifyDisplayChanged.isSupported()) { - jlfNotifyDisplayChanged = new OptionalMethod<>( - JLightweightFrame.class,"notifyDisplayChanged", Integer.TYPE); - } - - jlfOverrideNativeWindowHandle = new OptionalMethod<>(JLightweightFrame.class, - "overrideNativeWindowHandle", Long.TYPE, Runnable.class); - - } - /* * Called on EDT */ private void setContentImpl(JComponent content) { if (lwFrame != null) { - lwFrame.dispose(); + swiop.disposeFrame(lwFrame); lwFrame = null; } if (content != null) { - lwFrame = new JLightweightFrame(); + lwFrame = swiop.createLightweightFrame(); SwingNodeWindowFocusListener snfListener = new SwingNodeWindowFocusListener(this); - lwFrame.addWindowFocusListener(snfListener); + swiop.addWindowFocusListener(lwFrame, snfListener); if (getScene() != null) { Window window = getScene().getWindow(); if (window != null) { - if (jlfNotifyDisplayChanged.isIntegerApi()) { - jlfNotifyDisplayChanged.invoke(lwFrame, - (int) Math.round(window.getRenderScaleX())); - } else { - jlfNotifyDisplayChanged.invoke(lwFrame, - window.getRenderScaleX(), - window.getRenderScaleY()); - } + swiop.notifyDisplayChanged(lwFrame, window.getRenderScaleX(), + window.getRenderScaleY()); } } - lwFrame.setContent(new SwingNodeContent(content, this)); - lwFrame.setVisible(true); + swiop.setContent(lwFrame, swiop.createSwingNodeContent(content, this)); + swiop.setVisible(lwFrame, true); - SwingNodeDisposer disposeRec = new SwingNodeDisposer(lwFrame); - Disposer.addRecord(this, disposeRec); + Disposer.addRecord(this, swiop.createSwingNodeDisposer(lwFrame)); if (getScene() != null) { notifyNativeHandle(getScene().getWindow()); } - SwingFXUtils.runOnFxThread(() -> { - locateLwFrame(); // initialize location + SwingNodeHelper.runOnFxThread(() -> { + locateLwFrame();// initialize location if (focusedProperty().get()) { activateLwFrame(true); @@ -412,7 +469,7 @@ { Runnable r = () -> peer.setImageBuffer(IntBuffer.wrap(data), x, y, w, h, w, h, linestride, scaleX, scaleY); - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { if (peer != null) { r.run(); } else { @@ -427,7 +484,7 @@ */ void setImageBounds(final int x, final int y, final int w, final int h) { Runnable r = () -> peer.setImageBounds(x, y, w, h, w, h); - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { if (peer != null) { r.run(); } else { @@ -444,7 +501,7 @@ peer.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight); NodeHelper.markDirty(this, DirtyBits.NODE_CONTENTS); }; - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { if (peer != null) { r.run(); } else { @@ -475,7 +532,7 @@ this.fxHeight = height; NodeHelper.geomChanged(this); NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY); - SwingFXUtils.runOnEDT(() -> { + SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { locateLwFrame(); } @@ -559,7 +616,8 @@ private final EventHandler ungrabHandler = event -> { if (!skipBackwardUnrgabNotification) { if (lwFrame != null) { - AccessController.doPrivileged(new PostEventAction(new UngrabEvent(lwFrame))); + AccessController.doPrivileged(new PostEventAction( + swiop.createUngrabEvent(lwFrame))); } } }; @@ -672,14 +730,6 @@ } } - /** - * Calls JLightweightFrame.setHostBounds. - * Must be called on EDT only. - */ - private static final OptionalMethod jlfSetHostBounds = - new OptionalMethod<>(JLightweightFrame.class, "setHostBounds", - Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); - private void locateLwFrame() { if (getScene() == null || lwFrame == null @@ -701,18 +751,11 @@ final int frameW = (int) (fxWidth); final int frameH = (int) (fxHeight); - SwingFXUtils.runOnEDT(() -> { + SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { - if (jlfNotifyDisplayChanged.isIntegerApi()) { - jlfNotifyDisplayChanged.invoke(lwFrame, - (int)Math.round(renderScaleX)); - } else { - jlfNotifyDisplayChanged.invoke(lwFrame, renderScaleX, - renderScaleY); - } - lwFrame.setBounds(frameX, frameY, frameW, frameH); - jlfSetHostBounds.invoke(lwFrame, windowX, windowY, - windowW, windowH); + swiop.notifyDisplayChanged(lwFrame, renderScaleX, renderScaleY); + swiop.setBounds(lwFrame, frameX, frameY, frameW, frameH); + swiop.setHostBounds(lwFrame, windowX, windowY, windowW, windowH); } }); } @@ -729,7 +772,7 @@ deactivate = new Timer(50, (e) -> { { if (lwFrame != null) { - lwFrame.emulateActivation(false); + swiop.emulateActivation(lwFrame, false); } } }); @@ -741,9 +784,9 @@ } } - SwingFXUtils.runOnEDT(() -> { + SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { - lwFrame.emulateActivation(activate); + swiop.emulateActivation(lwFrame, activate); } }); } @@ -752,9 +795,9 @@ if (lwFrame == null) { return; } - SwingFXUtils.runOnEDT(() -> { + SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { - lwFrame.dispose(); + swiop.disposeFrame(lwFrame); lwFrame = null; } }); @@ -764,9 +807,9 @@ if (lwFrame == null) { return; } - SwingFXUtils.runOnEDT(() -> { + SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { - lwFrame.setVisible(visible); + swiop.setVisible(lwFrame, visible); } }); } @@ -775,16 +818,11 @@ if (lwFrame == null) { return; } - SwingFXUtils.runOnEDT(() -> { + SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { - if (jlfNotifyDisplayChanged.isIntegerApi()) { - jlfNotifyDisplayChanged.invoke(lwFrame, - (int)Math.round(scaleX)); - } else { - jlfNotifyDisplayChanged.invoke(lwFrame, scaleX, scaleY); - } - } - }); + swiop.notifyDisplayChanged(lwFrame, scaleX, scaleY); + } + }); } /* @@ -796,19 +834,6 @@ return bounds; } - private static class SwingNodeDisposer implements DisposerRecord { - JLightweightFrame lwFrame; - - SwingNodeDisposer(JLightweightFrame ref) { - this.lwFrame = ref; - } - public void dispose() { - if (lwFrame != null) { - lwFrame.dispose(); - lwFrame = null; - } - } - } private static class SwingNodeWindowFocusListener implements WindowFocusListener { private WeakReference swingNodeRef; @@ -819,7 +844,7 @@ @Override public void windowGainedFocus(WindowEvent e) { - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { SwingNode swingNode = swingNodeRef.get(); if (swingNode != null) { swingNode.requestFocus(); @@ -829,7 +854,7 @@ @Override public void windowLostFocus(WindowEvent e) { - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { SwingNode swingNode = swingNodeRef.get(); if (swingNode != null) { swingNode.ungrabFocus(true); @@ -838,182 +863,6 @@ } } - private static class SwingNodeContent implements LightweightContent { - private JComponent comp; - private volatile FXDnD dnd; - private WeakReference swingNodeRef; - - SwingNodeContent(JComponent comp, SwingNode swingNode) { - this.comp = comp; - this.swingNodeRef = new WeakReference(swingNode); - } - @Override - public JComponent getComponent() { - return comp; - } - @Override - public void paintLock() { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.paintLock.lock(); - } - } - @Override - public void paintUnlock() { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.paintLock.unlock(); - } - } - - // Note: we skip @Override annotation and implement both pre-hiDPI and post-hiDPI versions - // of the method for compatibility. - //@Override - public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) { - imageBufferReset(data, x, y, width, height, linestride, 1); - } - //@Override - public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.setImageBuffer(data, x, y, width, height, linestride, scale, scale); - } - } - //@Override - public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.setImageBuffer(data, x, y, width, height, linestride, scaleX, scaleY); - } - } - @Override - public void imageReshaped(int x, int y, int width, int height) { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.setImageBounds(x, y, width, height); - } - } - @Override - public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight); - } - } - @Override - public void focusGrabbed() { - SwingFXUtils.runOnFxThread(() -> { - // On X11 grab is limited to a single XDisplay connection, - // so we can't delegate it to another GUI toolkit. - if (PlatformUtil.isLinux()) return; - - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - Scene scene = swingNode.getScene(); - if (scene != null && - scene.getWindow() != null && - WindowHelper.getPeer(scene.getWindow()) != null) { - WindowHelper.getPeer(scene.getWindow()).grabFocus(); - swingNode.grabbed = true; - } - } - }); - } - @Override - public void focusUngrabbed() { - SwingFXUtils.runOnFxThread(() -> { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.ungrabFocus(false); - } - }); - } - @Override - public void preferredSizeChanged(final int width, final int height) { - SwingFXUtils.runOnFxThread(() -> { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.swingPrefWidth = width; - swingNode.swingPrefHeight = height; - NodeHelper.notifyLayoutBoundsChanged(swingNode); - } - }); - } - @Override - public void maximumSizeChanged(final int width, final int height) { - SwingFXUtils.runOnFxThread(() -> { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.swingMaxWidth = width; - swingNode.swingMaxHeight = height; - NodeHelper.notifyLayoutBoundsChanged(swingNode); - } - }); - } - @Override - public void minimumSizeChanged(final int width, final int height) { - SwingFXUtils.runOnFxThread(() -> { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.swingMinWidth = width; - swingNode.swingMinHeight = height; - NodeHelper.notifyLayoutBoundsChanged(swingNode); - } - }); - } - - //@Override - public void setCursor(Cursor cursor) { - SwingFXUtils.runOnFxThread(() -> { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor)); - } - }); - } - - private void initDnD() { - // This is a part of AWT API, so the method may be invoked on any thread - synchronized (SwingNodeContent.this) { - if (this.dnd == null) { - SwingNode swingNode = swingNodeRef.get(); - if (swingNode != null) { - this.dnd = new FXDnD(swingNode); - } - } - } - } - - //@Override - public synchronized T createDragGestureRecognizer( - Class abstractRecognizerClass, - DragSource ds, Component c, int srcActions, - DragGestureListener dgl) - { - initDnD(); - return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl); - } - - //@Override - public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException - { - initDnD(); - return dnd.createDragSourceContextPeer(dge); - } - - //@Override - public void addDropTarget(DropTarget dt) { - initDnD(); - dnd.addDropTarget(dt); - } - - //@Override - public void removeDropTarget(DropTarget dt) { - initDnD(); - dnd.removeDropTarget(dt); - } - } - private void ungrabFocus(boolean postUngrabEvent) { // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. @@ -1049,7 +898,7 @@ @Override public void handle(MouseEvent event) { - JLightweightFrame frame = lwFrame; + Object frame = swiop.getLightweightFrame(); if (frame == null) { return; } @@ -1087,7 +936,7 @@ int absX = (int) Math.round(event.getScreenX()); int absY = (int) Math.round(event.getScreenY()); java.awt.event.MouseEvent mouseEvent = - new java.awt.event.MouseEvent( + swiop.createMouseEvent( frame, swingID, swingWhen, swingModifiers, relX, relY, absX, absY, event.getClickCount(), swingPopupTrigger, swingButton); @@ -1098,7 +947,7 @@ private class SwingScrollEventHandler implements EventHandler { @Override public void handle(ScrollEvent event) { - JLightweightFrame frame = lwFrame; + Object frame = swiop.getLightweightFrame(); if (frame == null) { return; } @@ -1122,7 +971,7 @@ } } - private void sendMouseWheelEvent(Component source, double fxX, double fxY, int swingModifiers, double delta) { + private void sendMouseWheelEvent(Object source, double fxX, double fxY, int swingModifiers, double delta) { int wheelRotation = (int) delta; int signum = (int) Math.signum(delta); if (signum * delta < 1) { @@ -1131,9 +980,7 @@ int x = (int) Math.round(fxX); int y = (int) Math.round(fxY); MouseWheelEvent mouseWheelEvent = - new MouseWheelEvent(source, java.awt.event.MouseEvent.MOUSE_WHEEL, - System.currentTimeMillis(), swingModifiers, x, y, 0, 0, - 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1 , -wheelRotation); + swiop.createMouseWheelEvent(source, swingModifiers, x, y, -wheelRotation); AccessController.doPrivileged(new PostEventAction(mouseWheelEvent)); } } @@ -1141,7 +988,7 @@ private class SwingKeyEventHandler implements EventHandler { @Override public void handle(KeyEvent event) { - JLightweightFrame frame = lwFrame; + Object frame = swiop.getLightweightFrame(); if (frame == null) { return; } @@ -1176,10 +1023,11 @@ } } long swingWhen = System.currentTimeMillis(); - java.awt.event.KeyEvent keyEvent = new java.awt.event.KeyEvent( - frame, swingID, swingWhen, swingModifiers, - swingKeyCode, swingChar); + java.awt.event.KeyEvent keyEvent = swiop.createKeyEvent(frame, + swingID, swingWhen, swingModifiers, swingKeyCode, + swingChar); AccessController.doPrivileged(new PostEventAction(keyEvent)); } } } + --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java 2018-07-10 15:05:46.172636500 +0530 +++ new/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java 2018-07-10 15:05:45.215514900 +0530 @@ -38,6 +38,7 @@ import java.awt.Insets; import java.awt.EventQueue; import java.awt.SecondaryLoop; +import java.awt.GraphicsEnvironment; import java.awt.event.AWTEventListener; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; @@ -77,13 +78,17 @@ import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; -import sun.awt.AppContext; -import sun.awt.SunToolkit; -import sun.java2d.SunGraphics2D; -import sun.java2d.SurfaceData; + import com.sun.javafx.logging.PlatformLogger; import com.sun.javafx.logging.PlatformLogger.Level; +import com.sun.javafx.embed.swing.InteropFactory; +import com.sun.javafx.embed.swing.SwingDnD; +import com.sun.javafx.embed.swing.SwingEvents; +import com.sun.javafx.embed.swing.SwingCursors; +import com.sun.javafx.embed.swing.SwingNodeHelper; +import com.sun.javafx.embed.swing.JFXPanelInterop; + /** * {@code JFXPanel} is a component to embed JavaFX content into * Swing applications. The content to be displayed is specified @@ -185,6 +190,17 @@ private static boolean fxInitialized; + private static InteropFactory instance = null; + private JFXPanelInterop jfxPaneliop; + + static { + try { + instance = InteropFactory.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + private synchronized void registerFinishListener() { if (instanceCount.getAndIncrement() > 0) { // Already registered @@ -256,6 +272,7 @@ public JFXPanel() { super(); + jfxPaneliop = instance.createJFXPanelImpl(); initFx(); hostContainer = new HostContainer(); @@ -450,10 +467,7 @@ // component. Fire the same MOUSE_PRESSED event after // requestFocus() so that 2nd mouse press will be honoured // since now fx have focus - AppContext context = SunToolkit.targetToAppContext(this); - if (context != null) { - SunToolkit.postEvent(context, e); - } + jfxPaneliop.postEvent(this, e); } } @@ -570,11 +584,12 @@ double newScaleFactorX = scaleFactorX; double newScaleFactorY = scaleFactorY; Graphics g = getGraphics(); - if (g instanceof SunGraphics2D) { - SurfaceData sd = ((SunGraphics2D) g).surfaceData; - newScaleFactorX = sd.getDefaultScaleX(); - newScaleFactorY = sd.getDefaultScaleY(); - } + newScaleFactorX = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(). + getDefaultTransform().getScaleX(); + newScaleFactorY = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(). + getDefaultTransform().getScaleY(); if (oldWidth != pWidth || oldHeight != pHeight || newScaleFactorX != scaleFactorX || newScaleFactorY != scaleFactorY) { @@ -765,11 +780,12 @@ double newScaleFactorX = scaleFactorX; double newScaleFactorY = scaleFactorY; - if (g instanceof SunGraphics2D) { - SurfaceData sd = ((SunGraphics2D)g).surfaceData; - newScaleFactorX = sd.getDefaultScaleX(); - newScaleFactorY = sd.getDefaultScaleY(); - } + newScaleFactorX = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(). + getDefaultTransform().getScaleX(); + newScaleFactorY = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(). + getDefaultTransform().getScaleY(); if (scaleFactorX != newScaleFactorX || scaleFactorY != newScaleFactorY) { createResizePixelBuffer(newScaleFactorX, newScaleFactorY); // The scene will request repaint. @@ -828,8 +844,8 @@ } private transient AWTEventListener ungrabListener = event -> { - if (event instanceof sun.awt.UngrabEvent) { - SwingFXUtils.runOnFxThread(() -> { + if (jfxPaneliop.isUngrabEvent(event)) { + SwingNodeHelper.runOnFxThread(() -> { if (JFXPanel.this.stagePeer != null && getScene() != null && getScene().getFocusOwner() != null && @@ -847,7 +863,7 @@ final Window eventWindow = source instanceof Window ? (Window)source : SwingUtilities.getWindowAncestor(source); if (jfxPanelWindow == eventWindow) { - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { if (JFXPanel.this.stagePeer != null) { // No need to check if grab is active or not. // NoAutoHide popups don't request the grab and @@ -877,11 +893,11 @@ AccessController.doPrivileged((PrivilegedAction) () -> { JFXPanel.this.getToolkit().addAWTEventListener(ungrabListener, - SunToolkit.GRAB_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK); + jfxPaneliop.setMask()); return null; }); updateComponentSize(); // see RT-23603 - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && !stage.isShowing()) { stage.show(); sendMoveEventToFX(); @@ -904,7 +920,7 @@ * chain of parent components are removed. */ @Override public void removeNotify() { - SwingFXUtils.runOnFxThread(() -> { + SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && stage.isShowing()) { stage.hide(); } @@ -928,12 +944,7 @@ } private void invokeOnClientEDT(Runnable r) { - AppContext context = SunToolkit.targetToAppContext(this); - if (context == null) { - if (log.isLoggable(Level.FINE)) log.fine("null AppContext encountered!"); - return; - } - SunToolkit.postEvent(context, new InvocationEvent(this, r)); + jfxPaneliop.postEvent(this, new InvocationEvent(this, r)); } private class HostContainer implements HostInterface { @@ -1054,9 +1065,7 @@ invokeOnClientEDT(() -> { Window window = SwingUtilities.getWindowAncestor(JFXPanel.this); if (window != null) { - if (JFXPanel.this.getToolkit() instanceof SunToolkit) { - ((SunToolkit)JFXPanel.this.getToolkit()).grab(window); - } + jfxPaneliop.grab(JFXPanel.this.getToolkit(), window); } }); @@ -1072,9 +1081,7 @@ invokeOnClientEDT(() -> { Window window = SwingUtilities.getWindowAncestor(JFXPanel.this); if (window != null) { - if (JFXPanel.this.getToolkit() instanceof SunToolkit) { - ((SunToolkit)JFXPanel.this.getToolkit()).ungrab(window); - } + jfxPaneliop.ungrab(JFXPanel.this.getToolkit(), window); } }); } --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/FXDnD.java 2018-07-10 15:05:51.249281100 +0530 +++ /dev/null 2018-07-10 15:05:51.000000000 +0530 @@ -1,564 +0,0 @@ -/* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import com.sun.javafx.tk.Toolkit; -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Point; -import java.awt.SecondaryLoop; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.DragGestureListener; -import java.awt.dnd.DragGestureRecognizer; -import java.awt.dnd.DragSource; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetContext; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.dnd.DropTargetListener; -import java.awt.dnd.InvalidDnDOperationException; -import java.awt.dnd.MouseDragGestureRecognizer; -import java.awt.dnd.peer.DragSourceContextPeer; -import java.awt.dnd.peer.DropTargetContextPeer; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Map; -import javafx.application.Platform; -import javafx.event.EventHandler; -import javafx.event.EventType; -import javafx.scene.input.DataFormat; -import javafx.scene.input.DragEvent; -import javafx.scene.input.Dragboard; -import javafx.scene.input.MouseEvent; -import javafx.scene.input.TransferMode; -import sun.awt.AWTAccessor; -import sun.awt.dnd.SunDragSourceContextPeer; -import sun.swing.JLightweightFrame; - - -/** - * A utility class to connect DnD mechanism of Swing and FX. - * It allows Swing content to use the FX machinery for performing DnD. - */ -final class FXDnD { - private final SwingNode node; - private static boolean fxAppThreadIsDispatchThread; - private SwingNode getNode() { return node; } - - static { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - fxAppThreadIsDispatchThread = - "true".equals(System.getProperty("javafx.embed.singleThread")); - return null; - } - }); - } - - FXDnD(SwingNode node) { - this.node = node; - } - - /** - * Utility class that operates on Maps with Components as keys. - * Useful when processing mouse events to choose an object from the map - * based on the component located at the given coordinates. - */ - private class ComponentMapper { - private int x, y; - private T object = null; - - private ComponentMapper(Map map, int xArg, int yArg) { - this.x = xArg; - this.y = yArg; - - final JLightweightFrame lwFrame = node.getLightweightFrame(); - Component c = AWTAccessor.getContainerAccessor().findComponentAt( - lwFrame, x, y, false); - if (c == null) return; - - synchronized (c.getTreeLock()) { - do { - object = map.get(c); - } while (object == null && (c = c.getParent()) != null); - - if (object != null) { - // The object is either a DropTarget or a DragSource, so: - //assert c == object.getComponent(); - - // Translate x, y from lwFrame to component coordinates - while (c != lwFrame && c != null) { - x -= c.getX(); - y -= c.getY(); - c = c.getParent(); - } - } - } - } - } - public ComponentMapper mapComponent(Map map, int x, int y) { - return new ComponentMapper(map, x, y); - } - - - - - - /////////////////////////////////////////////////////////////////////////// - // DRAG SOURCE IMPLEMENTATION - /////////////////////////////////////////////////////////////////////////// - - - private boolean isDragSourceListenerInstalled = false; - - // To keep track of where the DnD gesture actually started - private MouseEvent pressEvent = null; - private long pressTime = 0; - - private volatile SecondaryLoop loop; - - private final Map recognizers = new HashMap<>(); - - // Note that we don't really use the MouseDragGestureRecognizer facilities, - // however some code in JDK may expect a descendant of this class rather - // than a generic DragGestureRecognizer. So we inherit from it. - private class FXDragGestureRecognizer extends MouseDragGestureRecognizer { - FXDragGestureRecognizer(DragSource ds, Component c, int srcActions, - DragGestureListener dgl) - { - super(ds, c, srcActions, dgl); - - if (c != null) recognizers.put(c, this); - } - - @Override public void setComponent(Component c) { - final Component old = getComponent(); - if (old != null) recognizers.remove(old); - super.setComponent(c); - if (c != null) recognizers.put(c, this); - } - - protected void registerListeners() { - SwingFXUtils.runOnFxThread(() -> { - if (!isDragSourceListenerInstalled) { - node.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); - node.addEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); - node.addEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); - - isDragSourceListenerInstalled = true; - } - }); - } - - protected void unregisterListeners() { - SwingFXUtils.runOnFxThread(() -> { - if (isDragSourceListenerInstalled) { - node.removeEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressHandler); - node.removeEventHandler(MouseEvent.DRAG_DETECTED, onDragStartHandler); - node.removeEventHandler(DragEvent.DRAG_DONE, onDragDoneHandler); - - isDragSourceListenerInstalled = false; - } - }); - } - - private void fireEvent(int x, int y, long evTime, int modifiers) { - // In theory we should register all the events that trigger the gesture (like PRESS, DRAG, DRAG, BINGO!) - // But we can live with this hack for now. - appendEvent(new java.awt.event.MouseEvent(getComponent(), java.awt.event.MouseEvent.MOUSE_PRESSED, - evTime, modifiers, x, y, 0, false)); - - // Also, the modifiers here should've actually come from the last known mouse event (last MOVE or DRAG). - // But we're OK with using the initial PRESS modifiers for now - int initialAction = SunDragSourceContextPeer.convertModifiersToDropAction( - modifiers, getSourceActions()); - - fireDragGestureRecognized(initialAction, new java.awt.Point(x, y)); - } - } - - // Invoked on EDT - private void fireEvent(int x, int y, long evTime, int modifiers) { - ComponentMapper mapper = mapComponent(recognizers, x, y); - - final FXDragGestureRecognizer r = mapper.object; - if (r != null) { - r.fireEvent(mapper.x, mapper.y, evTime, modifiers); - } else { - // No recognizer, no DnD, no startDrag, so release the FX loop now - SwingFXUtils.leaveFXNestedLoop(FXDnD.this); - } - } - - private MouseEvent getInitialGestureEvent() { - return pressEvent; - } - - private final EventHandler onMousePressHandler = (event) -> { - // It would be nice to maintain a list of all the events that initiate - // a DnD gesture (see a comment in FXDragGestureRecognizer.fireEvent(). - // For now, we simply use the initial PRESS event for this purpose. - pressEvent = event; - pressTime = System.currentTimeMillis(); - }; - - - private volatile FXDragSourceContextPeer activeDSContextPeer; - - private final EventHandler onDragStartHandler = (event) -> { - // Call to AWT and determine the active DragSourceContextPeer - activeDSContextPeer = null; - final MouseEvent firstEv = getInitialGestureEvent(); - SwingFXUtils.runOnEDTAndWait(FXDnD.this, () -> fireEvent( - (int)firstEv.getX(), (int)firstEv.getY(), pressTime, - SwingEvents.fxMouseModsToMouseMods(firstEv))); - if (activeDSContextPeer == null) return; - - // Since we're going to start DnD, consume the event. - event.consume(); - - Dragboard db = getNode().startDragAndDrop(SwingDnD.dropActionsToTransferModes( - activeDSContextPeer.sourceActions).toArray(new TransferMode[1])); - - // At this point the activeDSContextPeer.transferable contains all the data from AWT - Map fxData = new HashMap<>(); - for (String mt : activeDSContextPeer.transferable.getMimeTypes()) { - DataFormat f = DataFormat.lookupMimeType(mt); - //TODO: what to do if f == null? - if (f != null) fxData.put(f, activeDSContextPeer.transferable.getData(mt)); - } - - final boolean hasContent = db.setContent(fxData); - if (!hasContent) { - // No data, no DnD, no onDragDoneHandler, so release the AWT loop now - if (!fxAppThreadIsDispatchThread) { - loop.exit(); - } - } - }; - - private final EventHandler onDragDoneHandler = (event) -> { - event.consume(); - - // Release FXDragSourceContextPeer.startDrag() - if (!fxAppThreadIsDispatchThread) { - loop.exit(); - } - - if (activeDSContextPeer != null) { - final TransferMode mode = event.getTransferMode(); - activeDSContextPeer.dragDone( - mode == null ? 0 : SwingDnD.transferModeToDropAction(mode), - (int)event.getX(), (int)event.getY()); - } - }; - - - private final class FXDragSourceContextPeer extends SunDragSourceContextPeer { - private volatile int sourceActions = 0; - - private final CachingTransferable transferable = new CachingTransferable(); - - @Override public void startSecondaryEventLoop(){ - Toolkit.getToolkit().enterNestedEventLoop(this); - } - @Override public void quitSecondaryEventLoop(){ - assert !Platform.isFxApplicationThread(); - Platform.runLater(() -> Toolkit.getToolkit().exitNestedEventLoop(FXDragSourceContextPeer.this, null)); - } - - @Override protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) { - //TODO - } - - - private void dragDone(int operation, int x, int y) { - dragDropFinished(operation != 0, operation, x, y); - } - - FXDragSourceContextPeer(DragGestureEvent dge) { - super(dge); - } - - - // It's Map actually, but javac complains if the type isn't erased... - @Override protected void startDrag(Transferable trans, long[] formats, Map formatMap) - { - activeDSContextPeer = this; - - // NOTE: we ignore the formats[] and the formatMap altogether. - // AWT provides those to allow for more flexible representations of - // e.g. text data (in various formats, encodings, etc.) However, FX - // code isn't ready to handle those (e.g. it can't digest a - // StringReader as data, etc.) So instead we perform our internal - // translation. - // Note that fetchData == true. FX doesn't support delayed data - // callbacks yet anyway, so we have to fetch all the data from AWT upfront. - transferable.updateData(trans, true); - - sourceActions = getDragSourceContext().getSourceActions(); - - // Release the FX nested loop to allow onDragDetected to start the actual DnD operation, - // and then start an AWT nested loop to wait until DnD finishes. - if (!fxAppThreadIsDispatchThread) { - loop = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); - SwingFXUtils.leaveFXNestedLoop(FXDnD.this); - if (!loop.enter()) { - // An error occured, but there's little we can do here... - } - } - } - }; - - public T createDragGestureRecognizer( - Class abstractRecognizerClass, - DragSource ds, Component c, int srcActions, - DragGestureListener dgl) - { - return (T) new FXDragGestureRecognizer(ds, c, srcActions, dgl); - } - - public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException - { - return new FXDragSourceContextPeer(dge); - } - - - - - - /////////////////////////////////////////////////////////////////////////// - // DROP TARGET IMPLEMENTATION - /////////////////////////////////////////////////////////////////////////// - - - private boolean isDropTargetListenerInstalled = false; - private volatile FXDropTargetContextPeer activeDTContextPeer = null; - private final Map dropTargets = new HashMap<>(); - - private final EventHandler onDragEnteredHandler = (event) -> { - if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); - - int action = activeDTContextPeer.postDropTargetEvent(event); - - // If AWT doesn't accept anything, let parent nodes handle the event - if (action != 0) event.consume(); - }; - - private final EventHandler onDragExitedHandler = (event) -> { - if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); - - activeDTContextPeer.postDropTargetEvent(event); - - activeDTContextPeer = null; - }; - - private final EventHandler onDragOverHandler = (event) -> { - if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); - - int action = activeDTContextPeer.postDropTargetEvent(event); - - // If AWT doesn't accept anything, let parent nodes handle the event - if (action != 0) { - // NOTE: in FX the acceptTransferModes() may ONLY be called from DRAG_OVER. - // If the AWT app always reports NONE and suddenly decides to accept the - // data in its DRAG_DROPPED handler, this just won't work. There's no way - // to workaround this other than by modifing the AWT application code. - event.acceptTransferModes(SwingDnD.dropActionsToTransferModes(action).toArray(new TransferMode[1])); - event.consume(); - } - }; - - private final EventHandler onDragDroppedHandler = (event) -> { - if (activeDTContextPeer == null) activeDTContextPeer = new FXDropTargetContextPeer(); - - int action = activeDTContextPeer.postDropTargetEvent(event); - - if (action != 0) { - // NOTE: the dropAction is ignored since we use the action last - // reported from the DRAG_OVER handler. - // - // We might want to: - // - // assert activeDTContextPeer.dropAction == onDragDroppedHandler.currentAction; - // - // and maybe print a diagnostic message if they differ. - event.setDropCompleted(activeDTContextPeer.success); - - event.consume(); - } - - activeDTContextPeer = null; - }; - - private final class FXDropTargetContextPeer implements DropTargetContextPeer { - - private int targetActions = DnDConstants.ACTION_NONE; - private int currentAction = DnDConstants.ACTION_NONE; - private DropTarget dt = null; - private DropTargetContext ctx = null; - - private final CachingTransferable transferable = new CachingTransferable(); - - // Drop result - private boolean success = false; - private int dropAction = 0; - - @Override public synchronized void setTargetActions(int actions) { targetActions = actions; } - @Override public synchronized int getTargetActions() { return targetActions; } - - @Override public synchronized DropTarget getDropTarget() { return dt; } - - @Override public synchronized boolean isTransferableJVMLocal() { return false; } - - @Override public synchronized DataFlavor[] getTransferDataFlavors() { return transferable.getTransferDataFlavors(); } - @Override public synchronized Transferable getTransferable() { return transferable; } - - @Override public synchronized void acceptDrag(int dragAction) { currentAction = dragAction; } - @Override public synchronized void rejectDrag() { currentAction = DnDConstants.ACTION_NONE; } - - @Override public synchronized void acceptDrop(int dropAction) { this.dropAction = dropAction; } - @Override public synchronized void rejectDrop() { dropAction = DnDConstants.ACTION_NONE; } - - @Override public synchronized void dropComplete(boolean success) { this.success = success; } - - - private int postDropTargetEvent(DragEvent event) - { - ComponentMapper mapper = mapComponent(dropTargets, (int)event.getX(), (int)event.getY()); - - final EventType fxEvType = event.getEventType(); - - Dragboard db = event.getDragboard(); - transferable.updateData(db, DragEvent.DRAG_DROPPED.equals(fxEvType)); - - final int sourceActions = SwingDnD.transferModesToDropActions(db.getTransferModes()); - final int userAction = event.getTransferMode() == null ? DnDConstants.ACTION_NONE - : SwingDnD.transferModeToDropAction(event.getTransferMode()); - - // A target for the AWT DnD event - DropTarget target = mapper.object != null ? mapper.object : dt; - - SwingFXUtils.runOnEDTAndWait(FXDnD.this, () -> { - if (target != dt) { - if (ctx != null) { - AWTAccessor.getDropTargetContextAccessor().reset(ctx); - } - ctx = null; - - currentAction = dropAction = DnDConstants.ACTION_NONE; - } - - if (target != null) { - if (ctx == null) { - ctx = target.getDropTargetContext(); - AWTAccessor.getDropTargetContextAccessor() - .setDropTargetContextPeer(ctx, FXDropTargetContextPeer.this); - } - - DropTargetListener dtl = (DropTargetListener)target; - - if (DragEvent.DRAG_DROPPED.equals(fxEvType)) { - DropTargetDropEvent awtEvent = new DropTargetDropEvent( - ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); - - dtl.drop(awtEvent); - } else { - DropTargetDragEvent awtEvent = new DropTargetDragEvent( - ctx, new Point(mapper.x, mapper.y), userAction, sourceActions); - - if (DragEvent.DRAG_OVER.equals(fxEvType)) dtl.dragOver(awtEvent); - else if (DragEvent.DRAG_ENTERED.equals(fxEvType)) dtl.dragEnter(awtEvent); - else if (DragEvent.DRAG_EXITED.equals(fxEvType)) dtl.dragExit(awtEvent); - } - } - - dt = mapper.object; - if (dt == null) { - // FIXME: once we switch to JDK 9 as the boot JDK - // we need to re-implement the following using - // available API. - /* - if (ctx != null) ctx.removeNotify(); - */ - ctx = null; - - currentAction = dropAction = DnDConstants.ACTION_NONE; - } - if (DragEvent.DRAG_DROPPED.equals(fxEvType) || DragEvent.DRAG_EXITED.equals(fxEvType)) { - // This must be done to ensure that the data isn't being - // cached in AWT. Otherwise subsequent DnD operations will - // see the old data only. - // FIXME: once we switch to JDK 9 as the boot JDK - // we need to re-implement the following using - // available API. - /* - if (ctx != null) ctx.removeNotify(); - */ - ctx = null; - } - - SwingFXUtils.leaveFXNestedLoop(FXDnD.this); - }); - - if (DragEvent.DRAG_DROPPED.equals(fxEvType)) return dropAction; - - return currentAction; - } - } - - public void addDropTarget(DropTarget dt) { - dropTargets.put(dt.getComponent(), dt); - Platform.runLater(() -> { - if (!isDropTargetListenerInstalled) { - node.addEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); - node.addEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); - node.addEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); - node.addEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); - - isDropTargetListenerInstalled = true; - } - }); - } - - public void removeDropTarget(DropTarget dt) { - dropTargets.remove(dt.getComponent()); - Platform.runLater(() -> { - if (isDropTargetListenerInstalled && dropTargets.isEmpty()) { - node.removeEventHandler(DragEvent.DRAG_ENTERED, onDragEnteredHandler); - node.removeEventHandler(DragEvent.DRAG_EXITED, onDragExitedHandler); - node.removeEventHandler(DragEvent.DRAG_OVER, onDragOverHandler); - node.removeEventHandler(DragEvent.DRAG_DROPPED, onDragDroppedHandler); - - isDropTargetListenerInstalled = false; - } - }); - } -} --- /dev/null 2018-07-10 15:05:51.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/FXDnD.java 2018-07-10 15:05:49.853103800 +0530 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import com.sun.javafx.tk.Toolkit; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Point; +import java.awt.SecondaryLoop; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; +import javafx.application.Platform; +import javafx.event.EventHandler; +import javafx.event.EventType; +import javafx.scene.input.DataFormat; +import javafx.scene.input.DragEvent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TransferMode; + +import javafx.embed.swing.SwingNode; + +/** + * A utility class to connect DnD mechanism of Swing and FX. + * It allows Swing content to use the FX machinery for performing DnD. + */ +final public class FXDnD { + private static SwingNode node; + public static boolean fxAppThreadIsDispatchThread; + private SwingNode getNode() { return node; } + private static FXDnDInterop fxdndiop; + + static { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + fxAppThreadIsDispatchThread = + "true".equals(System.getProperty("javafx.embed.singleThread")); + return null; + } + }); + + InteropFactory instance = null; + try { + instance = InteropFactory.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + fxdndiop = instance.createFXDnDImpl(); + } + + public FXDnD(SwingNode node) { + this.node = node; + fxdndiop.setNode(node); + } + + public Object createDragSourceContext(DragGestureEvent dge) + throws InvalidDnDOperationException { + return fxdndiop.createDragSourceContext(dge); + } + + public T createDragGestureRecognizer( + Class abstractRecognizerClass, + DragSource ds, Component c, int srcActions, + DragGestureListener dgl) + { + return fxdndiop.createDragGestureRecognizer(ds, c, srcActions, dgl); + } + + public void addDropTarget(DropTarget dt) { + fxdndiop.addDropTarget(dt, node); + } + + public void removeDropTarget(DropTarget dt) { + fxdndiop.removeDropTarget(dt, node); + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java 2018-07-10 15:05:56.483445800 +0530 +++ new/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java 2018-07-10 15:05:55.431812200 +0530 @@ -28,6 +28,9 @@ import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; import java.nio.IntBuffer; import java.util.Set; import java.util.HashSet; @@ -41,7 +44,6 @@ import javafx.scene.paint.Color; import com.sun.javafx.tk.Toolkit; import javax.swing.SwingUtilities; -import sun.awt.image.IntegerComponentRaster; /** * This class provides utility methods for converting data types between @@ -111,10 +113,15 @@ wimg = new WritableImage(bw, bh); } PixelWriter pw = wimg.getPixelWriter(); - IntegerComponentRaster icr = (IntegerComponentRaster) bimg.getRaster(); - int data[] = icr.getDataStorage(); - int offset = icr.getDataOffset(0); - int scan = icr.getScanlineStride(); + DataBufferInt db = (DataBufferInt)bimg.getRaster().getDataBuffer(); + int data[] = db.getData(); + int offset = bimg.getRaster().getDataBuffer().getOffset(); + int scan = 0; + SampleModel sm = bimg.getRaster().getSampleModel(); + if (sm instanceof SinglePixelPackedSampleModel) { + scan = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); + } + PixelFormat pf = (bimg.isAlphaPremultiplied() ? PixelFormat.getIntArgbPreInstance() : PixelFormat.getIntArgbInstance()); @@ -281,72 +288,17 @@ if (bimg == null) { bimg = new BufferedImage(iw, ih, prefBimgType); } - IntegerComponentRaster icr = (IntegerComponentRaster) bimg.getRaster(); - int offset = icr.getDataOffset(0); - int scan = icr.getScanlineStride(); - int data[] = icr.getDataStorage(); + DataBufferInt db = (DataBufferInt)bimg.getRaster().getDataBuffer(); + int data[] = db.getData(); + int offset = bimg.getRaster().getDataBuffer().getOffset(); + int scan = 0; + SampleModel sm = bimg.getRaster().getSampleModel(); + if (sm instanceof SinglePixelPackedSampleModel) { + scan = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); + } + WritablePixelFormat pf = getAssociatedPixelFormat(bimg); pr.getPixels(0, 0, iw, ih, pf, data, offset, scan); return bimg; } - - /** - * If called from the FX Application Thread - * invokes a runnable directly blocking the calling code - * Otherwise - * uses Platform.runLater without blocking - */ - static void runOnFxThread(Runnable runnable) { - if (Platform.isFxApplicationThread()) { - runnable.run(); - } else { - Platform.runLater(runnable); - } - } - - /** - * If called from the event dispatch thread - * invokes a runnable directly blocking the calling code - * Otherwise - * uses SwingUtilities.invokeLater without blocking - */ - static void runOnEDT(final Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - private static final Set eventLoopKeys = new HashSet<>(); - - /** - * The runnable is responsible for leaving the nested event loop. - */ - static void runOnEDTAndWait(Object nestedLoopKey, Runnable r) { - Toolkit.getToolkit().checkFxUserThread(); - - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - eventLoopKeys.add(nestedLoopKey); - SwingUtilities.invokeLater(r); - Toolkit.getToolkit().enterNestedEventLoop(nestedLoopKey); - } - } - - static void leaveFXNestedLoop(Object nestedLoopKey) { - if (!eventLoopKeys.contains(nestedLoopKey)) return; - - if (Platform.isFxApplicationThread()) { - Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null); - } else { - Platform.runLater(() -> { - Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null); - }); - } - - eventLoopKeys.remove(nestedLoopKey); - } - } --- old/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingFXUtilsImpl.java 2018-07-10 15:06:01.109533200 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingFXUtilsImpl.java 2018-07-10 15:06:00.312432000 +0530 @@ -25,62 +25,22 @@ package com.sun.javafx.embed.swing; -import com.sun.javafx.application.PlatformImpl; -import com.sun.javafx.tk.Toolkit; import java.awt.EventQueue; -import java.awt.SecondaryLoop; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.concurrent.atomic.AtomicBoolean; -import javafx.application.Platform; -import sun.awt.AWTAccessor; -import sun.awt.FwDispatcher; public class SwingFXUtilsImpl { - private static class FwSecondaryLoop implements SecondaryLoop { + private static SwingFXUtilsImplInterop sfuiop; - private final AtomicBoolean isRunning = new AtomicBoolean(false); - - @Override - public boolean enter() { - if (isRunning.compareAndSet(false, true)) { - PlatformImpl.runAndWait(() -> { - Toolkit.getToolkit().enterNestedEventLoop(FwSecondaryLoop.this); - }); - return true; - } - return false; - } - - @Override - public boolean exit() { - if (isRunning.compareAndSet(true, false)) { - PlatformImpl.runAndWait(() -> { - Toolkit.getToolkit().exitNestedEventLoop(FwSecondaryLoop.this, null); - }); - return true; - } - return false; - } - } - - private static class FXDispatcher implements FwDispatcher { - - @Override - public boolean isDispatchThread() { - return Platform.isFxApplicationThread(); - } - - @Override - public void scheduleDispatch(Runnable runnable) { - Platform.runLater(runnable); - } - - @Override - public SecondaryLoop createSecondaryLoop() { - return new FwSecondaryLoop(); + static { + InteropFactory instance = null; + try { + instance = InteropFactory.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); } + sfuiop = instance.createSwingFXUtilsImpl(); } private static EventQueue getEventQueue() { @@ -90,11 +50,11 @@ //Called with reflection from PlatformImpl to avoid dependency public static void installFwEventQueue() { - AWTAccessor.getEventQueueAccessor().setFwDispatcher(getEventQueue(), new FXDispatcher()); + sfuiop.setFwDispatcher(getEventQueue()); } //Called with reflection from PlatformImpl to avoid dependency public static void removeFwEventQueue() { - AWTAccessor.getEventQueueAccessor().setFwDispatcher(getEventQueue(), null); + sfuiop.setFwDispatcher(getEventQueue()); } } --- /dev/null 2018-07-10 15:06:06.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingFXUtilsImplInterop.java 2018-07-10 15:06:05.190551400 +0530 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import java.awt.EventQueue; + +public abstract class SwingFXUtilsImplInterop { + public abstract void setFwDispatcher(EventQueue eventQueue); +} + --- /dev/null 2018-07-10 15:06:10.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/newimpl/SwingFXUtilsImplInteropN.java 2018-07-10 15:06:08.895521900 +0530 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.newimpl; + +import com.sun.javafx.application.PlatformImpl; +import javafx.application.Platform; +import com.sun.javafx.tk.Toolkit; +import java.awt.EventQueue; +import java.awt.SecondaryLoop; +import java.util.concurrent.atomic.AtomicBoolean; + +import jdk.swing.interop.DispatcherWrapper; +import com.sun.javafx.embed.swing.SwingFXUtilsImplInterop; + +public class SwingFXUtilsImplInteropN extends SwingFXUtilsImplInterop { + + private static class FwSecondaryLoop implements SecondaryLoop { + + private final AtomicBoolean isRunning = new AtomicBoolean(false); + + @Override + public boolean enter() { + if (isRunning.compareAndSet(false, true)) { + PlatformImpl.runAndWait(() -> { + Toolkit.getToolkit().enterNestedEventLoop(FwSecondaryLoop.this); + }); + return true; + } + return false; + } + + @Override + public boolean exit() { + if (isRunning.compareAndSet(true, false)) { + PlatformImpl.runAndWait(() -> { + Toolkit.getToolkit().exitNestedEventLoop(FwSecondaryLoop.this, null); + }); + return true; + } + return false; + } + } + + private static class FXDispatcher extends DispatcherWrapper { + + @Override + public boolean isDispatchThread() { + return Platform.isFxApplicationThread(); + } + + @Override + public void scheduleDispatch(Runnable runnable) { + Platform.runLater(runnable); + } + + @Override + public SecondaryLoop createSecondaryLoop() { + return new FwSecondaryLoop(); + } + } + + public void setFwDispatcher(EventQueue eventQueue) { + DispatcherWrapper.setFwDispatcher(eventQueue, new FXDispatcher()); + } +} + --- /dev/null 2018-07-10 15:06:13.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/oldimpl/SwingFXUtilsImplInteropO.java 2018-07-10 15:06:12.249447800 +0530 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing.oldimpl; + +import com.sun.javafx.application.PlatformImpl; +import javafx.application.Platform; +import com.sun.javafx.tk.Toolkit; +import java.awt.EventQueue; +import java.awt.SecondaryLoop; +import java.util.concurrent.atomic.AtomicBoolean; + +import sun.awt.AWTAccessor; +import sun.awt.FwDispatcher; + +import com.sun.javafx.embed.swing.SwingFXUtilsImplInterop; + +public class SwingFXUtilsImplInteropO extends SwingFXUtilsImplInterop { + + private static class FwSecondaryLoop implements SecondaryLoop { + + private final AtomicBoolean isRunning = new AtomicBoolean(false); + + @Override + public boolean enter() { + if (isRunning.compareAndSet(false, true)) { + PlatformImpl.runAndWait(() -> { + Toolkit.getToolkit().enterNestedEventLoop(FwSecondaryLoop.this); + }); + return true; + } + return false; + } + + @Override + public boolean exit() { + if (isRunning.compareAndSet(true, false)) { + PlatformImpl.runAndWait(() -> { + Toolkit.getToolkit().exitNestedEventLoop(FwSecondaryLoop.this, null); + }); + return true; + } + return false; + } + } + + private static class FXDispatcher implements FwDispatcher { + + @Override + public boolean isDispatchThread() { + return Platform.isFxApplicationThread(); + } + + @Override + public void scheduleDispatch(Runnable runnable) { + Platform.runLater(runnable); + } + + @Override + public SecondaryLoop createSecondaryLoop() { + return new FwSecondaryLoop(); + } + } + + public void setFwDispatcher(EventQueue eventQueue) { + AWTAccessor.getEventQueueAccessor().setFwDispatcher(eventQueue, new FXDispatcher()); + } +} + --- old/modules/javafx.graphics/src/main/java/com/sun/javafx/util/Utils.java 2018-07-10 15:06:17.245582200 +0530 +++ new/modules/javafx.graphics/src/main/java/com/sun/javafx/util/Utils.java 2018-07-10 15:06:16.447480900 +0530 @@ -44,6 +44,8 @@ import com.sun.javafx.PlatformUtil; import java.security.AccessController; import java.security.PrivilegedAction; +import com.sun.glass.utils.NativeLibLoader; +import com.sun.prism.impl.PrismSettings; /** * Some basic utilities which need to be in java (for shifting operations or @@ -961,4 +963,19 @@ return new String(dst, 0, dstIndex); } + + public static synchronized void loadNativeSwingLibrary() { + AccessController.doPrivileged((PrivilegedAction) () -> { + String libName = "prism_common"; + + if (PrismSettings.verbose) { + System.out.println("Loading Prism common native library ..."); + } + NativeLibLoader.loadLibrary(libName); + if (PrismSettings.verbose) { + System.out.println("\tsucceeded."); + } + return null; + }); + } } --- /dev/null 2018-07-10 15:06:21.000000000 +0530 +++ new/modules/javafx.graphics/src/main/native-prism/SwingInterop.c 2018-07-10 15:06:20.803534000 +0530 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +/* + * Class com_sun_javafx_embed_swing_newimpl_SwingNodeInteropN + * Method: overrideNativeWindowHandle + * Signature (Ljava/lang/Class;JLjava/lang/Runnable)Ljdk.swing.interop.LightweightFrameWrapper; + */ +JNIEXPORT jobject +Java_com_sun_javafx_embed_swing_newimpl_SwingNodeInteropN_overrideNativeWindowHandle( + JNIEnv *env, jclass cls, jclass ownerClass, jlong id, jclass runnable) { + + jmethodID cons; + if (ownerClass == NULL) { + return NULL; + } + cons = (*env)->GetMethodID(env, ownerClass, "overrideNativeWindowHandle", + "(JLjava/lang/Runnable;)V"); + if (cons == NULL || (*env)->ExceptionCheck(env)) { + return NULL; + } + return (*env)->NewObject(env, ownerClass, cons, id, runnable); +} + + --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/CachingTransferable.java 2018-07-10 15:06:26.149212900 +0530 +++ /dev/null 2018-07-10 15:06:26.000000000 +0530 @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import com.sun.javafx.embed.EmbeddedSceneDSInterface; -import com.sun.javafx.tk.Toolkit; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; -import java.util.HashMap; -import java.util.Set; -import javafx.scene.input.TransferMode; -import java.io.UnsupportedEncodingException; -import javafx.scene.input.Clipboard; -import javafx.scene.input.DataFormat; - -/** - * A Transferable implementation backed by a Map. - * The data can be populated either from AWT Transferable - * or from FX Clipboard. - */ -class CachingTransferable implements Transferable { - - @Override - public Object getTransferData(final DataFlavor flavor) throws UnsupportedEncodingException - { - String mimeType = DataFlavorUtils.getFxMimeType(flavor); - return DataFlavorUtils.adjustFxData( - flavor, getData(mimeType)); - } - - @Override - public DataFlavor[] getTransferDataFlavors() { - final String mimeTypes[] = getMimeTypes(); - return DataFlavorUtils.getDataFlavors(mimeTypes); - } - - @Override - public boolean isDataFlavorSupported(final DataFlavor flavor) { - return isMimeTypeAvailable( - DataFlavorUtils.getFxMimeType(flavor)); - } - - private Map mimeType2Data = Collections.EMPTY_MAP; - - void updateData(Transferable t, boolean fetchData) { - final Map mimeType2DataFlavor = - DataFlavorUtils.adjustSwingDataFlavors( - t.getTransferDataFlavors()); - - // If we keep reference to source Transferable in SwingDragSource and - // call Transferable#getTransferData() on it from - // SwingDragSource#getData() we may run into - // "java.awt.dnd.InvalidDnDOperationException" issue as - // SwingDragSource#getData() is called from FX user code and from - // QuantumClipboard#getContent() (sik!). These calls usually take - // place in the context of - // EmbeddedSceneDTInterface#handleDragDrop() method as the - // normal handling of DnD. - // Instead of keeping reference to source Transferable we just read - // all its data while in the context safe for calling - // Transferable#getTransferData(). - // - // This observation is true for standard AWT Transferable-s. - // Things may be totally broken for custom Transferable-s though. - - // For performance reasons, the DRAG_ENTERED and DRAG_OVER event - // handlers pass fetchData == false so as to update the set of - // available MIME types only. The DRAG_DROPPED handler passes - // fetchData == true which also fetches all the data. - // NOTE: Due to JDK-8028585 this code won't be able to fetch data - // when invoked from handlers other than DROPPED in any case. - - try { - mimeType2Data = DataFlavorUtils.readAllData(t, mimeType2DataFlavor, - fetchData); - } catch (Exception e) { - mimeType2Data = Collections.EMPTY_MAP; - } - } - - void updateData(Clipboard cb, boolean fetchData) { - mimeType2Data = new HashMap<>(); - for (DataFormat f : cb.getContentTypes()) { - mimeType2Data.put(DataFlavorUtils.getMimeType(f), - fetchData ? cb.getContent(f) : null); - } - } - - public Object getData(final String mimeType) { - return mimeType2Data.get(mimeType); - } - - public String[] getMimeTypes() { - return mimeType2Data.keySet().toArray(new String[0]); - } - - public boolean isMimeTypeAvailable(final String mimeType) { - return Arrays.asList(getMimeTypes()).contains(mimeType); - } -} --- /dev/null 2018-07-10 15:06:26.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/CachingTransferable.java 2018-07-10 15:06:24.497003100 +0530 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import com.sun.javafx.embed.EmbeddedSceneDSInterface; +import com.sun.javafx.tk.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import javafx.scene.input.TransferMode; +import java.io.UnsupportedEncodingException; +import javafx.scene.input.Clipboard; +import javafx.scene.input.DataFormat; + +/** + * A Transferable implementation backed by a Map. + * The data can be populated either from AWT Transferable + * or from FX Clipboard. + */ +public class CachingTransferable implements Transferable { + + @Override + public Object getTransferData(final DataFlavor flavor) throws UnsupportedEncodingException + { + String mimeType = DataFlavorUtils.getFxMimeType(flavor); + return DataFlavorUtils.adjustFxData( + flavor, getData(mimeType)); + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + final String mimeTypes[] = getMimeTypes(); + return DataFlavorUtils.getDataFlavors(mimeTypes); + } + + @Override + public boolean isDataFlavorSupported(final DataFlavor flavor) { + return isMimeTypeAvailable( + DataFlavorUtils.getFxMimeType(flavor)); + } + + private Map mimeType2Data = Collections.EMPTY_MAP; + + public void updateData(Transferable t, boolean fetchData) { + final Map mimeType2DataFlavor = + DataFlavorUtils.adjustSwingDataFlavors( + t.getTransferDataFlavors()); + + // If we keep reference to source Transferable in SwingDragSource and + // call Transferable#getTransferData() on it from + // SwingDragSource#getData() we may run into + // "java.awt.dnd.InvalidDnDOperationException" issue as + // SwingDragSource#getData() is called from FX user code and from + // QuantumClipboard#getContent() (sik!). These calls usually take + // place in the context of + // EmbeddedSceneDTInterface#handleDragDrop() method as the + // normal handling of DnD. + // Instead of keeping reference to source Transferable we just read + // all its data while in the context safe for calling + // Transferable#getTransferData(). + // + // This observation is true for standard AWT Transferable-s. + // Things may be totally broken for custom Transferable-s though. + + // For performance reasons, the DRAG_ENTERED and DRAG_OVER event + // handlers pass fetchData == false so as to update the set of + // available MIME types only. The DRAG_DROPPED handler passes + // fetchData == true which also fetches all the data. + // NOTE: Due to JDK-8028585 this code won't be able to fetch data + // when invoked from handlers other than DROPPED in any case. + + try { + mimeType2Data = DataFlavorUtils.readAllData(t, mimeType2DataFlavor, + fetchData); + } catch (Exception e) { + mimeType2Data = Collections.EMPTY_MAP; + } + } + + public void updateData(Clipboard cb, boolean fetchData) { + mimeType2Data = new HashMap<>(); + for (DataFormat f : cb.getContentTypes()) { + mimeType2Data.put(DataFlavorUtils.getMimeType(f), + fetchData ? cb.getContent(f) : null); + } + } + + public Object getData(final String mimeType) { + return mimeType2Data.get(mimeType); + } + + public String[] getMimeTypes() { + return mimeType2Data.keySet().toArray(new String[0]); + } + + public boolean isMimeTypeAvailable(final String mimeType) { + return Arrays.asList(getMimeTypes()).contains(mimeType); + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/DataFlavorUtils.java 2018-07-10 15:06:31.040834000 +0530 +++ /dev/null 2018-07-10 15:06:31.000000000 +0530 @@ -1,281 +0,0 @@ -/* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import javafx.scene.input.DataFormat; - -import java.io.ByteArrayOutputStream; -import java.util.Set; -import java.util.Map; -import java.util.List; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Collections; -import java.util.ArrayList; - -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; - -import java.nio.ByteBuffer; - - -final class DataFlavorUtils { - - static String getFxMimeType(final DataFlavor flavor) { - return flavor.getPrimaryType() + "/" + flavor.getSubType(); - } - - static DataFlavor[] getDataFlavors(String[] mimeTypes) { - final ArrayList flavors = - new ArrayList(mimeTypes.length); - for (String mime : mimeTypes) { - DataFlavor flavor = null; - try { - flavor = new DataFlavor(mime); - } catch (ClassNotFoundException | IllegalArgumentException e) { - continue; - } - flavors.add(flavor); - } - return flavors.toArray(new DataFlavor[0]); - } - - static DataFlavor getDataFlavor(final DataFormat format) { - DataFlavor[] flavors = getDataFlavors(format.getIdentifiers().toArray(new String[1])); - - // Well, that's our best guess... - return flavors.length == 0 ? null : flavors[0]; - } - - static String getMimeType(final DataFormat format) { - // Well, that's our best guess... - for (String id : format.getIdentifiers()) return id; - return null; - } - - static DataFormat getDataFormat(final DataFlavor flavor) { - String mimeType = getFxMimeType(flavor); - DataFormat dataFormat = DataFormat.lookupMimeType(mimeType); - if (dataFormat == null) { - dataFormat = new DataFormat(mimeType); // are we ready for this yet? - } - return dataFormat; - } - - /** - * InputStream implementation backed by a ByteBuffer. - * It can handle byte buffers that are backed by arrays - * as well as operating system memory. - */ - private static class ByteBufferInputStream extends InputStream { - private final ByteBuffer bb; - - private ByteBufferInputStream(ByteBuffer bb) { this.bb = bb; } - - @Override public int available() { return bb.remaining(); } - - @Override public int read() throws IOException { - if (!bb.hasRemaining()) return -1; - return bb.get() & 0xFF; // Make sure the value is in [0..255] - } - - @Override public int read(byte[] bytes, int off, int len) throws IOException { - if (!bb.hasRemaining()) return -1; - len = Math.min(len, bb.remaining()); - bb.get(bytes, off, len); - return len; - } - } - - static Object adjustFxData(final DataFlavor flavor, final Object fxData) - throws UnsupportedEncodingException - { - // TBD: Handle more data types!!! - if (fxData instanceof String) { - if (flavor.isRepresentationClassInputStream()) { - final String encoding = flavor.getParameter("charset"); - return new ByteArrayInputStream(encoding != null - ? ((String) fxData).getBytes(encoding) - : ((String) fxData).getBytes()); - } - if (flavor.isRepresentationClassByteBuffer()) { - // ... - } - } - if (fxData instanceof ByteBuffer) { - if (flavor.isRepresentationClassInputStream()) { - return new ByteBufferInputStream((ByteBuffer)fxData); - } - } - return fxData; - } - - static Object adjustSwingData(final DataFlavor flavor, - final String mimeType, - final Object swingData) - { - if (swingData == null) { - return swingData; - } - - if (flavor.isFlavorJavaFileListType()) { - // RT-12663 - final List fileList = (List)swingData; - final String[] paths = new String[fileList.size()]; - int i = 0; - for (File f : fileList) { - paths[i++] = f.getPath(); - } - return paths; - } - DataFormat dataFormat = DataFormat.lookupMimeType(mimeType); - if (DataFormat.PLAIN_TEXT.equals(dataFormat)) { - if (flavor.isFlavorTextType()) { - if (swingData instanceof InputStream) { - InputStream in = (InputStream)swingData; - // TBD: charset - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] bb = new byte[64]; - try { - int len = in.read(bb); - while (len != -1) { - out.write(bb, 0, len); - len = in.read(bb); - } - out.close(); - return new String(out.toByteArray()); - } catch (Exception z) { - // ignore - } - } - } else if (swingData != null) { - return swingData.toString(); - } - } - return swingData; - } - - static Map adjustSwingDataFlavors(final DataFlavor[] flavors) { - // Group data flavors by FX mime type. - final Map> mimeType2Flavors = - new HashMap<>(flavors.length); - for (DataFlavor flavor : flavors) { - final String mimeType = getFxMimeType(flavor); - if (mimeType2Flavors.containsKey(mimeType)) { - final Set mimeTypeFlavors = mimeType2Flavors.get( - mimeType); - try { - mimeTypeFlavors.add(flavor); - } catch (UnsupportedOperationException e) { - // List of data flavors corresponding to FX mime - // type has been finalized already. - } - } else { - Set mimeTypeFlavors = new HashSet(); - - // If this is text data flavor use DataFlavor representing - // a Java Unicode String class. This is what FX expects from - // clipboard. - if (flavor.isFlavorTextType()) { - mimeTypeFlavors.add(DataFlavor.stringFlavor); - mimeTypeFlavors = Collections.unmodifiableSet( - mimeTypeFlavors); - } else { - mimeTypeFlavors.add(flavor); - } - - mimeType2Flavors.put(mimeType, mimeTypeFlavors); - } - } - - // Choose the best data flavor corresponding to the given FX mime type - final Map mimeType2Flavor = new HashMap<>(); - for (String mimeType : mimeType2Flavors.keySet()) { - final DataFlavor[] mimeTypeFlavors = mimeType2Flavors.get(mimeType). - toArray(new DataFlavor[0]); - if (mimeTypeFlavors.length == 1) { - mimeType2Flavor.put(mimeType, mimeTypeFlavors[0]); - } else { - // TBD: something better!!! - mimeType2Flavor.put(mimeType, mimeTypeFlavors[0]); - } - } - - return mimeType2Flavor; - } - - private static Object readData(final Transferable t, final DataFlavor flavor) { - Object obj = null; - try { - obj = t.getTransferData(flavor); - } catch (UnsupportedFlavorException ex) { - // FIXME: report error - ex.printStackTrace(System.err); - } catch (IOException ex) { - // FIXME: report error - ex.printStackTrace(System.err); - } - return obj; - } - - /** - * Returns a Map populated with keys corresponding to all the MIME types - * available in the provided Transferable object. If fetchData is true, - * then the data is fetched as well, otherwise all the values are set to - * null. - */ - static Map readAllData(final Transferable t, - final Map fxMimeType2DataFlavor, - final boolean fetchData) - { - final Map fxMimeType2Data = new HashMap<>(); - for (DataFlavor flavor : t.getTransferDataFlavors()) { - Object obj = fetchData ? readData(t, flavor) : null; - if (obj != null || !fetchData) { - String mimeType = getFxMimeType(flavor); - obj = adjustSwingData(flavor, mimeType, obj); - fxMimeType2Data.put(mimeType, obj); - } - } - for (Map.Entry e: fxMimeType2DataFlavor.entrySet()) { - String mimeType = e.getKey(); - DataFlavor flavor = e.getValue(); - Object obj = fetchData ? readData(t, flavor) : null; - if (obj != null || !fetchData) { - obj = adjustSwingData(flavor, mimeType, obj); - fxMimeType2Data.put(e.getKey(), obj); - } - } - return fxMimeType2Data; - } -} --- /dev/null 2018-07-10 15:06:31.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/DataFlavorUtils.java 2018-07-10 15:06:29.629154800 +0530 @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import javafx.scene.input.DataFormat; + +import java.io.ByteArrayOutputStream; +import java.util.Set; +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Collections; +import java.util.ArrayList; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import java.nio.ByteBuffer; + + +final class DataFlavorUtils { + + static String getFxMimeType(final DataFlavor flavor) { + return flavor.getPrimaryType() + "/" + flavor.getSubType(); + } + + static DataFlavor[] getDataFlavors(String[] mimeTypes) { + final ArrayList flavors = + new ArrayList(mimeTypes.length); + for (String mime : mimeTypes) { + DataFlavor flavor = null; + try { + flavor = new DataFlavor(mime); + } catch (ClassNotFoundException | IllegalArgumentException e) { + continue; + } + flavors.add(flavor); + } + return flavors.toArray(new DataFlavor[0]); + } + + static DataFlavor getDataFlavor(final DataFormat format) { + DataFlavor[] flavors = getDataFlavors(format.getIdentifiers().toArray(new String[1])); + + // Well, that's our best guess... + return flavors.length == 0 ? null : flavors[0]; + } + + static String getMimeType(final DataFormat format) { + // Well, that's our best guess... + for (String id : format.getIdentifiers()) return id; + return null; + } + + static DataFormat getDataFormat(final DataFlavor flavor) { + String mimeType = getFxMimeType(flavor); + DataFormat dataFormat = DataFormat.lookupMimeType(mimeType); + if (dataFormat == null) { + dataFormat = new DataFormat(mimeType); // are we ready for this yet? + } + return dataFormat; + } + + /** + * InputStream implementation backed by a ByteBuffer. + * It can handle byte buffers that are backed by arrays + * as well as operating system memory. + */ + private static class ByteBufferInputStream extends InputStream { + private final ByteBuffer bb; + + private ByteBufferInputStream(ByteBuffer bb) { this.bb = bb; } + + @Override public int available() { return bb.remaining(); } + + @Override public int read() throws IOException { + if (!bb.hasRemaining()) return -1; + return bb.get() & 0xFF; // Make sure the value is in [0..255] + } + + @Override public int read(byte[] bytes, int off, int len) throws IOException { + if (!bb.hasRemaining()) return -1; + len = Math.min(len, bb.remaining()); + bb.get(bytes, off, len); + return len; + } + } + + static Object adjustFxData(final DataFlavor flavor, final Object fxData) + throws UnsupportedEncodingException + { + // TBD: Handle more data types!!! + if (fxData instanceof String) { + if (flavor.isRepresentationClassInputStream()) { + final String encoding = flavor.getParameter("charset"); + return new ByteArrayInputStream(encoding != null + ? ((String) fxData).getBytes(encoding) + : ((String) fxData).getBytes()); + } + if (flavor.isRepresentationClassByteBuffer()) { + // ... + } + } + if (fxData instanceof ByteBuffer) { + if (flavor.isRepresentationClassInputStream()) { + return new ByteBufferInputStream((ByteBuffer)fxData); + } + } + return fxData; + } + + static Object adjustSwingData(final DataFlavor flavor, + final String mimeType, + final Object swingData) + { + if (swingData == null) { + return swingData; + } + + if (flavor.isFlavorJavaFileListType()) { + // RT-12663 + final List fileList = (List)swingData; + final String[] paths = new String[fileList.size()]; + int i = 0; + for (File f : fileList) { + paths[i++] = f.getPath(); + } + return paths; + } + DataFormat dataFormat = DataFormat.lookupMimeType(mimeType); + if (DataFormat.PLAIN_TEXT.equals(dataFormat)) { + if (flavor.isFlavorTextType()) { + if (swingData instanceof InputStream) { + InputStream in = (InputStream)swingData; + // TBD: charset + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] bb = new byte[64]; + try { + int len = in.read(bb); + while (len != -1) { + out.write(bb, 0, len); + len = in.read(bb); + } + out.close(); + return new String(out.toByteArray()); + } catch (Exception z) { + // ignore + } + } + } else if (swingData != null) { + return swingData.toString(); + } + } + return swingData; + } + + static Map adjustSwingDataFlavors(final DataFlavor[] flavors) { + // Group data flavors by FX mime type. + final Map> mimeType2Flavors = + new HashMap<>(flavors.length); + for (DataFlavor flavor : flavors) { + final String mimeType = getFxMimeType(flavor); + if (mimeType2Flavors.containsKey(mimeType)) { + final Set mimeTypeFlavors = mimeType2Flavors.get( + mimeType); + try { + mimeTypeFlavors.add(flavor); + } catch (UnsupportedOperationException e) { + // List of data flavors corresponding to FX mime + // type has been finalized already. + } + } else { + Set mimeTypeFlavors = new HashSet(); + + // If this is text data flavor use DataFlavor representing + // a Java Unicode String class. This is what FX expects from + // clipboard. + if (flavor.isFlavorTextType()) { + mimeTypeFlavors.add(DataFlavor.stringFlavor); + mimeTypeFlavors = Collections.unmodifiableSet( + mimeTypeFlavors); + } else { + mimeTypeFlavors.add(flavor); + } + + mimeType2Flavors.put(mimeType, mimeTypeFlavors); + } + } + + // Choose the best data flavor corresponding to the given FX mime type + final Map mimeType2Flavor = new HashMap<>(); + for (String mimeType : mimeType2Flavors.keySet()) { + final DataFlavor[] mimeTypeFlavors = mimeType2Flavors.get(mimeType). + toArray(new DataFlavor[0]); + if (mimeTypeFlavors.length == 1) { + mimeType2Flavor.put(mimeType, mimeTypeFlavors[0]); + } else { + // TBD: something better!!! + mimeType2Flavor.put(mimeType, mimeTypeFlavors[0]); + } + } + + return mimeType2Flavor; + } + + private static Object readData(final Transferable t, final DataFlavor flavor) { + Object obj = null; + try { + obj = t.getTransferData(flavor); + } catch (UnsupportedFlavorException ex) { + // FIXME: report error + ex.printStackTrace(System.err); + } catch (IOException ex) { + // FIXME: report error + ex.printStackTrace(System.err); + } + return obj; + } + + /** + * Returns a Map populated with keys corresponding to all the MIME types + * available in the provided Transferable object. If fetchData is true, + * then the data is fetched as well, otherwise all the values are set to + * null. + */ + static Map readAllData(final Transferable t, + final Map fxMimeType2DataFlavor, + final boolean fetchData) + { + final Map fxMimeType2Data = new HashMap<>(); + for (DataFlavor flavor : t.getTransferDataFlavors()) { + Object obj = fetchData ? readData(t, flavor) : null; + if (obj != null || !fetchData) { + String mimeType = getFxMimeType(flavor); + obj = adjustSwingData(flavor, mimeType, obj); + fxMimeType2Data.put(mimeType, obj); + } + } + for (Map.Entry e: fxMimeType2DataFlavor.entrySet()) { + String mimeType = e.getKey(); + DataFlavor flavor = e.getValue(); + Object obj = fetchData ? readData(t, flavor) : null; + if (obj != null || !fetchData) { + obj = adjustSwingData(flavor, mimeType, obj); + fxMimeType2Data.put(e.getKey(), obj); + } + } + return fxMimeType2Data; + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingCursors.java 2018-07-10 15:06:36.541032500 +0530 +++ /dev/null 2018-07-10 15:06:36.000000000 +0530 @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Toolkit; -import java.awt.image.BufferedImage; - -import com.sun.javafx.cursor.CursorFrame; -import com.sun.javafx.cursor.ImageCursorFrame; - -/** - * An utility class to translate cursor types between embedded - * application and Swing. - * - */ -class SwingCursors { - - private static Cursor createCustomCursor(ImageCursorFrame cursorFrame) { - Toolkit awtToolkit = Toolkit.getDefaultToolkit(); - - double imageWidth = cursorFrame.getWidth(); - double imageHeight = cursorFrame.getHeight(); - Dimension nativeSize = awtToolkit.getBestCursorSize((int)imageWidth, (int)imageHeight); - - double scaledHotspotX = cursorFrame.getHotspotX() * nativeSize.getWidth() / imageWidth; - double scaledHotspotY = cursorFrame.getHotspotY() * nativeSize.getHeight() / imageHeight; - Point hotspot = new Point((int)scaledHotspotX, (int)scaledHotspotY); - - BufferedImage awtImage = SwingFXUtils.fromFXImage( - com.sun.javafx.tk.Toolkit.getImageAccessor().fromPlatformImage(cursorFrame.getPlatformImage()), null); - return awtToolkit.createCustomCursor(awtImage, hotspot, null); - } - - static Cursor embedCursorToCursor(CursorFrame cursorFrame) { - switch (cursorFrame.getCursorType()) { - case DEFAULT: - return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); - case CROSSHAIR: - return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); - case TEXT: - return Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); - case WAIT: - return Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); - case SW_RESIZE: - return Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR); - case SE_RESIZE: - return Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR); - case NW_RESIZE: - return Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR); - case NE_RESIZE: - return Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR); - case N_RESIZE: - case V_RESIZE: - return Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR); - case S_RESIZE: - return Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR); - case W_RESIZE: - case H_RESIZE: - return Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR); - case E_RESIZE: - return Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); - case OPEN_HAND: - case CLOSED_HAND: - case HAND: - return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); - case MOVE: - return Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); - // Not implemented, use default cursor instead - case DISAPPEAR: - return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); - case NONE: - return null; - case IMAGE: - return createCustomCursor((ImageCursorFrame) cursorFrame); - } - - return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); - } - - static javafx.scene.Cursor embedCursorToCursor(Cursor cursor) { - if (cursor == null) { - return javafx.scene.Cursor.DEFAULT; - } - - switch (cursor.getType()) { - case Cursor.DEFAULT_CURSOR: - return javafx.scene.Cursor.DEFAULT; - case Cursor.CROSSHAIR_CURSOR: - return javafx.scene.Cursor.CROSSHAIR; - case Cursor.E_RESIZE_CURSOR: - return javafx.scene.Cursor.E_RESIZE; - case Cursor.HAND_CURSOR: - return javafx.scene.Cursor.HAND; - case Cursor.MOVE_CURSOR: - return javafx.scene.Cursor.MOVE; - case Cursor.N_RESIZE_CURSOR: - return javafx.scene.Cursor.N_RESIZE; - case Cursor.NE_RESIZE_CURSOR: - return javafx.scene.Cursor.NE_RESIZE; - case Cursor.NW_RESIZE_CURSOR: - return javafx.scene.Cursor.NW_RESIZE; - case Cursor.S_RESIZE_CURSOR: - return javafx.scene.Cursor.S_RESIZE; - case Cursor.SE_RESIZE_CURSOR: - return javafx.scene.Cursor.SE_RESIZE; - case Cursor.SW_RESIZE_CURSOR: - return javafx.scene.Cursor.SW_RESIZE; - case Cursor.TEXT_CURSOR: - return javafx.scene.Cursor.TEXT; - case Cursor.W_RESIZE_CURSOR: - return javafx.scene.Cursor.W_RESIZE; - case Cursor.WAIT_CURSOR: - return javafx.scene.Cursor.WAIT; - default: - return javafx.scene.Cursor.DEFAULT; - } - } -} --- /dev/null 2018-07-10 15:06:36.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingCursors.java 2018-07-10 15:06:34.874320800 +0530 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; + +import com.sun.javafx.cursor.CursorFrame; +import com.sun.javafx.cursor.ImageCursorFrame; + +import javafx.embed.swing.SwingFXUtils; + +/** + * An utility class to translate cursor types between embedded + * application and Swing. + * + */ +public class SwingCursors { + + private static Cursor createCustomCursor(ImageCursorFrame cursorFrame) { + Toolkit awtToolkit = Toolkit.getDefaultToolkit(); + + double imageWidth = cursorFrame.getWidth(); + double imageHeight = cursorFrame.getHeight(); + Dimension nativeSize = awtToolkit.getBestCursorSize((int)imageWidth, (int)imageHeight); + + double scaledHotspotX = cursorFrame.getHotspotX() * nativeSize.getWidth() / imageWidth; + double scaledHotspotY = cursorFrame.getHotspotY() * nativeSize.getHeight() / imageHeight; + Point hotspot = new Point((int)scaledHotspotX, (int)scaledHotspotY); + + BufferedImage awtImage = SwingFXUtils.fromFXImage( + com.sun.javafx.tk.Toolkit.getImageAccessor().fromPlatformImage(cursorFrame.getPlatformImage()), null); + return awtToolkit.createCustomCursor(awtImage, hotspot, null); + } + + public static Cursor embedCursorToCursor(CursorFrame cursorFrame) { + switch (cursorFrame.getCursorType()) { + case DEFAULT: + return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); + case CROSSHAIR: + return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); + case TEXT: + return Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); + case WAIT: + return Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); + case SW_RESIZE: + return Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR); + case SE_RESIZE: + return Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR); + case NW_RESIZE: + return Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR); + case NE_RESIZE: + return Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR); + case N_RESIZE: + case V_RESIZE: + return Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR); + case S_RESIZE: + return Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR); + case W_RESIZE: + case H_RESIZE: + return Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR); + case E_RESIZE: + return Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); + case OPEN_HAND: + case CLOSED_HAND: + case HAND: + return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + case MOVE: + return Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + // Not implemented, use default cursor instead + case DISAPPEAR: + return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); + case NONE: + return null; + case IMAGE: + return createCustomCursor((ImageCursorFrame) cursorFrame); + } + + return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); + } + + public static javafx.scene.Cursor embedCursorToCursor(Cursor cursor) { + if (cursor == null) { + return javafx.scene.Cursor.DEFAULT; + } + + switch (cursor.getType()) { + case Cursor.DEFAULT_CURSOR: + return javafx.scene.Cursor.DEFAULT; + case Cursor.CROSSHAIR_CURSOR: + return javafx.scene.Cursor.CROSSHAIR; + case Cursor.E_RESIZE_CURSOR: + return javafx.scene.Cursor.E_RESIZE; + case Cursor.HAND_CURSOR: + return javafx.scene.Cursor.HAND; + case Cursor.MOVE_CURSOR: + return javafx.scene.Cursor.MOVE; + case Cursor.N_RESIZE_CURSOR: + return javafx.scene.Cursor.N_RESIZE; + case Cursor.NE_RESIZE_CURSOR: + return javafx.scene.Cursor.NE_RESIZE; + case Cursor.NW_RESIZE_CURSOR: + return javafx.scene.Cursor.NW_RESIZE; + case Cursor.S_RESIZE_CURSOR: + return javafx.scene.Cursor.S_RESIZE; + case Cursor.SE_RESIZE_CURSOR: + return javafx.scene.Cursor.SE_RESIZE; + case Cursor.SW_RESIZE_CURSOR: + return javafx.scene.Cursor.SW_RESIZE; + case Cursor.TEXT_CURSOR: + return javafx.scene.Cursor.TEXT; + case Cursor.W_RESIZE_CURSOR: + return javafx.scene.Cursor.W_RESIZE; + case Cursor.WAIT_CURSOR: + return javafx.scene.Cursor.WAIT; + default: + return javafx.scene.Cursor.DEFAULT; + } + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingDnD.java 2018-07-10 15:06:41.462157400 +0530 +++ /dev/null 2018-07-10 15:06:41.000000000 +0530 @@ -1,413 +0,0 @@ -/* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import java.io.UnsupportedEncodingException; - -import java.util.Collections; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import com.sun.javafx.embed.EmbeddedSceneDSInterface; -import com.sun.javafx.embed.HostDragStartListener; -import javafx.scene.input.TransferMode; - -import com.sun.javafx.embed.EmbeddedSceneInterface; -import com.sun.javafx.embed.EmbeddedSceneDTInterface; -import com.sun.javafx.tk.Toolkit; - -import javax.swing.JComponent; -import javax.swing.SwingUtilities; - -import java.awt.Point; - -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.DragGestureRecognizer; -import java.awt.dnd.DragSource; -import java.awt.dnd.DragSourceAdapter; -import java.awt.dnd.DragSourceListener; -import java.awt.dnd.DragSourceDropEvent; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetAdapter; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.dnd.DropTargetEvent; -import java.awt.dnd.DropTargetListener; -import java.awt.dnd.InvalidDnDOperationException; - -import java.awt.event.InputEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -/** - * An utility class to connect DnD mechanism of Swing and FX. - * It allows FX content to use the AWT machinery for performing DnD. - */ -final class SwingDnD { - - private final Transferable dndTransferable = new DnDTransferable(); - - private final DragSource dragSource; - private final DragSourceListener dragSourceListener; - - // swingDragSource and fxDropTarget are used when DnD is initiated from - // Swing or external process, i.e. this SwingDnD is used as a drop target - private SwingDragSource swingDragSource; - private EmbeddedSceneDTInterface fxDropTarget; - - // fxDragSource is used when DnD is initiated from FX, i.e. this - // SwingDnD acts as a drag source - private EmbeddedSceneDSInterface fxDragSource; - - private MouseEvent me; - - SwingDnD(final JComponent comp, final EmbeddedSceneInterface embeddedScene) { - - comp.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent me) { - storeMouseEvent(me); - } - @Override - public void mouseDragged(MouseEvent me) { - storeMouseEvent(me); - } - @Override - public void mousePressed(MouseEvent me) { - storeMouseEvent(me); - } - @Override - public void mouseReleased(MouseEvent me) { - storeMouseEvent(me); - } - }); - - dragSource = new DragSource(); - dragSourceListener = new DragSourceAdapter() { - @Override - public void dragDropEnd(final DragSourceDropEvent dsde) { - assert fxDragSource != null; - try { - fxDragSource.dragDropEnd(dropActionToTransferMode(dsde.getDropAction())); - } finally { - fxDragSource = null; - } - } - }; - - DropTargetListener dtl = new DropTargetAdapter() { - private TransferMode lastTransferMode; - - @Override - public void dragEnter(final DropTargetDragEvent e) { - // This is a temporary workaround for JDK-8027913 - if ((swingDragSource != null) || (fxDropTarget != null)) { - return; - } - - assert swingDragSource == null; - swingDragSource = new SwingDragSource(); - swingDragSource.updateContents(e, false); - - assert fxDropTarget == null; - // Cache the Transferable data in advance, as it cannot be - // queried from drop(). See comments in dragOver() and in - // drop() below - fxDropTarget = embeddedScene.createDropTarget(); - - final Point orig = e.getLocation(); - final Point screen = new Point(orig); - SwingUtilities.convertPointToScreen(screen, comp); - lastTransferMode = fxDropTarget.handleDragEnter( - orig.x, orig.y, screen.x, screen.y, - dropActionToTransferMode(e.getDropAction()), swingDragSource); - applyDragResult(lastTransferMode, e); - } - - @Override - public void dragExit(final DropTargetEvent e) { - assert swingDragSource != null; - assert fxDropTarget != null; - try { - fxDropTarget.handleDragLeave(); - } finally { - endDnD(); - lastTransferMode = null; - } - } - - @Override - public void dragOver(final DropTargetDragEvent e) { - assert swingDragSource != null; - swingDragSource.updateContents(e, false); - - assert fxDropTarget != null; - final Point orig = e.getLocation(); - final Point screen = new Point(orig); - SwingUtilities.convertPointToScreen(screen, comp); - lastTransferMode = fxDropTarget.handleDragOver( - orig.x, orig.y, screen.x, screen.y, - dropActionToTransferMode(e.getDropAction())); - applyDragResult(lastTransferMode, e); - } - - @Override - public void drop(final DropTargetDropEvent e) { - assert swingDragSource != null; - - // This allows the subsequent call to updateContents() to - // actually fetch the data from a drag source. The actual - // and final drop result may be redefined later. - applyDropResult(lastTransferMode, e); - swingDragSource.updateContents(e, true); - - final Point orig = e.getLocation(); - final Point screen = new Point(orig); - SwingUtilities.convertPointToScreen(screen, comp); - - assert fxDropTarget != null; - try { - lastTransferMode = fxDropTarget.handleDragDrop( - orig.x, orig.y, screen.x, screen.y, - dropActionToTransferMode(e.getDropAction())); - try { - applyDropResult(lastTransferMode, e); - } catch (InvalidDnDOperationException ignore) { - // This means the JDK doesn't contain a fix for 8029979 yet. - // DnD still works, but a drag source won't know about - // the actual drop result reported by the FX app from - // its drop() handler. It will use the dropResult from - // the last call to dragOver() instead. - } - } finally { - e.dropComplete(lastTransferMode != null); - endDnD(); - lastTransferMode = null; - } - } - }; - comp.setDropTarget(new DropTarget(comp, - DnDConstants.ACTION_COPY | DnDConstants.ACTION_MOVE | DnDConstants.ACTION_LINK, dtl)); - - } - - void addNotify() { - dragSource.addDragSourceListener(dragSourceListener); - } - - void removeNotify() { - // RT-22049: Multi-JFrame/JFXPanel app leaks JFXPanels - // Don't forget to unregister drag source listener! - dragSource.removeDragSourceListener(dragSourceListener); - } - - HostDragStartListener getDragStartListener() { - return (dragSource, dragAction) -> { - assert Toolkit.getToolkit().isFxUserThread(); - assert dragSource != null; - - // The method is called from FX Scene just before entering - // nested event loop servicing DnD events. - // It should initialize DnD in AWT EDT. - SwingUtilities.invokeLater(() -> { - assert fxDragSource == null; - assert swingDragSource == null; - assert fxDropTarget == null; - - fxDragSource = dragSource; - startDrag(me, dndTransferable, dragSource. - getSupportedActions(), dragAction); - }); - }; - } - - private void startDrag(final MouseEvent e, final Transferable t, - final Set sa, - final TransferMode dragAction) - { - assert sa.contains(dragAction); - // This is a replacement for the default AWT drag gesture recognizer. - // Not sure DragGestureRecognizer was ever supposed to be used this way. - final class StubDragGestureRecognizer extends DragGestureRecognizer { - StubDragGestureRecognizer(DragSource ds) { - super(ds, e.getComponent()); - setSourceActions(transferModesToDropActions(sa)); - appendEvent(e); - } - @Override - protected void registerListeners() { - } - @Override - protected void unregisterListeners() { - } - } - - final Point pt = new Point(e.getX(), e.getY()); - final int action = transferModeToDropAction(dragAction); - final DragGestureRecognizer dgs = new StubDragGestureRecognizer(dragSource); - final List events = - Arrays.asList(new InputEvent[] { dgs.getTriggerEvent() }); - final DragGestureEvent dse = new DragGestureEvent(dgs, action, pt, events); - dse.startDrag(null, t); - } - - private void endDnD() { - assert swingDragSource != null; - assert fxDropTarget != null; - fxDropTarget = null; - swingDragSource = null; - } - - private void storeMouseEvent(final MouseEvent me) { - this.me = me; - } - - private void applyDragResult(final TransferMode dragResult, - final DropTargetDragEvent e) - { - if (dragResult == null) { - e.rejectDrag(); - } else { - e.acceptDrag(transferModeToDropAction(dragResult)); - } - } - - private void applyDropResult(final TransferMode dropResult, - final DropTargetDropEvent e) - { - if (dropResult == null) { - e.rejectDrop(); - } else { - e.acceptDrop(transferModeToDropAction(dropResult)); - } - } - - static TransferMode dropActionToTransferMode(final int dropAction) { - switch (dropAction) { - case DnDConstants.ACTION_COPY: - return TransferMode.COPY; - case DnDConstants.ACTION_MOVE: - return TransferMode.MOVE; - case DnDConstants.ACTION_LINK: - return TransferMode.LINK; - case DnDConstants.ACTION_NONE: - return null; - default: - throw new IllegalArgumentException(); - } - } - - static int transferModeToDropAction(final TransferMode tm) { - switch (tm) { - case COPY: - return DnDConstants.ACTION_COPY; - case MOVE: - return DnDConstants.ACTION_MOVE; - case LINK: - return DnDConstants.ACTION_LINK; - default: - throw new IllegalArgumentException(); - } - } - - static Set dropActionsToTransferModes( - final int dropActions) - { - final Set tms = EnumSet.noneOf(TransferMode.class); - if ((dropActions & DnDConstants.ACTION_COPY) != 0) { - tms.add(TransferMode.COPY); - } - if ((dropActions & DnDConstants.ACTION_MOVE) != 0) { - tms.add(TransferMode.MOVE); - } - if ((dropActions & DnDConstants.ACTION_LINK) != 0) { - tms.add(TransferMode.LINK); - } - return Collections.unmodifiableSet(tms); - } - - static int transferModesToDropActions(final Set tms) { - int dropActions = DnDConstants.ACTION_NONE; - for (TransferMode tm : tms) { - dropActions |= transferModeToDropAction(tm); - } - return dropActions; - } - - // Transferable wrapper over FX dragboard. All the calls are - // forwarded to FX and executed on the FX event thread. - private final class DnDTransferable implements Transferable { - - @Override - public Object getTransferData(final DataFlavor flavor) - throws UnsupportedEncodingException - { - assert fxDragSource != null; - assert SwingUtilities.isEventDispatchThread(); - - String mimeType = DataFlavorUtils.getFxMimeType(flavor); - return DataFlavorUtils.adjustFxData( - flavor, fxDragSource.getData(mimeType)); - } - - @Override - public DataFlavor[] getTransferDataFlavors() { - assert fxDragSource != null; - assert SwingUtilities.isEventDispatchThread(); - - final String mimeTypes[] = fxDragSource.getMimeTypes(); - - final ArrayList flavors = - new ArrayList(mimeTypes.length); - for (String mime : mimeTypes) { - DataFlavor flavor = null; - try { - flavor = new DataFlavor(mime); - } catch (ClassNotFoundException e) { - // FIXME: what to do? - continue; - } - flavors.add(flavor); - } - return flavors.toArray(new DataFlavor[0]); - } - - @Override - public boolean isDataFlavorSupported(final DataFlavor flavor) { - assert fxDragSource != null; - assert SwingUtilities.isEventDispatchThread(); - - return fxDragSource.isMimeTypeAvailable( - DataFlavorUtils.getFxMimeType(flavor)); - } - } -} --- /dev/null 2018-07-10 15:06:41.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingDnD.java 2018-07-10 15:06:40.043977300 +0530 @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import java.io.UnsupportedEncodingException; + +import java.util.Collections; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import com.sun.javafx.embed.EmbeddedSceneDSInterface; +import com.sun.javafx.embed.HostDragStartListener; +import javafx.scene.input.TransferMode; + +import com.sun.javafx.embed.EmbeddedSceneInterface; +import com.sun.javafx.embed.EmbeddedSceneDTInterface; +import com.sun.javafx.tk.Toolkit; + +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +import java.awt.Point; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.dnd.InvalidDnDOperationException; + +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/** + * An utility class to connect DnD mechanism of Swing and FX. + * It allows FX content to use the AWT machinery for performing DnD. + */ +final public class SwingDnD { + + private final Transferable dndTransferable = new DnDTransferable(); + + private final DragSource dragSource; + private final DragSourceListener dragSourceListener; + + // swingDragSource and fxDropTarget are used when DnD is initiated from + // Swing or external process, i.e. this SwingDnD is used as a drop target + private SwingDragSource swingDragSource; + private EmbeddedSceneDTInterface fxDropTarget; + + // fxDragSource is used when DnD is initiated from FX, i.e. this + // SwingDnD acts as a drag source + private EmbeddedSceneDSInterface fxDragSource; + + private MouseEvent me; + + public SwingDnD(final JComponent comp, final EmbeddedSceneInterface embeddedScene) { + + comp.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent me) { + storeMouseEvent(me); + } + @Override + public void mouseDragged(MouseEvent me) { + storeMouseEvent(me); + } + @Override + public void mousePressed(MouseEvent me) { + storeMouseEvent(me); + } + @Override + public void mouseReleased(MouseEvent me) { + storeMouseEvent(me); + } + }); + + dragSource = new DragSource(); + dragSourceListener = new DragSourceAdapter() { + @Override + public void dragDropEnd(final DragSourceDropEvent dsde) { + assert fxDragSource != null; + try { + fxDragSource.dragDropEnd(dropActionToTransferMode(dsde.getDropAction())); + } finally { + fxDragSource = null; + } + } + }; + + DropTargetListener dtl = new DropTargetAdapter() { + private TransferMode lastTransferMode; + + @Override + public void dragEnter(final DropTargetDragEvent e) { + // This is a temporary workaround for JDK-8027913 + if ((swingDragSource != null) || (fxDropTarget != null)) { + return; + } + + assert swingDragSource == null; + swingDragSource = new SwingDragSource(); + swingDragSource.updateContents(e, false); + + assert fxDropTarget == null; + // Cache the Transferable data in advance, as it cannot be + // queried from drop(). See comments in dragOver() and in + // drop() below + fxDropTarget = embeddedScene.createDropTarget(); + + final Point orig = e.getLocation(); + final Point screen = new Point(orig); + SwingUtilities.convertPointToScreen(screen, comp); + lastTransferMode = fxDropTarget.handleDragEnter( + orig.x, orig.y, screen.x, screen.y, + dropActionToTransferMode(e.getDropAction()), swingDragSource); + applyDragResult(lastTransferMode, e); + } + + @Override + public void dragExit(final DropTargetEvent e) { + assert swingDragSource != null; + assert fxDropTarget != null; + try { + fxDropTarget.handleDragLeave(); + } finally { + endDnD(); + lastTransferMode = null; + } + } + + @Override + public void dragOver(final DropTargetDragEvent e) { + assert swingDragSource != null; + swingDragSource.updateContents(e, false); + + assert fxDropTarget != null; + final Point orig = e.getLocation(); + final Point screen = new Point(orig); + SwingUtilities.convertPointToScreen(screen, comp); + lastTransferMode = fxDropTarget.handleDragOver( + orig.x, orig.y, screen.x, screen.y, + dropActionToTransferMode(e.getDropAction())); + applyDragResult(lastTransferMode, e); + } + + @Override + public void drop(final DropTargetDropEvent e) { + assert swingDragSource != null; + + // This allows the subsequent call to updateContents() to + // actually fetch the data from a drag source. The actual + // and final drop result may be redefined later. + applyDropResult(lastTransferMode, e); + swingDragSource.updateContents(e, true); + + final Point orig = e.getLocation(); + final Point screen = new Point(orig); + SwingUtilities.convertPointToScreen(screen, comp); + + assert fxDropTarget != null; + try { + lastTransferMode = fxDropTarget.handleDragDrop( + orig.x, orig.y, screen.x, screen.y, + dropActionToTransferMode(e.getDropAction())); + try { + applyDropResult(lastTransferMode, e); + } catch (InvalidDnDOperationException ignore) { + // This means the JDK doesn't contain a fix for 8029979 yet. + // DnD still works, but a drag source won't know about + // the actual drop result reported by the FX app from + // its drop() handler. It will use the dropResult from + // the last call to dragOver() instead. + } + } finally { + e.dropComplete(lastTransferMode != null); + endDnD(); + lastTransferMode = null; + } + } + }; + comp.setDropTarget(new DropTarget(comp, + DnDConstants.ACTION_COPY | DnDConstants.ACTION_MOVE | DnDConstants.ACTION_LINK, dtl)); + + } + + public void addNotify() { + dragSource.addDragSourceListener(dragSourceListener); + } + + public void removeNotify() { + // RT-22049: Multi-JFrame/JFXPanel app leaks JFXPanels + // Don't forget to unregister drag source listener! + dragSource.removeDragSourceListener(dragSourceListener); + } + + public HostDragStartListener getDragStartListener() { + return (dragSource, dragAction) -> { + assert Toolkit.getToolkit().isFxUserThread(); + assert dragSource != null; + + // The method is called from FX Scene just before entering + // nested event loop servicing DnD events. + // It should initialize DnD in AWT EDT. + SwingUtilities.invokeLater(() -> { + assert fxDragSource == null; + assert swingDragSource == null; + assert fxDropTarget == null; + + fxDragSource = dragSource; + startDrag(me, dndTransferable, dragSource. + getSupportedActions(), dragAction); + }); + }; + } + + private void startDrag(final MouseEvent e, final Transferable t, + final Set sa, + final TransferMode dragAction) + { + assert sa.contains(dragAction); + // This is a replacement for the default AWT drag gesture recognizer. + // Not sure DragGestureRecognizer was ever supposed to be used this way. + final class StubDragGestureRecognizer extends DragGestureRecognizer { + StubDragGestureRecognizer(DragSource ds) { + super(ds, e.getComponent()); + setSourceActions(transferModesToDropActions(sa)); + appendEvent(e); + } + @Override + protected void registerListeners() { + } + @Override + protected void unregisterListeners() { + } + } + + final Point pt = new Point(e.getX(), e.getY()); + final int action = transferModeToDropAction(dragAction); + final DragGestureRecognizer dgs = new StubDragGestureRecognizer(dragSource); + final List events = + Arrays.asList(new InputEvent[] { dgs.getTriggerEvent() }); + final DragGestureEvent dse = new DragGestureEvent(dgs, action, pt, events); + dse.startDrag(null, t); + } + + private void endDnD() { + assert swingDragSource != null; + assert fxDropTarget != null; + fxDropTarget = null; + swingDragSource = null; + } + + private void storeMouseEvent(final MouseEvent me) { + this.me = me; + } + + private void applyDragResult(final TransferMode dragResult, + final DropTargetDragEvent e) + { + if (dragResult == null) { + e.rejectDrag(); + } else { + e.acceptDrag(transferModeToDropAction(dragResult)); + } + } + + private void applyDropResult(final TransferMode dropResult, + final DropTargetDropEvent e) + { + if (dropResult == null) { + e.rejectDrop(); + } else { + e.acceptDrop(transferModeToDropAction(dropResult)); + } + } + + public static TransferMode dropActionToTransferMode(final int dropAction) { + switch (dropAction) { + case DnDConstants.ACTION_COPY: + return TransferMode.COPY; + case DnDConstants.ACTION_MOVE: + return TransferMode.MOVE; + case DnDConstants.ACTION_LINK: + return TransferMode.LINK; + case DnDConstants.ACTION_NONE: + return null; + default: + throw new IllegalArgumentException(); + } + } + + public static int transferModeToDropAction(final TransferMode tm) { + switch (tm) { + case COPY: + return DnDConstants.ACTION_COPY; + case MOVE: + return DnDConstants.ACTION_MOVE; + case LINK: + return DnDConstants.ACTION_LINK; + default: + throw new IllegalArgumentException(); + } + } + + public static Set dropActionsToTransferModes( + final int dropActions) + { + final Set tms = EnumSet.noneOf(TransferMode.class); + if ((dropActions & DnDConstants.ACTION_COPY) != 0) { + tms.add(TransferMode.COPY); + } + if ((dropActions & DnDConstants.ACTION_MOVE) != 0) { + tms.add(TransferMode.MOVE); + } + if ((dropActions & DnDConstants.ACTION_LINK) != 0) { + tms.add(TransferMode.LINK); + } + return Collections.unmodifiableSet(tms); + } + + public static int transferModesToDropActions(final Set tms) { + int dropActions = DnDConstants.ACTION_NONE; + for (TransferMode tm : tms) { + dropActions |= transferModeToDropAction(tm); + } + return dropActions; + } + + // Transferable wrapper over FX dragboard. All the calls are + // forwarded to FX and executed on the FX event thread. + private final class DnDTransferable implements Transferable { + + @Override + public Object getTransferData(final DataFlavor flavor) + throws UnsupportedEncodingException + { + assert fxDragSource != null; + assert SwingUtilities.isEventDispatchThread(); + + String mimeType = DataFlavorUtils.getFxMimeType(flavor); + return DataFlavorUtils.adjustFxData( + flavor, fxDragSource.getData(mimeType)); + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + assert fxDragSource != null; + assert SwingUtilities.isEventDispatchThread(); + + final String mimeTypes[] = fxDragSource.getMimeTypes(); + + final ArrayList flavors = + new ArrayList(mimeTypes.length); + for (String mime : mimeTypes) { + DataFlavor flavor = null; + try { + flavor = new DataFlavor(mime); + } catch (ClassNotFoundException e) { + // FIXME: what to do? + continue; + } + flavors.add(flavor); + } + return flavors.toArray(new DataFlavor[0]); + } + + @Override + public boolean isDataFlavorSupported(final DataFlavor flavor) { + assert fxDragSource != null; + assert SwingUtilities.isEventDispatchThread(); + + return fxDragSource.isMimeTypeAvailable( + DataFlavorUtils.getFxMimeType(flavor)); + } + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingDragSource.java 2018-07-10 15:06:47.091372200 +0530 +++ /dev/null 2018-07-10 15:06:47.000000000 +0530 @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import com.sun.javafx.embed.EmbeddedSceneDSInterface; -import com.sun.javafx.tk.Toolkit; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import javafx.scene.input.TransferMode; - -/** - * Drag source to deliver data from Swing environment to embedded FX scene. - */ -final class SwingDragSource extends CachingTransferable implements EmbeddedSceneDSInterface { - - private int sourceActions; - - SwingDragSource() { - } - - void updateContents(final DropTargetDragEvent e, boolean fetchData) { - sourceActions = e.getSourceActions(); - updateData(e.getTransferable(), fetchData); - } - - void updateContents(final DropTargetDropEvent e, boolean fetchData) { - sourceActions = e.getSourceActions(); - updateData(e.getTransferable(), fetchData); - } - - @Override - public Set getSupportedActions() { - assert Toolkit.getToolkit().isFxUserThread(); - return SwingDnD.dropActionsToTransferModes(sourceActions); - } - - @Override - public void dragDropEnd(TransferMode performedAction) { - throw new UnsupportedOperationException(); - } -} --- /dev/null 2018-07-10 15:06:47.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingDragSource.java 2018-07-10 15:06:45.422160200 +0530 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import com.sun.javafx.embed.EmbeddedSceneDSInterface; +import com.sun.javafx.tk.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import javafx.scene.input.TransferMode; + +/** + * Drag source to deliver data from Swing environment to embedded FX scene. + */ +final class SwingDragSource extends CachingTransferable implements EmbeddedSceneDSInterface { + + private int sourceActions; + + SwingDragSource() { + } + + void updateContents(final DropTargetDragEvent e, boolean fetchData) { + sourceActions = e.getSourceActions(); + updateData(e.getTransferable(), fetchData); + } + + void updateContents(final DropTargetDropEvent e, boolean fetchData) { + sourceActions = e.getSourceActions(); + updateData(e.getTransferable(), fetchData); + } + + @Override + public Set getSupportedActions() { + assert Toolkit.getToolkit().isFxUserThread(); + return SwingDnD.dropActionsToTransferModes(sourceActions); + } + + @Override + public void dragDropEnd(TransferMode performedAction) { + throw new UnsupportedOperationException(); + } +} --- old/modules/javafx.swing/src/main/java/javafx/embed/swing/SwingEvents.java 2018-07-10 15:06:51.935987400 +0530 +++ /dev/null 2018-07-10 15:06:52.000000000 +0530 @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package javafx.embed.swing; - -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; - -import com.sun.javafx.embed.AbstractEvents; -import javafx.event.EventType; -import javafx.scene.input.ScrollEvent; - -/** - * A utility class to translate event types and data between embedded - * application and Swing. - */ -class SwingEvents { - - static int mouseIDToEmbedMouseType(int id) { - switch (id) { - case MouseEvent.MOUSE_PRESSED: - return AbstractEvents.MOUSEEVENT_PRESSED; - case MouseEvent.MOUSE_RELEASED: - return AbstractEvents.MOUSEEVENT_RELEASED; - case MouseEvent.MOUSE_CLICKED: - return AbstractEvents.MOUSEEVENT_CLICKED; - case MouseEvent.MOUSE_MOVED: - return AbstractEvents.MOUSEEVENT_MOVED; - case MouseEvent.MOUSE_DRAGGED: - return AbstractEvents.MOUSEEVENT_DRAGGED; - case MouseEvent.MOUSE_ENTERED: - return AbstractEvents.MOUSEEVENT_ENTERED; - case MouseEvent.MOUSE_EXITED: - return AbstractEvents.MOUSEEVENT_EXITED; - } - return 0; - } - - static int mouseButtonToEmbedMouseButton(int button, int extModifiers) { - int abstractButton = AbstractEvents.MOUSEEVENT_NONE_BUTTON; - switch (button) { - case MouseEvent.BUTTON1: - abstractButton = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; - break; - case MouseEvent.BUTTON2: - abstractButton = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; - break; - case MouseEvent.BUTTON3: - abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; - break; - default: - break; - } - // Fix for RT-15457: we should report mouse buttons for mouse drags - if ((extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) { - abstractButton = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; - } else if ((extModifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) { - abstractButton = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; - } else if ((extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { - abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; - } - return abstractButton; - } - - static int getWheelRotation(MouseEvent e) { - if (e instanceof MouseWheelEvent) { - return ((MouseWheelEvent)e).getWheelRotation(); - } - return 0; - } - - static int keyIDToEmbedKeyType(int id) { - switch (id) { - case KeyEvent.KEY_PRESSED: - return AbstractEvents.KEYEVENT_PRESSED; - case KeyEvent.KEY_RELEASED: - return AbstractEvents.KEYEVENT_RELEASED; - case KeyEvent.KEY_TYPED: - return AbstractEvents.KEYEVENT_TYPED; - } - return 0; - } - - static int keyModifiersToEmbedKeyModifiers(int extModifiers) { - int embedModifiers = 0; - if ((extModifiers & InputEvent.SHIFT_DOWN_MASK) != 0) { - embedModifiers |= AbstractEvents.MODIFIER_SHIFT; - } - if ((extModifiers & InputEvent.CTRL_DOWN_MASK) != 0) { - embedModifiers |= AbstractEvents.MODIFIER_CONTROL; - } - if ((extModifiers & InputEvent.ALT_DOWN_MASK) != 0) { - embedModifiers |= AbstractEvents.MODIFIER_ALT; - } - if ((extModifiers & InputEvent.META_DOWN_MASK) != 0) { - embedModifiers |= AbstractEvents.MODIFIER_META; - } - return embedModifiers; - } - - static char keyCharToEmbedKeyChar(char ch) { - // Convert Swing LF character to Fx CR character. - return ch == '\n' ? '\r' : ch; - } - - // FX -> Swing conversion methods - - static int fxMouseEventTypeToMouseID(javafx.scene.input.MouseEvent event) { - EventType type = event.getEventType(); - if (type == javafx.scene.input.MouseEvent.MOUSE_MOVED) { - return MouseEvent.MOUSE_MOVED; - } - if (type == javafx.scene.input.MouseEvent.MOUSE_PRESSED) { - return MouseEvent.MOUSE_PRESSED; - } - if (type == javafx.scene.input.MouseEvent.MOUSE_RELEASED) { - return MouseEvent.MOUSE_RELEASED; - } - if (type == javafx.scene.input.MouseEvent.MOUSE_CLICKED) { - return MouseEvent.MOUSE_CLICKED; - } - if (type == javafx.scene.input.MouseEvent.MOUSE_ENTERED) { - return MouseEvent.MOUSE_ENTERED; - } - if (type == javafx.scene.input.MouseEvent.MOUSE_EXITED) { - return MouseEvent.MOUSE_EXITED; - } - if (type == javafx.scene.input.MouseEvent.MOUSE_DRAGGED) { - return MouseEvent.MOUSE_DRAGGED; - } - if (type == javafx.scene.input.MouseEvent.DRAG_DETECTED) { - return -1; - } - throw new RuntimeException("Unknown MouseEvent type: " + type); - } - - static int fxMouseModsToMouseMods(javafx.scene.input.MouseEvent event) { - int mods = 0; - if (event.isAltDown()) { - mods |= InputEvent.ALT_DOWN_MASK; - } - if (event.isControlDown()) { - mods |= InputEvent.CTRL_DOWN_MASK; - } - if (event.isMetaDown()) { - mods |= InputEvent.META_DOWN_MASK; - } - if (event.isShiftDown()) { - mods |= InputEvent.SHIFT_DOWN_MASK; - } - if (event.isPrimaryButtonDown()) { - mods |= MouseEvent.BUTTON1_DOWN_MASK; - } - if (event.isSecondaryButtonDown()) { - mods |= MouseEvent.BUTTON3_DOWN_MASK; - } - if (event.isMiddleButtonDown()) { - mods |= MouseEvent.BUTTON2_DOWN_MASK; - } - return mods; - } - - static int fxMouseButtonToMouseButton(javafx.scene.input.MouseEvent event) { - switch (event.getButton()) { - case PRIMARY: - return MouseEvent.BUTTON1; - case SECONDARY: - return MouseEvent.BUTTON3; - case MIDDLE: - return MouseEvent.BUTTON2; - } - return 0; - } - - static int fxKeyEventTypeToKeyID(javafx.scene.input.KeyEvent event) { - EventType eventType = event.getEventType(); - if (eventType == javafx.scene.input.KeyEvent.KEY_PRESSED) { - return KeyEvent.KEY_PRESSED; - } - if (eventType == javafx.scene.input.KeyEvent.KEY_RELEASED) { - return KeyEvent.KEY_RELEASED; - } - if (eventType == javafx.scene.input.KeyEvent.KEY_TYPED) { - return KeyEvent.KEY_TYPED; - } - throw new RuntimeException("Unknown KeyEvent type: " + eventType); - } - - static int fxKeyModsToKeyMods(javafx.scene.input.KeyEvent event) { - int mods = 0; - if (event.isAltDown()) { - mods |= InputEvent.ALT_DOWN_MASK; - } - if (event.isControlDown()) { - mods |= InputEvent.CTRL_DOWN_MASK; - } - if (event.isMetaDown()) { - mods |= InputEvent.META_DOWN_MASK; - } - if (event.isShiftDown()) { - mods |= InputEvent.SHIFT_DOWN_MASK; - } - return mods; - } - - static int fxScrollModsToMouseWheelMods(ScrollEvent event) { - int mods = 0; - if (event.isAltDown()) { - mods |= InputEvent.ALT_DOWN_MASK; - } - if (event.isControlDown()) { - mods |= InputEvent.CTRL_DOWN_MASK; - } - if (event.isMetaDown()) { - mods |= InputEvent.META_DOWN_MASK; - } - if (event.isShiftDown()) { - mods |= InputEvent.SHIFT_DOWN_MASK; - } - return mods; - } -} --- /dev/null 2018-07-10 15:06:52.000000000 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java 2018-07-10 15:06:50.550311400 +0530 @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.embed.swing; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; + +import com.sun.javafx.embed.AbstractEvents; +import javafx.event.EventType; +import javafx.scene.input.ScrollEvent; + +/** + * A utility class to translate event types and data between embedded + * application and Swing. + */ +public class SwingEvents { + + public static int mouseIDToEmbedMouseType(int id) { + switch (id) { + case MouseEvent.MOUSE_PRESSED: + return AbstractEvents.MOUSEEVENT_PRESSED; + case MouseEvent.MOUSE_RELEASED: + return AbstractEvents.MOUSEEVENT_RELEASED; + case MouseEvent.MOUSE_CLICKED: + return AbstractEvents.MOUSEEVENT_CLICKED; + case MouseEvent.MOUSE_MOVED: + return AbstractEvents.MOUSEEVENT_MOVED; + case MouseEvent.MOUSE_DRAGGED: + return AbstractEvents.MOUSEEVENT_DRAGGED; + case MouseEvent.MOUSE_ENTERED: + return AbstractEvents.MOUSEEVENT_ENTERED; + case MouseEvent.MOUSE_EXITED: + return AbstractEvents.MOUSEEVENT_EXITED; + } + return 0; + } + + public static int mouseButtonToEmbedMouseButton(int button, int extModifiers) { + int abstractButton = AbstractEvents.MOUSEEVENT_NONE_BUTTON; + switch (button) { + case MouseEvent.BUTTON1: + abstractButton = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; + break; + case MouseEvent.BUTTON2: + abstractButton = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; + break; + case MouseEvent.BUTTON3: + abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; + break; + default: + break; + } + // Fix for RT-15457: we should report mouse buttons for mouse drags + if ((extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) { + abstractButton = AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON; + } else if ((extModifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) { + abstractButton = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON; + } else if ((extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) { + abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON; + } + return abstractButton; + } + + public static int getWheelRotation(MouseEvent e) { + if (e instanceof MouseWheelEvent) { + return ((MouseWheelEvent)e).getWheelRotation(); + } + return 0; + } + + public static int keyIDToEmbedKeyType(int id) { + switch (id) { + case KeyEvent.KEY_PRESSED: + return AbstractEvents.KEYEVENT_PRESSED; + case KeyEvent.KEY_RELEASED: + return AbstractEvents.KEYEVENT_RELEASED; + case KeyEvent.KEY_TYPED: + return AbstractEvents.KEYEVENT_TYPED; + } + return 0; + } + + public static int keyModifiersToEmbedKeyModifiers(int extModifiers) { + int embedModifiers = 0; + if ((extModifiers & InputEvent.SHIFT_DOWN_MASK) != 0) { + embedModifiers |= AbstractEvents.MODIFIER_SHIFT; + } + if ((extModifiers & InputEvent.CTRL_DOWN_MASK) != 0) { + embedModifiers |= AbstractEvents.MODIFIER_CONTROL; + } + if ((extModifiers & InputEvent.ALT_DOWN_MASK) != 0) { + embedModifiers |= AbstractEvents.MODIFIER_ALT; + } + if ((extModifiers & InputEvent.META_DOWN_MASK) != 0) { + embedModifiers |= AbstractEvents.MODIFIER_META; + } + return embedModifiers; + } + + public static char keyCharToEmbedKeyChar(char ch) { + // Convert Swing LF character to Fx CR character. + return ch == '\n' ? '\r' : ch; + } + + // FX -> Swing conversion methods + + public static int fxMouseEventTypeToMouseID(javafx.scene.input.MouseEvent event) { + EventType type = event.getEventType(); + if (type == javafx.scene.input.MouseEvent.MOUSE_MOVED) { + return MouseEvent.MOUSE_MOVED; + } + if (type == javafx.scene.input.MouseEvent.MOUSE_PRESSED) { + return MouseEvent.MOUSE_PRESSED; + } + if (type == javafx.scene.input.MouseEvent.MOUSE_RELEASED) { + return MouseEvent.MOUSE_RELEASED; + } + if (type == javafx.scene.input.MouseEvent.MOUSE_CLICKED) { + return MouseEvent.MOUSE_CLICKED; + } + if (type == javafx.scene.input.MouseEvent.MOUSE_ENTERED) { + return MouseEvent.MOUSE_ENTERED; + } + if (type == javafx.scene.input.MouseEvent.MOUSE_EXITED) { + return MouseEvent.MOUSE_EXITED; + } + if (type == javafx.scene.input.MouseEvent.MOUSE_DRAGGED) { + return MouseEvent.MOUSE_DRAGGED; + } + if (type == javafx.scene.input.MouseEvent.DRAG_DETECTED) { + return -1; + } + throw new RuntimeException("Unknown MouseEvent type: " + type); + } + + public static int fxMouseModsToMouseMods(javafx.scene.input.MouseEvent event) { + int mods = 0; + if (event.isAltDown()) { + mods |= InputEvent.ALT_DOWN_MASK; + } + if (event.isControlDown()) { + mods |= InputEvent.CTRL_DOWN_MASK; + } + if (event.isMetaDown()) { + mods |= InputEvent.META_DOWN_MASK; + } + if (event.isShiftDown()) { + mods |= InputEvent.SHIFT_DOWN_MASK; + } + if (event.isPrimaryButtonDown()) { + mods |= MouseEvent.BUTTON1_DOWN_MASK; + } + if (event.isSecondaryButtonDown()) { + mods |= MouseEvent.BUTTON3_DOWN_MASK; + } + if (event.isMiddleButtonDown()) { + mods |= MouseEvent.BUTTON2_DOWN_MASK; + } + return mods; + } + + public static int fxMouseButtonToMouseButton(javafx.scene.input.MouseEvent event) { + switch (event.getButton()) { + case PRIMARY: + return MouseEvent.BUTTON1; + case SECONDARY: + return MouseEvent.BUTTON3; + case MIDDLE: + return MouseEvent.BUTTON2; + } + return 0; + } + + public static int fxKeyEventTypeToKeyID(javafx.scene.input.KeyEvent event) { + EventType eventType = event.getEventType(); + if (eventType == javafx.scene.input.KeyEvent.KEY_PRESSED) { + return KeyEvent.KEY_PRESSED; + } + if (eventType == javafx.scene.input.KeyEvent.KEY_RELEASED) { + return KeyEvent.KEY_RELEASED; + } + if (eventType == javafx.scene.input.KeyEvent.KEY_TYPED) { + return KeyEvent.KEY_TYPED; + } + throw new RuntimeException("Unknown KeyEvent type: " + eventType); + } + + public static int fxKeyModsToKeyMods(javafx.scene.input.KeyEvent event) { + int mods = 0; + if (event.isAltDown()) { + mods |= InputEvent.ALT_DOWN_MASK; + } + if (event.isControlDown()) { + mods |= InputEvent.CTRL_DOWN_MASK; + } + if (event.isMetaDown()) { + mods |= InputEvent.META_DOWN_MASK; + } + if (event.isShiftDown()) { + mods |= InputEvent.SHIFT_DOWN_MASK; + } + return mods; + } + + public static int fxScrollModsToMouseWheelMods(ScrollEvent event) { + int mods = 0; + if (event.isAltDown()) { + mods |= InputEvent.ALT_DOWN_MASK; + } + if (event.isControlDown()) { + mods |= InputEvent.CTRL_DOWN_MASK; + } + if (event.isMetaDown()) { + mods |= InputEvent.META_DOWN_MASK; + } + if (event.isShiftDown()) { + mods |= InputEvent.SHIFT_DOWN_MASK; + } + return mods; + } +} --- old/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingNodeHelper.java 2018-07-10 15:06:57.206156600 +0530 +++ new/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingNodeHelper.java 2018-07-10 15:06:56.311042900 +0530 @@ -25,6 +25,8 @@ package com.sun.javafx.embed.swing; +import java.util.concurrent.locks.ReentrantLock; + import com.sun.javafx.geom.BaseBounds; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.javafx.scene.NodeHelper; @@ -33,6 +35,12 @@ import javafx.embed.swing.SwingNode; import javafx.scene.Node; +import java.util.Set; +import java.util.HashSet; +import javafx.application.Platform; +import javax.swing.SwingUtilities; +import com.sun.javafx.tk.Toolkit; + /** * Used to access internal methods of SwingNode. */ @@ -76,6 +84,138 @@ return swingNodeAccessor.doComputeContains(node, localX, localY); } + public static Object getLightweightFrame(SwingNode node) { + return swingNodeAccessor.getLightweightFrame(node); + } + + public static ReentrantLock getPaintLock(SwingNode node) { + return swingNodeAccessor.getPaintLock(node); + } + + public static void setImageBuffer(SwingNode node, final int[] data, + final int x, final int y, + final int w, final int h, final int linestride, + final double scaleX, final double scaleY) { + swingNodeAccessor.setImageBuffer(node, data, x, y, w, h, + linestride, scaleX, scaleY); + } + + public static void setImageBounds(SwingNode node, final int x, final int y, + final int w, final int h) { + swingNodeAccessor.setImageBounds(node, x, y, w, h); + } + + public static void repaintDirtyRegion(SwingNode node, final int dirtyX, final int dirtyY, + final int dirtyWidth, final int dirtyHeight) { + swingNodeAccessor.repaintDirtyRegion(node, dirtyX, dirtyY, + dirtyWidth, dirtyHeight); + } + + public static void ungrabFocus(SwingNode node, boolean postUngrabEvent) { + swingNodeAccessor.ungrabFocus(node, postUngrabEvent); + } + + public static void setSwingPrefWidth(SwingNode node, int swingPrefWidth) { + swingNodeAccessor.setSwingPrefWidth(node, swingPrefWidth);; + } + + public static void setSwingPrefHeight(SwingNode node, int swingPrefHeight) { + swingNodeAccessor.setSwingPrefHeight(node, swingPrefHeight); + } + + public static void setSwingMaxWidth(SwingNode node, int swingMaxWidth) { + swingNodeAccessor.setSwingMaxWidth(node, swingMaxWidth); + } + + public static void setSwingMaxHeight(SwingNode node, int swingMaxHeight) { + swingNodeAccessor.setSwingMaxHeight(node, swingMaxHeight); + } + + public static void setSwingMinWidth(SwingNode node, int swingMinWidth) { + swingNodeAccessor.setSwingMinWidth(node, swingMinWidth); + } + + public static void setSwingMinHeight(SwingNode node, int swingMinHeight) { + swingNodeAccessor.setSwingMinHeight(node, swingMinHeight); + } + + public static void setGrabbed(SwingNode node, boolean grab) { + swingNodeAccessor.setGrabbed(node, grab); + } + + /** + * If called from the FX Application Thread + * invokes a runnable directly blocking the calling code + * Otherwise + * uses Platform.runLater without blocking + * + * @param runnable {@code Runnable} to be invoked + */ + public static void runOnFxThread(Runnable runnable) { + if (Platform.isFxApplicationThread()) { + runnable.run(); + } else { + Platform.runLater(runnable); + } + } + + /** + * If called from the event dispatch thread + * invokes a runnable directly blocking the calling code + * Otherwise + * uses SwingUtilities.invokeLater without blocking + * + * @param r {@code Runnable} to be invoked + */ + public static void runOnEDT(final Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + SwingUtilities.invokeLater(r); + } + } + + private static final Set eventLoopKeys = new HashSet<>(); + + /** + * The runnable is responsible for entering the nested event loop. + * + * @param nestedLoopKey the Object that identifies the nested event loop, + * which must not be null + * @param r {@code Runnable} to be invoked + */ + public static void runOnEDTAndWait(Object nestedLoopKey, Runnable r) { + Toolkit.getToolkit().checkFxUserThread(); + + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + eventLoopKeys.add(nestedLoopKey); + SwingUtilities.invokeLater(r); + Toolkit.getToolkit().enterNestedEventLoop(nestedLoopKey); + } + } + + /** + * The runnable is responsible for leaving the nested event loop. + * + * @param nestedLoopKey the Object that identifies the nested event loop, + * which must not be null + */ + public static void leaveFXNestedLoop(Object nestedLoopKey) { + if (!eventLoopKeys.contains(nestedLoopKey)) return; + + if (Platform.isFxApplicationThread()) { + Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null); + } else { + Platform.runLater(() -> { + Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null); + }); + } + + eventLoopKeys.remove(nestedLoopKey); + } + public static void setSwingNodeAccessor(final SwingNodeAccessor newAccessor) { if (swingNodeAccessor != null) { throw new IllegalStateException(); @@ -89,6 +229,24 @@ void doUpdatePeer(Node node); BaseBounds doComputeGeomBounds(Node node, BaseBounds bounds, BaseTransform tx); boolean doComputeContains(Node node, double localX, double localY); + Object getLightweightFrame(SwingNode node); + ReentrantLock getPaintLock(SwingNode node); + void setImageBuffer(SwingNode node, final int[] data, + final int x, final int y, + final int w, final int h, final int linestride, + final double scaleX, final double scaleY); + void setImageBounds(SwingNode node, final int x, final int y, + final int w, final int h); + void repaintDirtyRegion(SwingNode node, final int dirtyX, final int dirtyY, + final int dirtyWidth, final int dirtyHeight); + void ungrabFocus(SwingNode node, boolean postUngrabEvent); + void setSwingPrefWidth(SwingNode node, int swingPrefWidth); + void setSwingPrefHeight(SwingNode node, int swingPrefHeight); + void setSwingMaxWidth(SwingNode node, int swingMaxWidth); + void setSwingMaxHeight(SwingNode node, int swingMaxHeight); + void setSwingMinWidth(SwingNode node, int swingMinWidth); + void setSwingMinHeight(SwingNode node, int swingMinHeight); + void setGrabbed(SwingNode node, boolean grab); } }