--- old/javafx-embed-swing/build-closed.xml 2013-03-12 19:40:14.000000000 +0400
+++ new/javafx-embed-swing/build-closed.xml 2013-03-12 19:40:14.000000000 +0400
@@ -6,7 +6,10 @@
--- old/javafx-embed-swing/project.properties 2013-03-12 19:40:15.000000000 +0400
+++ new/javafx-embed-swing/project.properties 2013-03-12 19:40:15.000000000 +0400
@@ -3,6 +3,9 @@
javac.classpath=\
${rt.dist.root.dir}/javafx-common/dist/javafx-common.jar:\
${rt.dist.root.dir}/javafx-ui-common/dist/javafx-ui-common.jar:\
+ ${rt.dist.root.dir}/javafx-geom/dist/javafx-geom.jar:\
+ ${rt.dist.root.dir}/javafx-sg-common/dist/javafx-sg-common.jar:\
+ ${rt.dist.root.dir}/javafx-beans/dist/javafx-beans.jar:\
${JFXRT_HOME}/lib/ext/jfxrt.jar
javac.test.classpath=\
${javac.classpath}:\
--- old/javafx-embed-swing/src/javafx/embed/swing/SwingEvents.java 2013-03-12 19:40:15.000000000 +0400
+++ new/javafx-embed-swing/src/javafx/embed/swing/SwingEvents.java 2013-03-12 19:40:15.000000000 +0400
@@ -31,6 +31,7 @@
import java.awt.event.MouseWheelEvent;
import com.sun.javafx.embed.AbstractEvents;
+import javafx.event.EventType;
/**
* An utility class to translate cursor types between embedded
@@ -121,4 +122,105 @@
}
return embedModifiers;
}
+
+ // 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.BUTTON2_DOWN_MASK;
+ }
+ if (event.isMiddleButtonDown()) {
+ mods |= MouseEvent.BUTTON3_DOWN_MASK;
+ }
+ return mods;
+ }
+
+ static int fxMouseButtonToMouseButton(javafx.scene.input.MouseEvent event) {
+ switch (event.getButton()) {
+ case PRIMARY:
+ return MouseEvent.BUTTON1;
+ case SECONDARY:
+ return MouseEvent.BUTTON2;
+ case MIDDLE:
+ return MouseEvent.BUTTON3;
+ }
+ 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;
+ }
+
}
--- old/javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java 2013-03-12 19:40:16.000000000 +0400
+++ new/javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java 2013-03-12 19:40:16.000000000 +0400
@@ -63,6 +63,7 @@
import com.sun.javafx.sg.PGCubicCurve;
import com.sun.javafx.sg.PGCylinder;
import com.sun.javafx.sg.PGEllipse;
+import com.sun.javafx.sg.PGExternalNode;
import com.sun.javafx.sg.PGGroup;
import com.sun.javafx.sg.PGImageView;
import com.sun.javafx.sg.PGLightBase;
@@ -379,6 +380,11 @@
}
@Override
+ public PGExternalNode createPGExternalNode() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
public Object createSVGPathObject(SVGPath svgpath) {
throw new UnsupportedOperationException("Not supported yet.");
}
--- old/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java 2013-03-12 19:40:17.000000000 +0400
+++ new/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java 2013-03-12 19:40:17.000000000 +0400
@@ -81,6 +81,7 @@
import com.sun.javafx.sg.PGCircle;
import com.sun.javafx.sg.PGCubicCurve;
import com.sun.javafx.sg.PGEllipse;
+import com.sun.javafx.sg.PGExternalNode;
import com.sun.javafx.sg.PGGroup;
import com.sun.javafx.sg.PGImageView;
import com.sun.javafx.sg.PGLine;
@@ -657,6 +658,8 @@
public abstract boolean isLightsDirty();
public abstract void setLightsDirty(boolean lightsDirty);
+ public abstract PGExternalNode createPGExternalNode();
+
/**
* Tests whether the pixel on the given coordinates in the given image
* is non-empty (not fully transparent). Return value is not defined
--- old/javafx-ui-quantum/src/com/sun/javafx/tk/quantum/QuantumToolkit.java 2013-03-12 19:40:18.000000000 +0400
+++ new/javafx-ui-quantum/src/com/sun/javafx/tk/quantum/QuantumToolkit.java 2013-03-12 19:40:17.000000000 +0400
@@ -93,6 +93,7 @@
import com.sun.javafx.sg.PGCircle;
import com.sun.javafx.sg.PGCubicCurve;
import com.sun.javafx.sg.PGEllipse;
+import com.sun.javafx.sg.PGExternalNode;
import com.sun.javafx.sg.PGGroup;
import com.sun.javafx.sg.PGImageView;
import com.sun.javafx.sg.PGLine;
@@ -187,6 +188,7 @@
import com.sun.javafx.sg.prism.NGPointLight;
import com.sun.javafx.sg.prism.NGSphere;
import com.sun.javafx.sg.prism.NGTriangleMesh;
+import com.sun.javafx.sg.prism.NGExternalNode;
public final class QuantumToolkit extends DesktopToolkit implements ToolkitInterface {
@@ -1130,6 +1132,10 @@
return new NGText();
}
+ @Override public PGExternalNode createPGExternalNode() {
+ return new NGExternalNode();
+ }
+
@Override public Object createSVGPathObject(SVGPath svgpath) {
int windingRule = svgpath.getFillRule() == FillRule.NON_ZERO ? PathIterator.WIND_NON_ZERO : PathIterator.WIND_EVEN_ODD;
Path2D path = new Path2D(windingRule);
--- old/test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java 2013-03-12 19:40:18.000000000 +0400
+++ new/test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java 2013-03-12 19:40:18.000000000 +0400
@@ -81,6 +81,7 @@
import com.sun.javafx.sg.PGCubicCurve;
import com.sun.javafx.sg.PGCylinder;
import com.sun.javafx.sg.PGEllipse;
+import com.sun.javafx.sg.PGExternalNode;
import com.sun.javafx.sg.PGGroup;
import com.sun.javafx.sg.PGImageView;
import com.sun.javafx.sg.PGLightBase;
@@ -438,6 +439,10 @@
return new StubText();
}
+ @Override public PGExternalNode createPGExternalNode() {
+ return new StubExternalNode();
+ }
+
/*
* additional testing functions
*/
--- /dev/null 2013-03-12 19:40:19.000000000 +0400
+++ new/javafx-embed-swing/src/javafx/embed/swing/SwingNode.java 2013-03-12 19:40:19.000000000 +0400
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2013, 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.geom.BaseBounds;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.jmx.MXNodeAlgorithm;
+import com.sun.javafx.jmx.MXNodeAlgorithmContext;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.traversal.Direction;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.sg.PGExternalNode;
+import com.sun.javafx.stage.FocusUngrabEvent;
+
+import javafx.application.Platform;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.beans.value.ChangeListener;
+import javafx.geometry.Point2D;
+import javafx.scene.input.KeyCode;
+
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import java.awt.AWTEvent;
+import java.awt.EventQueue;
+import java.awt.Toolkit;
+
+import java.nio.IntBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javafx.scene.Scene;
+import javafx.stage.Window;
+import sun.awt.UngrabEvent;
+import sun.swing.LightweightContent;
+import sun.swing.JLightweightFrame;
+
+/**
+ * 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
+ * that accepts an instance of Swing {@code JComponent}. The hierarchy of components
+ * contained in the {@code JComponent} instance should not contain any heavyweight
+ * components, otherwise {@code SwingNode} may fail to paint it. The content gets
+ * repainted automatically. All the input and focus events are forwarded to the
+ * {@code JComponent} instance transparently to the developer.
+ *
+ * Here is a typical pattern which demonstrates how {@code SwingNode} can be used:
+ *
() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, final Boolean newValue) {
+ activateLwFrame(newValue);
+ }
+ });
+ }
+
+ /**
+ * Attaches a {@code JComponent} instance to display in this {@code SwingNode}.
+ *
+ * The method can be called either on the JavaFX Application thread or the Swing thread.
+ * Note however, that access to a Swing component must occur from the Swing thread according
+ * to the Swing threading restrictions.
+ *
+ * @param content a Swing component to display in this {@code SwingNode}
+ *
+ * @see java.awt.EventQueue#isDispatchThread()
+ * @see javafx.application.Platform#isFxApplicationThread()
+ */
+ public void setContent(final JComponent content) {
+ this.content = content;
+
+ invokeOnEDT(new Runnable() {
+ @Override
+ public void run() {
+ setContentImpl(content);
+ }
+ });
+ }
+
+ /**
+ * Returns the {@code JComponent} instance attached to this {@code SwingNode}.
+ *
+ * The method can be called either on the JavaFX Application thread or the Swing thread.
+ * Note however, that access to a Swing component must occur from the Swing thread according
+ * to the Swing threading restrictions.
+ *
+ * @see java.awt.EventQueue#isDispatchThread()
+ * @see javafx.application.Platform#isFxApplicationThread()
+ *
+ * @return the Swing component attached to this {@code SwingNode}
+ */
+ public JComponent getContent() {
+ return content;
+ }
+
+ /*
+ * Called on Swing thread
+ */
+ private void setContentImpl(JComponent content) {
+ if (lwFrame != null) {
+ lwFrame.dispose();
+ lwFrame = null;
+ }
+ if (content != null) {
+ lwFrame = new JLightweightFrame();
+ contentProvider = new SwingNodeContent(content);
+ lwFrame.setContent(contentProvider);
+ lwFrame.setVisible(true);
+
+ locateLwFrame(); // initialize location
+
+ if (focusedProperty().get()) {
+ activateLwFrame(true);
+ }
+ }
+ }
+
+ private List peerRequests = new ArrayList<>();
+
+ /*
+ * Called on Swing thread
+ */
+ void setImageBuffer(final int[] data,
+ final int x, final int y,
+ final int w, final int h,
+ final int linestride)
+ {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ peer.setImageBuffer(IntBuffer.wrap(data), x, y, w, h, linestride);
+ impl_markDirty(DirtyBits.NODE_CONTENTS);
+ }
+ };
+ if (peer != null) {
+ Platform.runLater(r);
+ } else {
+ peerRequests.clear();
+ peerRequests.add(r);
+ }
+ }
+
+ /*
+ * Called on Swing thread
+ */
+ void setImageBounds(final int x, final int y, final int w, final int h) {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ peer.setImageBounds(x, y, w, h);
+ impl_markDirty(DirtyBits.NODE_CONTENTS);
+ }
+ };
+ if (peer != null) {
+ Platform.runLater(r);
+ } else {
+ peerRequests.add(r);
+ }
+ }
+
+ /*
+ * Called on Swing thread
+ */
+ void repaintDirtyRegion(final int dirtyX, final int dirtyY, final int dirtyWidth, final int dirtyHeight) {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ peer.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ impl_markDirty(DirtyBits.NODE_CONTENTS);
+ }
+ };
+ if (peer != null) {
+ Platform.runLater(r);
+ } else {
+ peerRequests.add(r);
+ }
+ }
+
+ @Override public boolean isResizable() {
+ return true;
+ }
+
+ @Override public void resize(final double width, final double height) {
+ this.width = width;
+ this.height = height;
+ super.resize(width, height);
+ impl_geomChanged();
+ impl_markDirty(DirtyBits.NODE_GEOMETRY);
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (lwFrame != null) {
+ lwFrame.setSize((int)width, (int)height);
+ }
+ }
+ });
+ }
+
+ @Override
+ public double maxWidth(double height) {
+ return Double.MAX_VALUE;
+ }
+
+ @Override
+ public double maxHeight(double width) {
+ return Double.MAX_VALUE;
+ }
+
+ @Override
+ public double prefWidth(double height) {
+ return -1;
+ }
+
+ @Override
+ public double prefHeight(double width) {
+ return -1;
+ }
+
+ @Override
+ public double minWidth(double height) {
+ return 0;
+ }
+
+ @Override
+ public double minHeight(double width) {
+ return 0;
+ }
+
+ @Override
+ protected boolean impl_computeContains(double localX, double localY) {
+ return true;
+ }
+
+ private InvalidationListener locationListener = new InvalidationListener() {
+ @Override
+ public void invalidated(Observable observable) {
+ locateLwFrame();
+ }
+ };
+
+ private EventHandler ungrabHandler = new EventHandler() {
+ @Override
+ public void handle(FocusUngrabEvent event) {
+ if (!skipBackwardUnrgabNotification) {
+ AccessController.doPrivileged(new PostEventAction(new UngrabEvent(lwFrame)));
+ }
+ }
+ };
+
+ private ChangeListener windowVisibleListener = new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+ if (!newValue) {
+ disposeLwFrame();
+
+ } else {
+ setContent(content);
+ }
+ }
+ };
+
+ private void removeListeners(Scene scene) {
+ Window window = scene.getWindow();
+ if (window != null) {
+ window.xProperty().removeListener(locationListener);
+ window.yProperty().removeListener(locationListener);
+ window.removeEventHandler(FocusUngrabEvent.FOCUS_UNGRAB, ungrabHandler);
+ window.showingProperty().removeListener(windowVisibleListener);
+ }
+ }
+
+ private void addListeners(Scene scene) {
+ Window window = scene.getWindow();
+ if (window != null) {
+ window.xProperty().addListener(locationListener);
+ window.yProperty().addListener(locationListener);
+ window.addEventHandler(FocusUngrabEvent.FOCUS_UNGRAB, ungrabHandler);
+ window.showingProperty().addListener(windowVisibleListener);
+ }
+ }
+
+ @Override
+ protected PGNode impl_createPGNode() {
+ peer = com.sun.javafx.tk.Toolkit.getToolkit().createPGExternalNode();
+ peer.setLock(paintLock);
+ for (Runnable request : peerRequests) {
+ request.run();
+ }
+ peerRequests = null;
+
+ if (content != null) {
+ setContent(content); // in case the Node is re-added to Scene
+ }
+ addListeners(getScene());
+
+ sceneProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Scene> observable, Scene oldValue, Scene newValue) {
+ // Removed from scene, or added to another scene.
+ // The lwFrame will be recreated from impl_createPGNode().
+ removeListeners(oldValue);
+ disposeLwFrame();
+ }
+ });
+
+ impl_treeVisibleProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+ setLwFrameVisible(newValue);
+ }
+ });
+
+ return peer;
+ }
+
+ @Override
+ public void impl_updatePG() {
+ super.impl_updatePG();
+
+ if (impl_isDirty(DirtyBits.NODE_VISIBLE)) {
+ locateLwFrame(); // initialize location
+ }
+ }
+
+ private void locateLwFrame() {
+ if (getScene() == null || lwFrame == null) {
+ return;
+ }
+ final Point2D loc = localToScene(0, 0);
+ final int windowX = (int)getScene().getWindow().getX();
+ final int windowY = (int)getScene().getWindow().getY();
+ final int sceneX = (int)getScene().getX();
+ final int sceneY = (int)getScene().getY();
+
+ invokeOnEDT(new Runnable() {
+ @Override
+ public void run() {
+ if (lwFrame != null) {
+ lwFrame.setLocation(windowX + sceneX + (int)loc.getX(),
+ windowY + sceneY + (int)loc.getY());
+ }
+ }
+ });
+ }
+
+ private void activateLwFrame(final boolean activate) {
+ if (lwFrame == null) {
+ return;
+ }
+ invokeOnEDT(new Runnable() {
+ @Override
+ public void run() {
+ if (lwFrame != null) {
+ lwFrame.emulateActivation(activate);
+ }
+ }
+ });
+ }
+
+ private void disposeLwFrame() {
+ if (lwFrame == null) {
+ return;
+ }
+ invokeOnEDT(new Runnable() {
+ @Override
+ public void run() {
+ if (lwFrame != null) {
+ lwFrame.dispose();
+ lwFrame = null;
+ }
+ }
+ });
+ }
+
+ private void setLwFrameVisible(final boolean visible) {
+ if (lwFrame == null) {
+ return;
+ }
+ invokeOnEDT(new Runnable() {
+ @Override
+ public void run() {
+ if (lwFrame != null) {
+ lwFrame.setVisible(visible);
+ }
+ }
+ });
+ }
+
+ @Override
+ public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+ bounds.deriveWithNewBounds(0, 0, 0, (float)width, (float)height, 0);
+ tx.transform(bounds, bounds);
+ return bounds;
+ }
+
+ @Override
+ public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
+ return alg.processLeafNode(this, ctx);
+ }
+
+ private class SwingNodeContent implements LightweightContent {
+ private JComponent comp;
+ public SwingNodeContent(JComponent comp) {
+ this.comp = comp;
+ }
+ @Override
+ public JComponent getComponent() {
+ return comp;
+ }
+ @Override
+ public void paintLock() {
+ paintLock.lock();
+ }
+ @Override
+ public void paintUnlock() {
+ paintLock.unlock();
+ }
+ @Override
+ public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) {
+ SwingNode.this.setImageBuffer(data, x, y, width, height, linestride);
+ }
+ @Override
+ public void imageReshaped(int x, int y, int width, int height) {
+ SwingNode.this.setImageBounds(x, y, width, height);
+ }
+ @Override
+ public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
+ SwingNode.this.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ }
+ @Override
+ public void focusGrabbed() {
+ Platform.runLater(new Runnable() {
+ @Override
+ public void run() {
+ if (getScene() != null && getScene().getWindow() != null) {
+ getScene().getWindow().impl_getPeer().grabFocus();
+ }
+ }
+ });
+ }
+ @Override
+ public void focusUngrabbed() {
+ Platform.runLater(new Runnable() {
+ @Override
+ public void run() {
+ if (getScene() != null && getScene().getWindow() != null) {
+ skipBackwardUnrgabNotification = true;
+ getScene().getWindow().impl_getPeer().ungrabFocus();
+ skipBackwardUnrgabNotification = false;
+ }
+ }
+ });
+ }
+ }
+
+ private class PostEventAction implements PrivilegedAction {
+ private AWTEvent event;
+ public PostEventAction(AWTEvent event) {
+ this.event = event;
+ }
+ @Override
+ public Void run() {
+ EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ eq.postEvent(event);
+ return null;
+ }
+ }
+
+ private class SwingMouseEventHandler implements EventHandler {
+ @Override
+ public void handle(MouseEvent event) {
+ if (event.getEventType() == MouseEvent.MOUSE_PRESSED &&
+ !SwingNode.this.isFocused() && SwingNode.this.isFocusTraversable())
+ {
+ SwingNode.this.requestFocus();
+ }
+ int swingID = SwingEvents.fxMouseEventTypeToMouseID(event);
+ if (swingID < 0) {
+ return;
+ }
+ int swingModifiers = SwingEvents.fxMouseModsToMouseMods(event);
+ // TODO: popupTrigger
+ boolean swingPopupTrigger = event.getButton() == MouseButton.SECONDARY;
+ int swingButton = SwingEvents.fxMouseButtonToMouseButton(event);
+ long swingWhen = System.currentTimeMillis();
+ java.awt.event.MouseEvent mouseEvent =
+ new java.awt.event.MouseEvent(
+ lwFrame, swingID, swingWhen, swingModifiers,
+ (int)event.getX(), (int)event.getY(), (int)event.getScreenX(), (int)event.getSceneY(),
+ event.getClickCount(), swingPopupTrigger, swingButton);
+ AccessController.doPrivileged(new PostEventAction(mouseEvent));
+ }
+ }
+
+ private class SwingKeyEventHandler implements EventHandler {
+ @Override
+ public void handle(KeyEvent event) {
+ if (event.getCharacter().isEmpty()) {
+ // TODO: should we post an "empty" character?
+ return;
+ }
+ // Let Ctrl+Tab, Shift+Strl+Tab traverse focus out.
+ if (event.getCode() == KeyCode.TAB && event.isControlDown()) {
+ Direction d = event.isShiftDown() ? Direction.PREVIOUS : Direction.NEXT;
+ getParent().getImpl_traversalEngine().trav(SwingNode.this, d);
+ return;
+ }
+ // Don't let Arrows, Tab, Shift+Tab traverse focus out.
+ if (event.getCode() == KeyCode.LEFT ||
+ event.getCode() == KeyCode.RIGHT ||
+ event.getCode() == KeyCode.TAB)
+ {
+ event.consume();
+ }
+
+ int swingID = SwingEvents.fxKeyEventTypeToKeyID(event);
+ if (swingID < 0) {
+ return;
+ }
+ int swingModifiers = SwingEvents.fxKeyModsToKeyMods(event);
+ int swingKeyCode = event.getCode().impl_getCode();
+ char swingChar = event.getCharacter().charAt(0);
+ long swingWhen = System.currentTimeMillis();
+ java.awt.event.KeyEvent keyEvent = new java.awt.event.KeyEvent(
+ lwFrame, swingID, swingWhen, swingModifiers,
+ swingKeyCode, swingChar);
+ AccessController.doPrivileged(new PostEventAction(keyEvent));
+ }
+ }
+
+ private static void invokeOnEDT(final Runnable r) {
+ if (SwingUtilities.isEventDispatchThread()) {
+ r.run();
+ } else {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ r.run();
+ }
+ });
+ }
+ }
+}
--- /dev/null 2013-03-12 19:40:20.000000000 +0400
+++ new/javafx-sg-common/src/com/sun/javafx/sg/PGExternalNode.java 2013-03-12 19:40:19.000000000 +0400
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 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.sg;
+
+import java.nio.Buffer;
+import java.util.concurrent.locks.ReentrantLock;
+
+public interface PGExternalNode extends PGNode {
+ public void setLock(ReentrantLock lock);
+
+ public void setImageBuffer(Buffer buffer, int x, int y, int width, int height, int linestride);
+
+ public void setImageBounds(int x, int y, int width, int height);
+
+ public void repaintDirtyRegion(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight);
+}
--- /dev/null 2013-03-12 19:40:20.000000000 +0400
+++ new/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGExternalNode.java 2013-03-12 19:40:20.000000000 +0400
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013, 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.sg.prism;
+
+import com.sun.javafx.geom.Rectangle;
+import com.sun.javafx.sg.PGExternalNode;
+
+import com.sun.prism.Graphics;
+import com.sun.prism.PixelFormat;
+import com.sun.prism.ResourceFactory;
+import com.sun.prism.Texture;
+
+import java.nio.Buffer;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class NGExternalNode extends NGNode implements PGExternalNode {
+
+ // pixel buffer of the source image
+ volatile private Buffer srcbuffer;
+
+ // line stride of the source buffer
+ volatile private int linestride;
+
+ // source image bounds
+ volatile private int srcx;
+ volatile private int srcy;
+ volatile private int srcwidth;
+ volatile private int srcheight;
+
+ // of the same size as the source image buffer
+ private Texture dsttexture;
+
+ // size of the source image buffer
+ volatile private int dstwidth;
+ volatile private int dstheight;
+
+ // when the content shrinks, we need to clear the target
+ volatile private boolean clearTarget = false;
+
+ // relative to the [srcx, srcy]
+ volatile private Rectangle dirtyRect;
+ volatile private boolean isDirty;
+
+ volatile private Lock paintLock;
+
+ @Override
+ protected void renderContent(Graphics g) {
+ paintLock.lock();
+ try {
+ if (srcbuffer == null) {
+ // the buffer may be initialized with some delay, asynchronously
+ return;
+ }
+ if ((dsttexture == null) ||
+ (dsttexture.getContentWidth() != dstwidth) ||
+ (dsttexture.getContentHeight() != dstheight))
+ {
+ ResourceFactory factory = g.getResourceFactory();
+ if (!factory.isDeviceReady()) {
+ System.err.println("NGExternalNode: graphics device is not ready");
+ return;
+ }
+ if (dsttexture != null) {
+ dsttexture.dispose();
+ }
+ dsttexture =
+ factory.createTexture(PixelFormat.INT_ARGB_PRE,
+ Texture.Usage.DYNAMIC,
+ Texture.WrapMode.CLAMP_NOT_NEEDED,
+ dstwidth, dstheight);
+
+ if (dsttexture == null) {
+ System.err.println("NGExternalNode: failed to create a texture");
+ return;
+ }
+ }
+ if (dirtyRect == null) return;
+
+ dsttexture.update(srcbuffer,
+ PixelFormat.INT_ARGB_PRE,
+ srcx + dirtyRect.x, srcy + dirtyRect.y, // dst
+ srcx + dirtyRect.x, srcy + dirtyRect.y, dirtyRect.width, dirtyRect.height, // src
+ linestride * 4,
+ false);
+
+ if (clearTarget) {
+ clearTarget = false;
+ g.clear();
+ }
+ g.drawTexture(dsttexture,
+ srcx, srcy, srcx + srcwidth, srcy + srcheight, // dst
+ srcx, srcy, srcx + srcwidth, srcy + srcheight); // src
+
+ isDirty = false;
+
+ } finally {
+ paintLock.unlock();
+ }
+ }
+
+ @Override
+ public void setLock(ReentrantLock lock) {
+ this.paintLock = lock;
+ }
+
+ @Override
+ public void setImageBuffer(Buffer buffer, int x, int y, int width, int height, int linestride) {
+ paintLock.lock();
+ try {
+ this.srcbuffer = buffer;
+
+ this.srcx = x;
+ this.srcy = y;
+ this.srcwidth = width;
+ this.srcheight = height;
+
+ this.linestride = linestride;
+
+ this.dstwidth = linestride;
+ this.dstheight = buffer.capacity() / linestride;
+
+ dirtyRect = new Rectangle(x, y, width, height);
+
+ } finally {
+ paintLock.unlock();
+ }
+ }
+
+ @Override
+ public void setImageBounds(int x, int y, int width, int height) {
+ paintLock.lock();
+ try {
+ // content shrinked
+ if (width < srcwidth || height < srcheight) {
+ clearTarget = true;
+ }
+ this.srcx = x;
+ this.srcy = y;
+ this.srcwidth = width;
+ this.srcheight = height;
+
+ dirtyRect.setBounds(x, y, width, height);
+
+ } finally {
+ paintLock.unlock();
+ }
+ }
+
+ @Override
+ public void repaintDirtyRegion(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
+ paintLock.lock();
+ try {
+ if (isDirty) {
+ dirtyRect.add(new Rectangle(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
+ } else {
+ dirtyRect.setBounds(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ }
+ // System.out.println("NGExternalNode.repaintDirtyRegion: " + dirtyRect);
+
+ isDirty = true;
+ visualsChanged();
+
+ } finally {
+ paintLock.unlock();
+ }
+ }
+
+ @Override
+ protected boolean hasOverlappingContents() { return false; }
+}
--- /dev/null 2013-03-12 19:40:21.000000000 +0400
+++ new/test-stub-toolkit/src/com/sun/javafx/pgstub/StubExternalNode.java 2013-03-12 19:40:21.000000000 +0400
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 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.pgstub;
+
+import com.sun.javafx.sg.PGExternalNode;
+
+import java.nio.Buffer;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class StubExternalNode extends StubNode implements PGExternalNode {
+ @Override
+ public void setLock(ReentrantLock lock) {}
+
+ @Override
+ public void setImageBuffer(Buffer buffer, int x, int y, int width, int height, int linestride) {}
+
+ @Override
+ public void setImageBounds(int x, int y, int width, int height) {}
+
+ @Override
+ public void repaintDirtyRegion(int x, int y, int width, int height) {}
+}