< prev index next >

modules/javafx.swing/src/main/java/javafx/embed/swing/SwingNode.java

Print this page




  36 import javafx.scene.input.KeyCode;
  37 import javafx.scene.input.KeyEvent;
  38 import javafx.scene.input.MouseButton;
  39 import javafx.scene.input.MouseEvent;
  40 import javafx.scene.input.ScrollEvent;
  41 import javafx.stage.Window;
  42 import javax.swing.JComponent;
  43 import javax.swing.Timer;
  44 import java.awt.AWTEvent;
  45 import java.awt.Component;
  46 import java.awt.Cursor;
  47 import java.awt.EventQueue;
  48 import java.awt.Toolkit;
  49 import java.lang.ref.WeakReference;
  50 import java.awt.dnd.DragGestureEvent;
  51 import java.awt.dnd.DragGestureListener;
  52 import java.awt.dnd.DragGestureRecognizer;
  53 import java.awt.dnd.DragSource;
  54 import java.awt.dnd.DropTarget;
  55 import java.awt.dnd.InvalidDnDOperationException;
  56 import java.awt.dnd.peer.DragSourceContextPeer;
  57 import java.awt.event.InputEvent;
  58 import java.awt.event.MouseWheelEvent;
  59 import java.awt.event.WindowEvent;
  60 import java.awt.event.WindowFocusListener;
  61 import java.lang.reflect.Method;
  62 import java.nio.IntBuffer;
  63 import java.security.AccessController;
  64 import java.security.PrivilegedAction;
  65 import java.util.ArrayList;
  66 import java.util.HashSet;
  67 import java.util.List;
  68 import java.util.Set;
  69 import java.util.concurrent.locks.ReentrantLock;
  70 import com.sun.javafx.embed.swing.Disposer;
  71 import com.sun.javafx.embed.swing.DisposerRecord;
  72 import com.sun.javafx.geom.BaseBounds;
  73 import com.sun.javafx.geom.transform.BaseTransform;
  74 import com.sun.javafx.scene.DirtyBits;
  75 import com.sun.javafx.sg.prism.NGExternalNode;
  76 import com.sun.javafx.sg.prism.NGNode;
  77 import com.sun.javafx.stage.FocusUngrabEvent;
  78 import com.sun.javafx.stage.WindowHelper;
  79 import com.sun.javafx.tk.TKStage;
  80 import com.sun.javafx.PlatformUtil;
  81 import com.sun.javafx.embed.swing.SwingNodeHelper;
  82 import com.sun.javafx.scene.NodeHelper;
  83 import sun.awt.UngrabEvent;
  84 import sun.swing.JLightweightFrame;
  85 import sun.swing.LightweightContent;



  86 
  87 import static javafx.stage.WindowEvent.WINDOW_HIDDEN;
  88 
  89 /**
  90  * This class is used to embed a Swing content into a JavaFX application.
  91  * The content to be displayed is specified with the {@link #setContent} method
  92  * that accepts an instance of Swing {@code JComponent}. The hierarchy of components
  93  * contained in the {@code JComponent} instance should not contain any heavyweight
  94  * components, otherwise {@code SwingNode} may fail to paint it. The content gets
  95  * repainted automatically. All the input and focus events are forwarded to the
  96  * {@code JComponent} instance transparently to the developer.
  97  * <p>
  98  * Here is a typical pattern which demonstrates how {@code SwingNode} can be used:
  99  * <pre>
 100  *     public class SwingFx extends Application {
 101  *
 102  *         @Override
 103  *         public void start(Stage stage) {
 104  *             final SwingNode swingNode = new SwingNode();
 105  *             createAndSetSwingContent(swingNode);


 159             }
 160 
 161             @Override
 162             public boolean doComputeContains(Node node, double localX, double localY) {
 163                 return ((SwingNode) node).doComputeContains(localX, localY);
 164             }
 165         });
 166     }
 167 
 168     private double fxWidth;
 169     private double fxHeight;
 170 
 171     private int swingPrefWidth;
 172     private int swingPrefHeight;
 173     private int swingMaxWidth;
 174     private int swingMaxHeight;
 175     private int swingMinWidth;
 176     private int swingMinHeight;
 177 
 178     private volatile JComponent content;
 179     private volatile JLightweightFrame lwFrame;
 180     final JLightweightFrame getLightweightFrame() { return lwFrame; }
 181 
 182     private NGExternalNode peer;
 183 
 184     private final ReentrantLock paintLock = new ReentrantLock();
 185 
 186     private boolean skipBackwardUnrgabNotification;
 187     private boolean grabbed; // lwframe initiated grab
 188     private Timer deactivate; // lwFrame deactivate delay for Linux
 189 
 190     {
 191         // To initialize the class helper at the begining each constructor of this class
 192         SwingNodeHelper.initHelper(this);
 193     }
 194 
 195     /**
 196      * Constructs a new instance of {@code SwingNode}.
 197      */
 198     public SwingNode() {
 199         setFocusTraversable(true);
 200         setEventHandler(MouseEvent.ANY, new SwingMouseEventHandler());
 201         setEventHandler(KeyEvent.ANY, new SwingKeyEventHandler());
 202         setEventHandler(ScrollEvent.SCROLL, new SwingScrollEventHandler());
 203 
 204         focusedProperty().addListener((observable, oldValue, newValue) -> {
 205              activateLwFrame(newValue);
 206         });
 207 
 208         //Workaround for RT-34170
 209         javafx.scene.text.Font.getFamilies();
 210     }
 211 
 212 
 213     private EventHandler windowHiddenHandler = (Event event) -> {
 214         if (lwFrame != null &&  event.getTarget() instanceof Window) {
 215             final Window w = (Window) event.getTarget();
 216             TKStage tk = WindowHelper.getPeer(w);
 217             if (tk != null) {
 218                 if (isThreadMerged) {
 219                     jlfOverrideNativeWindowHandle.invoke(lwFrame, 0L, null);
 220                 } else {
 221                     // Postpone actual window closing to ensure that
 222                     // a native window handler is valid on a Swing side
 223                     tk.postponeClose();
 224                     SwingFXUtils.runOnEDT(() -> {
 225                         jlfOverrideNativeWindowHandle.invoke(lwFrame, 0L,
 226                                 (Runnable) () -> SwingFXUtils.runOnFxThread(
 227                                         () -> tk.closePostponed()));
 228                     });
 229                 }
 230             }
 231         }
 232 
 233     };
 234 
 235     private Window hWindow = null;
 236     private void notifyNativeHandle(Window window) {
 237         if (hWindow != window) {
 238             if (hWindow != null) {
 239                 hWindow.removeEventHandler(WINDOW_HIDDEN, windowHiddenHandler);
 240             }
 241             if (window != null) {
 242                 window.addEventHandler(WINDOW_HIDDEN, windowHiddenHandler);
 243             }
 244             hWindow = window;
 245         }
 246 
 247         if (lwFrame != null) {
 248             long rawHandle = 0L;
 249             if (window != null) {
 250                 TKStage tkStage = WindowHelper.getPeer(window);
 251                 if (tkStage != null) {
 252                     rawHandle = tkStage.getRawHandle();
 253                 }
 254             }
 255             jlfOverrideNativeWindowHandle.invoke(lwFrame, rawHandle, null);
 256         }
 257     }
 258 
 259     /**
 260      * Attaches a {@code JComponent} instance to display in this {@code SwingNode}.
 261      * <p>
 262      * The method can be called either on the JavaFX Application thread or the Event Dispatch thread.
 263      * Note however, that access to a Swing component must occur from the Event Dispatch thread
 264      * according to the Swing threading restrictions.
 265      *
 266      * @param content a Swing component to display in this {@code SwingNode}
 267      *
 268      * @see java.awt.EventQueue#isDispatchThread()
 269      * @see javafx.application.Platform#isFxApplicationThread()
 270      */
 271     public void setContent(final JComponent content) {
 272         this.content = content;
 273 
 274         SwingFXUtils.runOnEDT(() -> setContentImpl(content));
 275     }


 314         }
 315 
 316         public boolean isIntegerApi() {
 317             return isIntegerAPI;
 318         }
 319 
 320         public Object invoke(T object, Object... args) {
 321             if (method != null) {
 322                 try {
 323                     return method.invoke(object, args);
 324                 } catch (Throwable ex) {
 325                     throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex);
 326                 }
 327             } else {
 328                 return null;
 329             }
 330         }
 331     }
 332 
 333     /**
 334      * Calls JLightweightFrame.notifyDisplayChanged.
 335      * Must be called on EDT only.
 336      */
 337     private static OptionalMethod<JLightweightFrame> jlfNotifyDisplayChanged;
 338     private static OptionalMethod<JLightweightFrame> jlfOverrideNativeWindowHandle;


 339 
 340     static {
 341         jlfNotifyDisplayChanged = new OptionalMethod<>(JLightweightFrame.class,
 342                 "notifyDisplayChanged", Double.TYPE, Double.TYPE);
 343         if (!jlfNotifyDisplayChanged.isSupported()) {
 344             jlfNotifyDisplayChanged = new OptionalMethod<>(
 345                   JLightweightFrame.class,"notifyDisplayChanged", Integer.TYPE);
 346         }
 347 
 348         jlfOverrideNativeWindowHandle = new OptionalMethod<>(JLightweightFrame.class,
 349                 "overrideNativeWindowHandle", Long.TYPE, Runnable.class);

 350 

 351     }
 352 
 353     /*
 354      * Called on EDT
 355      */
 356     private void setContentImpl(JComponent content) {
 357         if (lwFrame != null) {
 358             lwFrame.dispose();
 359             lwFrame = null;
 360         }
 361         if (content != null) {
 362             lwFrame = new JLightweightFrame();
 363 
 364             SwingNodeWindowFocusListener snfListener =
 365                                  new SwingNodeWindowFocusListener(this);
 366             lwFrame.addWindowFocusListener(snfListener);
 367 
 368             if (getScene() != null) {
 369                 Window window = getScene().getWindow();
 370                 if (window != null) {
 371                     if (jlfNotifyDisplayChanged.isIntegerApi()) {
 372                         jlfNotifyDisplayChanged.invoke(lwFrame,
 373                                 (int) Math.round(window.getRenderScaleX()));
 374                     } else {
 375                         jlfNotifyDisplayChanged.invoke(lwFrame,
 376                                 window.getRenderScaleX(),
 377                                 window.getRenderScaleY());
 378                     }
 379                 }
 380             }
 381             lwFrame.setContent(new SwingNodeContent(content, this));
 382             lwFrame.setVisible(true);


 542      * @return the minimum height that the node should be resized to during layout
 543      */
 544     @Override public double minHeight(double width) {
 545         return swingMinHeight;
 546     }
 547 
 548     /*
 549      * Note: This method MUST only be called via its accessor method.
 550      */
 551     private boolean doComputeContains(double localX, double localY) {
 552         return true;
 553     }
 554 
 555     private final InvalidationListener locationListener = observable -> {
 556         locateLwFrame();
 557     };
 558 
 559     private final EventHandler<FocusUngrabEvent> ungrabHandler = event -> {
 560         if (!skipBackwardUnrgabNotification) {
 561             if (lwFrame != null) {
 562                 AccessController.doPrivileged(new PostEventAction(new UngrabEvent(lwFrame)));

 563             }
 564         }
 565     };
 566 
 567     private final ChangeListener<Boolean> windowVisibleListener = (observable, oldValue, newValue) -> {
 568         if (!newValue) {
 569             disposeLwFrame();
 570         } else {
 571             setContent(content);
 572         }
 573     };
 574 
 575     private final ChangeListener<Window> sceneWindowListener = (observable, oldValue, newValue) -> {
 576         if (oldValue != null) {
 577             removeWindowListeners(oldValue);
 578         }
 579 
 580         notifyNativeHandle(newValue);
 581 
 582         if (newValue != null) {


 656             setLwFrameVisible(newValue);
 657         });
 658 
 659         return peer;
 660     }
 661 
 662     /*
 663      * Note: This method MUST only be called via its accessor method.
 664      */
 665     private void doUpdatePeer() {
 666         if (NodeHelper.isDirty(this, DirtyBits.NODE_VISIBLE)
 667                 || NodeHelper.isDirty(this, DirtyBits.NODE_BOUNDS)) {
 668             locateLwFrame(); // initialize location
 669         }
 670         if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
 671             peer.markContentDirty();
 672         }
 673     }
 674 
 675     /**
 676      * Calls JLightweightFrame.setHostBounds.
 677      * Must be called on EDT only.
 678      */
 679     private static final OptionalMethod<JLightweightFrame> jlfSetHostBounds =
 680         new OptionalMethod<>(JLightweightFrame.class, "setHostBounds",
 681                 Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE);
 682 
 683     private void locateLwFrame() {
 684         if (getScene() == null
 685                 || lwFrame == null
 686                 || getScene().getWindow() == null
 687                 || !getScene().getWindow().isShowing()) {
 688             // Not initialized yet. Skip the update to set the real values later
 689             return;
 690         }
 691         Window w = getScene().getWindow();
 692         double renderScaleX = w.getRenderScaleX();
 693         double renderScaleY = w.getRenderScaleY();
 694         final Point2D loc = localToScene(0, 0);
 695         final int windowX = (int) (w.getX());
 696         final int windowY = (int) (w.getY());
 697         final int windowW = (int) (w.getWidth());
 698         final int windowH = (int) (w.getHeight());
 699         final int frameX = (int) Math.round(w.getX() + getScene().getX() + loc.getX());
 700         final int frameY = (int) Math.round(w.getY() + getScene().getY() + loc.getY());


 780                 if (jlfNotifyDisplayChanged.isIntegerApi()) {
 781                     jlfNotifyDisplayChanged.invoke(lwFrame,
 782                                                        (int)Math.round(scaleX));
 783                 } else {
 784                     jlfNotifyDisplayChanged.invoke(lwFrame, scaleX, scaleY);
 785                 }
 786             }
 787         });
 788     }
 789 
 790     /*
 791      * Note: This method MUST only be called via its accessor method.
 792      */
 793     private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
 794         bounds.deriveWithNewBounds(0, 0, 0, (float)fxWidth, (float)fxHeight, 0);
 795         tx.transform(bounds, bounds);
 796         return bounds;
 797     }
 798 
 799     private static class SwingNodeDisposer implements DisposerRecord {
 800          JLightweightFrame lwFrame;
 801 
 802          SwingNodeDisposer(JLightweightFrame ref) {
 803              this.lwFrame = ref;
 804          }
 805          public void dispose() {
 806              if (lwFrame != null) {
 807                  lwFrame.dispose();
 808                  lwFrame = null;
 809              }
 810          }
 811     }
 812 
 813     private static class SwingNodeWindowFocusListener implements WindowFocusListener {
 814         private WeakReference<SwingNode> swingNodeRef;
 815 
 816         SwingNodeWindowFocusListener(SwingNode swingNode) {
 817             this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
 818         }
 819 
 820         @Override
 821         public void windowGainedFocus(WindowEvent e) {
 822             SwingFXUtils.runOnFxThread(() -> {
 823                 SwingNode swingNode = swingNodeRef.get();
 824                 if (swingNode != null) {
 825                     swingNode.requestFocus();
 826                 }
 827             });
 828         }
 829 
 830         @Override
 831         public void windowLostFocus(WindowEvent e) {
 832             SwingFXUtils.runOnFxThread(() -> {
 833                 SwingNode swingNode = swingNodeRef.get();
 834                 if (swingNode != null) {
 835                     swingNode.ungrabFocus(true);
 836                 }
 837             });
 838         }
 839     }
 840 
 841     private static class SwingNodeContent implements LightweightContent {
 842         private JComponent comp;
 843         private volatile FXDnD dnd;
 844         private WeakReference<SwingNode> swingNodeRef;
 845 
 846         SwingNodeContent(JComponent comp, SwingNode swingNode) {
 847             this.comp = comp;
 848             this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
 849         }
 850         @Override
 851         public JComponent getComponent() {
 852             return comp;
 853         }
 854         @Override
 855         public void paintLock() {
 856             SwingNode swingNode = swingNodeRef.get();
 857             if (swingNode != null) {
 858                 swingNode.paintLock.lock();
 859             }
 860         }
 861         @Override
 862         public void paintUnlock() {
 863             SwingNode swingNode = swingNodeRef.get();
 864             if (swingNode != null) {
 865                 swingNode.paintLock.unlock();
 866             }
 867         }
 868 
 869         // Note: we skip @Override annotation and implement both pre-hiDPI and post-hiDPI versions
 870         // of the method for compatibility.
 871         //@Override
 872         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) {
 873             imageBufferReset(data, x, y, width, height, linestride, 1);
 874         }
 875         //@Override
 876         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) {
 877             SwingNode swingNode = swingNodeRef.get();
 878             if (swingNode != null) {
 879                 swingNode.setImageBuffer(data, x, y, width, height, linestride, scale, scale);
 880             }
 881         }
 882         //@Override
 883         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) {
 884             SwingNode swingNode = swingNodeRef.get();
 885             if (swingNode != null) {
 886                 swingNode.setImageBuffer(data, x, y, width, height, linestride, scaleX, scaleY);
 887             }
 888         }
 889         @Override
 890         public void imageReshaped(int x, int y, int width, int height) {
 891             SwingNode swingNode = swingNodeRef.get();
 892             if (swingNode != null) {
 893                 swingNode.setImageBounds(x, y, width, height);
 894             }
 895         }
 896         @Override
 897         public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
 898             SwingNode swingNode = swingNodeRef.get();
 899             if (swingNode != null) {
 900                 swingNode.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 901             }
 902         }


 967             SwingFXUtils.runOnFxThread(() -> {
 968                 SwingNode swingNode = swingNodeRef.get();
 969                 if (swingNode != null) {
 970                     swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor));
 971                 }
 972             });
 973         }
 974 
 975         private void initDnD() {
 976             // This is a part of AWT API, so the method may be invoked on any thread
 977             synchronized (SwingNodeContent.this) {
 978                 if (this.dnd == null) {
 979                     SwingNode swingNode = swingNodeRef.get();
 980                     if (swingNode != null) {
 981                         this.dnd = new FXDnD(swingNode);
 982                     }
 983                 }
 984             }
 985         }
 986 
 987         //@Override
 988         public synchronized <T extends DragGestureRecognizer> T createDragGestureRecognizer(
 989                 Class<T> abstractRecognizerClass,
 990                 DragSource ds, Component c, int srcActions,
 991                 DragGestureListener dgl)
 992         {
 993             initDnD();
 994             return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl);
 995         }
 996 
 997         //@Override
 998         public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException
 999         {
1000             initDnD();
1001             return dnd.createDragSourceContextPeer(dge);
1002         }
1003 
1004         //@Override
1005         public void addDropTarget(DropTarget dt) {
1006             initDnD();
1007             dnd.addDropTarget(dt);
1008         }
1009 
1010         //@Override
1011         public void removeDropTarget(DropTarget dt) {
1012             initDnD();
1013             dnd.removeDropTarget(dt);
1014         }
1015     }
1016 
1017     private void ungrabFocus(boolean postUngrabEvent) {
1018         // On X11 grab is limited to a single XDisplay connection,
1019         // so we can't delegate it to another GUI toolkit.
1020         if (PlatformUtil.isLinux()) return;
1021 
1022         if (grabbed &&
1023             getScene() != null &&
1024             getScene().getWindow() != null &&
1025             WindowHelper.getPeer(getScene().getWindow()) != null)
1026         {
1027             skipBackwardUnrgabNotification = !postUngrabEvent;
1028             WindowHelper.getPeer(getScene().getWindow()).ungrabFocus();
1029             skipBackwardUnrgabNotification = false;
1030             grabbed = false;


1032     }
1033 
1034     private class PostEventAction implements PrivilegedAction<Void> {
1035         private AWTEvent event;
1036         PostEventAction(AWTEvent event) {
1037             this.event = event;
1038         }
1039         @Override
1040         public Void run() {
1041             EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
1042             eq.postEvent(event);
1043             return null;
1044         }
1045     }
1046 
1047     private class SwingMouseEventHandler implements EventHandler<MouseEvent> {
1048         private final Set<MouseButton> mouseClickedAllowed = new HashSet<>();
1049 
1050         @Override
1051         public void handle(MouseEvent event) {
1052             JLightweightFrame frame = lwFrame;
1053             if (frame == null) {
1054                 return;
1055             }
1056             int swingID = SwingEvents.fxMouseEventTypeToMouseID(event);
1057             if (swingID < 0) {
1058                 return;
1059             }
1060 
1061             // Prevent ancestors of the SwingNode from stealing the focus
1062             event.consume();
1063 
1064             final EventType<?> type = event.getEventType();
1065             if (type == MouseEvent.MOUSE_PRESSED) {
1066                 mouseClickedAllowed.add(event.getButton());
1067             } else if (type == MouseEvent.MOUSE_RELEASED) {
1068                 // RELEASED comes before CLICKED, so we don't remove the button from the set
1069                 //mouseClickedAllowed.remove(event.getButton());
1070             } else if (type == MouseEvent.MOUSE_DRAGGED) {
1071                 // This is what AWT/Swing do
1072                 mouseClickedAllowed.clear();
1073             } else if (type == MouseEvent.MOUSE_CLICKED) {
1074                 if (event.getClickCount() == 1 && !mouseClickedAllowed.contains(event.getButton())) {
1075                     // RT-34610: In FX, CLICKED events are generated even after dragging the mouse pointer
1076                     // Note that this is only relevant for single clicks. Double clicks use a smudge factor.
1077                     return;
1078                 }
1079                 mouseClickedAllowed.remove(event.getButton());
1080             }
1081             int swingModifiers = SwingEvents.fxMouseModsToMouseMods(event);
1082             boolean swingPopupTrigger = event.isPopupTrigger();
1083             int swingButton = SwingEvents.fxMouseButtonToMouseButton(event);
1084             long swingWhen = System.currentTimeMillis();
1085             int relX = (int) Math.round(event.getX());
1086             int relY = (int) Math.round(event.getY());
1087             int absX = (int) Math.round(event.getScreenX());
1088             int absY = (int) Math.round(event.getScreenY());
1089             java.awt.event.MouseEvent mouseEvent =
1090                     new java.awt.event.MouseEvent(
1091                         frame, swingID, swingWhen, swingModifiers,
1092                         relX, relY, absX, absY,
1093                         event.getClickCount(), swingPopupTrigger, swingButton);
1094             AccessController.doPrivileged(new PostEventAction(mouseEvent));
1095         }
1096     }
1097 
1098     private class SwingScrollEventHandler implements EventHandler<ScrollEvent> {
1099         @Override
1100         public void handle(ScrollEvent event) {
1101             JLightweightFrame frame = lwFrame;
1102             if (frame == null) {
1103                 return;
1104             }
1105 
1106             int swingModifiers = SwingEvents.fxScrollModsToMouseWheelMods(event);
1107             final boolean isShift = (swingModifiers & InputEvent.SHIFT_DOWN_MASK) != 0;
1108 
1109             // Vertical scroll.
1110             if (!isShift && event.getDeltaY() != 0.0) {
1111                 sendMouseWheelEvent(frame, event.getX(), event.getY(),
1112                         swingModifiers, event.getDeltaY() / event.getMultiplierY());
1113             }
1114             // Horizontal scroll or shirt+vertical scroll.
1115             final double delta = isShift && event.getDeltaY() != 0.0
1116                                   ? event.getDeltaY() / event.getMultiplierY()
1117                                   : event.getDeltaX() / event.getMultiplierX();
1118             if (delta != 0.0) {
1119                 swingModifiers |= InputEvent.SHIFT_DOWN_MASK;
1120                 sendMouseWheelEvent(frame, event.getX(), event.getY(),
1121                         swingModifiers, delta);
1122             }
1123         }
1124 
1125         private void sendMouseWheelEvent(Component source, double fxX, double fxY, int swingModifiers, double delta) {
1126             int wheelRotation = (int) delta;
1127             int signum = (int) Math.signum(delta);
1128             if (signum * delta < 1) {
1129                 wheelRotation = signum;
1130             }
1131             int x = (int) Math.round(fxX);
1132             int y = (int) Math.round(fxY);
1133             MouseWheelEvent mouseWheelEvent =
1134                     new MouseWheelEvent(source, java.awt.event.MouseEvent.MOUSE_WHEEL,
1135                             System.currentTimeMillis(), swingModifiers, x, y, 0, 0,
1136                             0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1 , -wheelRotation);
1137             AccessController.doPrivileged(new PostEventAction(mouseWheelEvent));
1138         }
1139     }
1140 
1141     private class SwingKeyEventHandler implements EventHandler<KeyEvent> {
1142         @Override
1143         public void handle(KeyEvent event) {
1144             JLightweightFrame frame = lwFrame;
1145             if (frame == null) {
1146                 return;
1147             }
1148             if (event.getCharacter().isEmpty()) {
1149                 // TODO: should we post an "empty" character?
1150                 return;
1151             }
1152             // Don't let Arrows, Tab, Shift+Tab traverse focus out.
1153             if (event.getCode() == KeyCode.LEFT  ||
1154                 event.getCode() == KeyCode.RIGHT ||
1155                 event.getCode() == KeyCode.UP ||
1156                 event.getCode() == KeyCode.DOWN ||
1157                 event.getCode() == KeyCode.TAB)
1158             {
1159                 event.consume();
1160             }
1161 
1162             int swingID = SwingEvents.fxKeyEventTypeToKeyID(event);
1163             if (swingID < 0) {
1164                 return;
1165             }
1166             int swingModifiers = SwingEvents.fxKeyModsToKeyMods(event);
1167             int swingKeyCode = event.getCode().getCode();
1168             char swingChar = event.getCharacter().charAt(0);
1169 
1170             // A workaround. Some swing L&F's process mnemonics on KEY_PRESSED,
1171             // for which swing provides a keychar. Extracting it from the text.
1172             if (event.getEventType() == javafx.scene.input.KeyEvent.KEY_PRESSED) {
1173                 String text = event.getText();
1174                 if (text.length() == 1) {
1175                     swingChar = text.charAt(0);
1176                 }
1177             }
1178             long swingWhen = System.currentTimeMillis();
1179             java.awt.event.KeyEvent keyEvent = new java.awt.event.KeyEvent(
1180                     frame, swingID, swingWhen, swingModifiers,
1181                     swingKeyCode, swingChar);
1182             AccessController.doPrivileged(new PostEventAction(keyEvent));
1183         }
1184     }
1185 }


  36 import javafx.scene.input.KeyCode;
  37 import javafx.scene.input.KeyEvent;
  38 import javafx.scene.input.MouseButton;
  39 import javafx.scene.input.MouseEvent;
  40 import javafx.scene.input.ScrollEvent;
  41 import javafx.stage.Window;
  42 import javax.swing.JComponent;
  43 import javax.swing.Timer;
  44 import java.awt.AWTEvent;
  45 import java.awt.Component;
  46 import java.awt.Cursor;
  47 import java.awt.EventQueue;
  48 import java.awt.Toolkit;
  49 import java.lang.ref.WeakReference;
  50 import java.awt.dnd.DragGestureEvent;
  51 import java.awt.dnd.DragGestureListener;
  52 import java.awt.dnd.DragGestureRecognizer;
  53 import java.awt.dnd.DragSource;
  54 import java.awt.dnd.DropTarget;
  55 import java.awt.dnd.InvalidDnDOperationException;

  56 import java.awt.event.InputEvent;
  57 import java.awt.event.MouseWheelEvent;
  58 import java.awt.event.WindowEvent;
  59 import java.awt.event.WindowFocusListener;
  60 import java.lang.reflect.Method;
  61 import java.nio.IntBuffer;
  62 import java.security.AccessController;
  63 import java.security.PrivilegedAction;
  64 import java.util.ArrayList;
  65 import java.util.HashSet;
  66 import java.util.List;
  67 import java.util.Set;
  68 import java.util.concurrent.locks.ReentrantLock;
  69 import com.sun.javafx.embed.swing.Disposer;
  70 import com.sun.javafx.embed.swing.DisposerRecord;
  71 import com.sun.javafx.geom.BaseBounds;
  72 import com.sun.javafx.geom.transform.BaseTransform;
  73 import com.sun.javafx.scene.DirtyBits;
  74 import com.sun.javafx.sg.prism.NGExternalNode;
  75 import com.sun.javafx.sg.prism.NGNode;
  76 import com.sun.javafx.stage.FocusUngrabEvent;
  77 import com.sun.javafx.stage.WindowHelper;
  78 import com.sun.javafx.tk.TKStage;
  79 import com.sun.javafx.PlatformUtil;
  80 import com.sun.javafx.embed.swing.SwingNodeHelper;
  81 import com.sun.javafx.scene.NodeHelper;
  82 
  83 import com.sun.javafx.util.Utils;
  84 
  85 import jdk.swing.interop.LightweightFrameWrapper;
  86 import jdk.swing.interop.LightweightContentWrapper;
  87 import jdk.swing.interop.DragSourceContextWrapper;
  88 
  89 import static javafx.stage.WindowEvent.WINDOW_HIDDEN;
  90 
  91 /**
  92  * This class is used to embed a Swing content into a JavaFX application.
  93  * The content to be displayed is specified with the {@link #setContent} method
  94  * that accepts an instance of Swing {@code JComponent}. The hierarchy of components
  95  * contained in the {@code JComponent} instance should not contain any heavyweight
  96  * components, otherwise {@code SwingNode} may fail to paint it. The content gets
  97  * repainted automatically. All the input and focus events are forwarded to the
  98  * {@code JComponent} instance transparently to the developer.
  99  * <p>
 100  * Here is a typical pattern which demonstrates how {@code SwingNode} can be used:
 101  * <pre>
 102  *     public class SwingFx extends Application {
 103  *
 104  *         @Override
 105  *         public void start(Stage stage) {
 106  *             final SwingNode swingNode = new SwingNode();
 107  *             createAndSetSwingContent(swingNode);


 161             }
 162 
 163             @Override
 164             public boolean doComputeContains(Node node, double localX, double localY) {
 165                 return ((SwingNode) node).doComputeContains(localX, localY);
 166             }
 167         });
 168     }
 169 
 170     private double fxWidth;
 171     private double fxHeight;
 172 
 173     private int swingPrefWidth;
 174     private int swingPrefHeight;
 175     private int swingMaxWidth;
 176     private int swingMaxHeight;
 177     private int swingMinWidth;
 178     private int swingMinHeight;
 179 
 180     private volatile JComponent content;
 181     private volatile LightweightFrameWrapper lwFrame;
 182     final LightweightFrameWrapper getLightweightFrame() { return lwFrame; }
 183 
 184     private NGExternalNode peer;
 185 
 186     private final ReentrantLock paintLock = new ReentrantLock();
 187 
 188     private boolean skipBackwardUnrgabNotification;
 189     private boolean grabbed; // lwframe initiated grab
 190     private Timer deactivate; // lwFrame deactivate delay for Linux
 191 
 192     {
 193         // To initialize the class helper at the begining each constructor of this class
 194         SwingNodeHelper.initHelper(this);
 195     }
 196 
 197     /**
 198      * Constructs a new instance of {@code SwingNode}.
 199      */
 200     public SwingNode() {
 201         setFocusTraversable(true);
 202         setEventHandler(MouseEvent.ANY, new SwingMouseEventHandler());
 203         setEventHandler(KeyEvent.ANY, new SwingKeyEventHandler());
 204         setEventHandler(ScrollEvent.SCROLL, new SwingScrollEventHandler());
 205 
 206         focusedProperty().addListener((observable, oldValue, newValue) -> {
 207              activateLwFrame(newValue);
 208         });
 209 
 210         //Workaround for RT-34170
 211         javafx.scene.text.Font.getFamilies();
 212     }
 213 
 214 
 215     private EventHandler windowHiddenHandler = (Event event) -> {
 216         if (lwFrame != null &&  event.getTarget() instanceof Window) {
 217             final Window w = (Window) event.getTarget();
 218             TKStage tk = WindowHelper.getPeer(w);
 219             if (tk != null) {
 220                 if (isThreadMerged) {
 221                     overrideNativeWindowHandle(lwFrameWrapperClass, 0L, null);
 222                 } else {
 223                     // Postpone actual window closing to ensure that
 224                     // a native window handler is valid on a Swing side
 225                     tk.postponeClose();
 226                     SwingFXUtils.runOnEDT(() -> {
 227                         overrideNativeWindowHandle(lwFrameWrapperClass, 0L, 
 228                                 (Runnable) () -> SwingFXUtils.runOnFxThread(
 229                                         () -> tk.closePostponed()));
 230                     });
 231                 }
 232             }
 233         }
 234 
 235     };
 236 
 237     private Window hWindow = null;
 238     private void notifyNativeHandle(Window window) {
 239         if (hWindow != window) {
 240             if (hWindow != null) {
 241                 hWindow.removeEventHandler(WINDOW_HIDDEN, windowHiddenHandler);
 242             }
 243             if (window != null) {
 244                 window.addEventHandler(WINDOW_HIDDEN, windowHiddenHandler);
 245             }
 246             hWindow = window;
 247         }
 248 
 249         if (lwFrame != null) {
 250             long rawHandle = 0L;
 251             if (window != null) {
 252                 TKStage tkStage = WindowHelper.getPeer(window);
 253                 if (tkStage != null) {
 254                     rawHandle = tkStage.getRawHandle();
 255                 }
 256             }
 257             overrideNativeWindowHandle(lwFrameWrapperClass, rawHandle, null);            
 258         }
 259     }
 260 
 261     /**
 262      * Attaches a {@code JComponent} instance to display in this {@code SwingNode}.
 263      * <p>
 264      * The method can be called either on the JavaFX Application thread or the Event Dispatch thread.
 265      * Note however, that access to a Swing component must occur from the Event Dispatch thread
 266      * according to the Swing threading restrictions.
 267      *
 268      * @param content a Swing component to display in this {@code SwingNode}
 269      *
 270      * @see java.awt.EventQueue#isDispatchThread()
 271      * @see javafx.application.Platform#isFxApplicationThread()
 272      */
 273     public void setContent(final JComponent content) {
 274         this.content = content;
 275 
 276         SwingFXUtils.runOnEDT(() -> setContentImpl(content));
 277     }


 316         }
 317 
 318         public boolean isIntegerApi() {
 319             return isIntegerAPI;
 320         }
 321 
 322         public Object invoke(T object, Object... args) {
 323             if (method != null) {
 324                 try {
 325                     return method.invoke(object, args);
 326                 } catch (Throwable ex) {
 327                     throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex);
 328                 }
 329             } else {
 330                 return null;
 331             }
 332         }
 333     }
 334 
 335     /**
 336      * Calls LightweightFrameWrapper.notifyDisplayChanged.
 337      * Must be called on EDT only.
 338      */
 339     private static OptionalMethod<LightweightFrameWrapper> jlfNotifyDisplayChanged;
 340     private static Class lwFrameWrapperClass = null;
 341     private static native void overrideNativeWindowHandle(Class lwFrameWrapperClass, long handle, 
 342                                                      Runnable closeWindow);
 343 
 344     static {
 345         jlfNotifyDisplayChanged = new OptionalMethod<>(LightweightFrameWrapper.class,
 346                 "notifyDisplayChanged", Double.TYPE, Double.TYPE);
 347         if (!jlfNotifyDisplayChanged.isSupported()) {
 348             jlfNotifyDisplayChanged = new OptionalMethod<>(
 349                   LightweightFrameWrapper.class,"notifyDisplayChanged", Integer.TYPE);
 350         }
 351 
 352         try {
 353             lwFrameWrapperClass = Class.forName("jdk.swing.interop.LightweightFrameWrapper");
 354         } catch (Throwable t) {}
 355 
 356         Utils.loadNativeSwingLibrary();
 357     }
 358 
 359     /*
 360      * Called on EDT
 361      */
 362     private void setContentImpl(JComponent content) {
 363         if (lwFrame != null) {
 364             lwFrame.dispose();
 365             lwFrame = null;
 366         }
 367         if (content != null) {
 368             lwFrame = new LightweightFrameWrapper();
 369 
 370             SwingNodeWindowFocusListener snfListener =
 371                                  new SwingNodeWindowFocusListener(this);
 372             lwFrame.addWindowFocusListener(snfListener);
 373 
 374             if (getScene() != null) {
 375                 Window window = getScene().getWindow();
 376                 if (window != null) {
 377                     if (jlfNotifyDisplayChanged.isIntegerApi()) {
 378                         jlfNotifyDisplayChanged.invoke(lwFrame,
 379                                 (int) Math.round(window.getRenderScaleX()));
 380                     } else {
 381                         jlfNotifyDisplayChanged.invoke(lwFrame,
 382                                 window.getRenderScaleX(),
 383                                 window.getRenderScaleY());
 384                     }
 385                 }
 386             }
 387             lwFrame.setContent(new SwingNodeContent(content, this));
 388             lwFrame.setVisible(true);


 548      * @return the minimum height that the node should be resized to during layout
 549      */
 550     @Override public double minHeight(double width) {
 551         return swingMinHeight;
 552     }
 553 
 554     /*
 555      * Note: This method MUST only be called via its accessor method.
 556      */
 557     private boolean doComputeContains(double localX, double localY) {
 558         return true;
 559     }
 560 
 561     private final InvalidationListener locationListener = observable -> {
 562         locateLwFrame();
 563     };
 564 
 565     private final EventHandler<FocusUngrabEvent> ungrabHandler = event -> {
 566         if (!skipBackwardUnrgabNotification) {
 567             if (lwFrame != null) {
 568                 AccessController.doPrivileged(new PostEventAction(
 569                     lwFrame.createUngrabEvent(lwFrame)));
 570             }
 571         }
 572     };
 573 
 574     private final ChangeListener<Boolean> windowVisibleListener = (observable, oldValue, newValue) -> {
 575         if (!newValue) {
 576             disposeLwFrame();
 577         } else {
 578             setContent(content);
 579         }
 580     };
 581 
 582     private final ChangeListener<Window> sceneWindowListener = (observable, oldValue, newValue) -> {
 583         if (oldValue != null) {
 584             removeWindowListeners(oldValue);
 585         }
 586 
 587         notifyNativeHandle(newValue);
 588 
 589         if (newValue != null) {


 663             setLwFrameVisible(newValue);
 664         });
 665 
 666         return peer;
 667     }
 668 
 669     /*
 670      * Note: This method MUST only be called via its accessor method.
 671      */
 672     private void doUpdatePeer() {
 673         if (NodeHelper.isDirty(this, DirtyBits.NODE_VISIBLE)
 674                 || NodeHelper.isDirty(this, DirtyBits.NODE_BOUNDS)) {
 675             locateLwFrame(); // initialize location
 676         }
 677         if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
 678             peer.markContentDirty();
 679         }
 680     }
 681 
 682     /**
 683      * Calls LightweightFrameWrapper.setHostBounds.
 684      * Must be called on EDT only.
 685      */
 686     private static final OptionalMethod<LightweightFrameWrapper> jlfSetHostBounds =
 687         new OptionalMethod<>(LightweightFrameWrapper.class, "setHostBounds",
 688                 Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE);
 689 
 690     private void locateLwFrame() {
 691         if (getScene() == null
 692                 || lwFrame == null
 693                 || getScene().getWindow() == null
 694                 || !getScene().getWindow().isShowing()) {
 695             // Not initialized yet. Skip the update to set the real values later
 696             return;
 697         }
 698         Window w = getScene().getWindow();
 699         double renderScaleX = w.getRenderScaleX();
 700         double renderScaleY = w.getRenderScaleY();
 701         final Point2D loc = localToScene(0, 0);
 702         final int windowX = (int) (w.getX());
 703         final int windowY = (int) (w.getY());
 704         final int windowW = (int) (w.getWidth());
 705         final int windowH = (int) (w.getHeight());
 706         final int frameX = (int) Math.round(w.getX() + getScene().getX() + loc.getX());
 707         final int frameY = (int) Math.round(w.getY() + getScene().getY() + loc.getY());


 787                 if (jlfNotifyDisplayChanged.isIntegerApi()) {
 788                     jlfNotifyDisplayChanged.invoke(lwFrame,
 789                                                        (int)Math.round(scaleX));
 790                 } else {
 791                     jlfNotifyDisplayChanged.invoke(lwFrame, scaleX, scaleY);
 792                 }
 793             }
 794         });
 795     }
 796 
 797     /*
 798      * Note: This method MUST only be called via its accessor method.
 799      */
 800     private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
 801         bounds.deriveWithNewBounds(0, 0, 0, (float)fxWidth, (float)fxHeight, 0);
 802         tx.transform(bounds, bounds);
 803         return bounds;
 804     }
 805 
 806     private static class SwingNodeDisposer implements DisposerRecord {
 807          LightweightFrameWrapper lwFrame;
 808 
 809          SwingNodeDisposer(LightweightFrameWrapper ref) {
 810              this.lwFrame = ref;
 811          }
 812          public void dispose() {
 813              if (lwFrame != null) {
 814                  lwFrame.dispose();
 815                  lwFrame = null;
 816              }
 817          }
 818     }
 819 
 820     private static class SwingNodeWindowFocusListener implements WindowFocusListener {
 821         private WeakReference<SwingNode> swingNodeRef;
 822 
 823         SwingNodeWindowFocusListener(SwingNode swingNode) {
 824             this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
 825         }
 826 
 827         @Override
 828         public void windowGainedFocus(WindowEvent e) {
 829             SwingFXUtils.runOnFxThread(() -> {
 830                 SwingNode swingNode = swingNodeRef.get();
 831                 if (swingNode != null) {
 832                     swingNode.requestFocus();
 833                 }
 834             });
 835         }
 836 
 837         @Override
 838         public void windowLostFocus(WindowEvent e) {
 839             SwingFXUtils.runOnFxThread(() -> {
 840                 SwingNode swingNode = swingNodeRef.get();
 841                 if (swingNode != null) {
 842                     swingNode.ungrabFocus(true);
 843                 }
 844             });
 845         }
 846     }
 847 
 848     private static class SwingNodeContent extends LightweightContentWrapper {
 849         private JComponent comp;
 850         private volatile FXDnD dnd;
 851         private WeakReference<SwingNode> swingNodeRef;
 852 
 853         SwingNodeContent(JComponent comp, SwingNode swingNode) {
 854             this.comp = comp;
 855             this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
 856         }
 857         @Override
 858         public JComponent getComponent() {
 859             return comp;
 860         }
 861         @Override
 862         public void paintLock() {
 863             SwingNode swingNode = swingNodeRef.get();
 864             if (swingNode != null) {
 865                 swingNode.paintLock.lock();
 866             }
 867         }
 868         @Override
 869         public void paintUnlock() {
 870             SwingNode swingNode = swingNodeRef.get();
 871             if (swingNode != null) {
 872                 swingNode.paintLock.unlock();
 873             }
 874         }
 875 
 876         @Override


 877         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride) {
 878             imageBufferReset(data, x, y, width, height, linestride, 1);
 879         }
 880         //@Override
 881         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, int scale) {
 882             SwingNode swingNode = swingNodeRef.get();
 883             if (swingNode != null) {
 884                 swingNode.setImageBuffer(data, x, y, width, height, linestride, scale, scale);
 885             }
 886         }
 887         @Override
 888         public void imageBufferReset(int[] data, int x, int y, int width, int height, int linestride, double scaleX, double scaleY) {
 889             SwingNode swingNode = swingNodeRef.get();
 890             if (swingNode != null) {
 891                 swingNode.setImageBuffer(data, x, y, width, height, linestride, scaleX, scaleY);
 892             }
 893         }
 894         @Override
 895         public void imageReshaped(int x, int y, int width, int height) {
 896             SwingNode swingNode = swingNodeRef.get();
 897             if (swingNode != null) {
 898                 swingNode.setImageBounds(x, y, width, height);
 899             }
 900         }
 901         @Override
 902         public void imageUpdated(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
 903             SwingNode swingNode = swingNodeRef.get();
 904             if (swingNode != null) {
 905                 swingNode.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 906             }
 907         }


 972             SwingFXUtils.runOnFxThread(() -> {
 973                 SwingNode swingNode = swingNodeRef.get();
 974                 if (swingNode != null) {
 975                     swingNode.setCursor(SwingCursors.embedCursorToCursor(cursor));
 976                 }
 977             });
 978         }
 979 
 980         private void initDnD() {
 981             // This is a part of AWT API, so the method may be invoked on any thread
 982             synchronized (SwingNodeContent.this) {
 983                 if (this.dnd == null) {
 984                     SwingNode swingNode = swingNodeRef.get();
 985                     if (swingNode != null) {
 986                         this.dnd = new FXDnD(swingNode);
 987                     }
 988                 }
 989             }
 990         }
 991 
 992         @Override
 993         public synchronized <T extends DragGestureRecognizer> T createDragGestureRecognizer(
 994                 Class<T> abstractRecognizerClass,
 995                 DragSource ds, Component c, int srcActions,
 996                 DragGestureListener dgl)
 997         {
 998             initDnD();
 999             return dnd.createDragGestureRecognizer(abstractRecognizerClass, ds, c, srcActions, dgl);
1000         }
1001 
1002         @Override
1003         public DragSourceContextWrapper createDragSourceContext(DragGestureEvent dge) throws InvalidDnDOperationException
1004         {
1005             initDnD();
1006             return dnd.createDragSourceContext(dge);
1007         }
1008 
1009         @Override
1010         public void addDropTarget(DropTarget dt) {
1011             initDnD();
1012             dnd.addDropTarget(dt);
1013         }
1014 
1015         @Override
1016         public void removeDropTarget(DropTarget dt) {
1017             initDnD();
1018             dnd.removeDropTarget(dt);
1019         }
1020     }
1021 
1022     private void ungrabFocus(boolean postUngrabEvent) {
1023         // On X11 grab is limited to a single XDisplay connection,
1024         // so we can't delegate it to another GUI toolkit.
1025         if (PlatformUtil.isLinux()) return;
1026 
1027         if (grabbed &&
1028             getScene() != null &&
1029             getScene().getWindow() != null &&
1030             WindowHelper.getPeer(getScene().getWindow()) != null)
1031         {
1032             skipBackwardUnrgabNotification = !postUngrabEvent;
1033             WindowHelper.getPeer(getScene().getWindow()).ungrabFocus();
1034             skipBackwardUnrgabNotification = false;
1035             grabbed = false;


1037     }
1038 
1039     private class PostEventAction implements PrivilegedAction<Void> {
1040         private AWTEvent event;
1041         PostEventAction(AWTEvent event) {
1042             this.event = event;
1043         }
1044         @Override
1045         public Void run() {
1046             EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
1047             eq.postEvent(event);
1048             return null;
1049         }
1050     }
1051 
1052     private class SwingMouseEventHandler implements EventHandler<MouseEvent> {
1053         private final Set<MouseButton> mouseClickedAllowed = new HashSet<>();
1054 
1055         @Override
1056         public void handle(MouseEvent event) {
1057             LightweightFrameWrapper frame = lwFrame;
1058             if (frame == null) {
1059                 return;
1060             }
1061             int swingID = SwingEvents.fxMouseEventTypeToMouseID(event);
1062             if (swingID < 0) {
1063                 return;
1064             }
1065 
1066             // Prevent ancestors of the SwingNode from stealing the focus
1067             event.consume();
1068 
1069             final EventType<?> type = event.getEventType();
1070             if (type == MouseEvent.MOUSE_PRESSED) {
1071                 mouseClickedAllowed.add(event.getButton());
1072             } else if (type == MouseEvent.MOUSE_RELEASED) {
1073                 // RELEASED comes before CLICKED, so we don't remove the button from the set
1074                 //mouseClickedAllowed.remove(event.getButton());
1075             } else if (type == MouseEvent.MOUSE_DRAGGED) {
1076                 // This is what AWT/Swing do
1077                 mouseClickedAllowed.clear();
1078             } else if (type == MouseEvent.MOUSE_CLICKED) {
1079                 if (event.getClickCount() == 1 && !mouseClickedAllowed.contains(event.getButton())) {
1080                     // RT-34610: In FX, CLICKED events are generated even after dragging the mouse pointer
1081                     // Note that this is only relevant for single clicks. Double clicks use a smudge factor.
1082                     return;
1083                 }
1084                 mouseClickedAllowed.remove(event.getButton());
1085             }
1086             int swingModifiers = SwingEvents.fxMouseModsToMouseMods(event);
1087             boolean swingPopupTrigger = event.isPopupTrigger();
1088             int swingButton = SwingEvents.fxMouseButtonToMouseButton(event);
1089             long swingWhen = System.currentTimeMillis();
1090             int relX = (int) Math.round(event.getX());
1091             int relY = (int) Math.round(event.getY());
1092             int absX = (int) Math.round(event.getScreenX());
1093             int absY = (int) Math.round(event.getScreenY());
1094             java.awt.event.MouseEvent mouseEvent =
1095                     frame.createMouseEvent(
1096                         frame, swingID, swingWhen, swingModifiers,
1097                         relX, relY, absX, absY,
1098                         event.getClickCount(), swingPopupTrigger, swingButton);
1099             AccessController.doPrivileged(new PostEventAction(mouseEvent));
1100         }
1101     }
1102 
1103     private class SwingScrollEventHandler implements EventHandler<ScrollEvent> {
1104         @Override
1105         public void handle(ScrollEvent event) {
1106             LightweightFrameWrapper frame = lwFrame;
1107             if (frame == null) {
1108                 return;
1109             }
1110 
1111             int swingModifiers = SwingEvents.fxScrollModsToMouseWheelMods(event);
1112             final boolean isShift = (swingModifiers & InputEvent.SHIFT_DOWN_MASK) != 0;
1113 
1114             // Vertical scroll.
1115             if (!isShift && event.getDeltaY() != 0.0) {
1116                 sendMouseWheelEvent(frame, event.getX(), event.getY(),
1117                         swingModifiers, event.getDeltaY() / event.getMultiplierY());
1118             }
1119             // Horizontal scroll or shirt+vertical scroll.
1120             final double delta = isShift && event.getDeltaY() != 0.0
1121                                   ? event.getDeltaY() / event.getMultiplierY()
1122                                   : event.getDeltaX() / event.getMultiplierX();
1123             if (delta != 0.0) {
1124                 swingModifiers |= InputEvent.SHIFT_DOWN_MASK;
1125                 sendMouseWheelEvent(frame, event.getX(), event.getY(),
1126                         swingModifiers, delta);
1127             }
1128         }
1129 
1130         private void sendMouseWheelEvent(LightweightFrameWrapper source, double fxX, double fxY, int swingModifiers, double delta) {
1131             int wheelRotation = (int) delta;
1132             int signum = (int) Math.signum(delta);
1133             if (signum * delta < 1) {
1134                 wheelRotation = signum;
1135             }
1136             int x = (int) Math.round(fxX);
1137             int y = (int) Math.round(fxY);
1138             MouseWheelEvent mouseWheelEvent =
1139                 lwFrame.createMouseWheelEvent(source, swingModifiers, x, y, -wheelRotation);


1140             AccessController.doPrivileged(new PostEventAction(mouseWheelEvent));
1141         }
1142     }
1143 
1144     private class SwingKeyEventHandler implements EventHandler<KeyEvent> {
1145         @Override
1146         public void handle(KeyEvent event) {
1147             LightweightFrameWrapper frame = lwFrame;
1148             if (frame == null) {
1149                 return;
1150             }
1151             if (event.getCharacter().isEmpty()) {
1152                 // TODO: should we post an "empty" character?
1153                 return;
1154             }
1155             // Don't let Arrows, Tab, Shift+Tab traverse focus out.
1156             if (event.getCode() == KeyCode.LEFT  ||
1157                 event.getCode() == KeyCode.RIGHT ||
1158                 event.getCode() == KeyCode.UP ||
1159                 event.getCode() == KeyCode.DOWN ||
1160                 event.getCode() == KeyCode.TAB)
1161             {
1162                 event.consume();
1163             }
1164 
1165             int swingID = SwingEvents.fxKeyEventTypeToKeyID(event);
1166             if (swingID < 0) {
1167                 return;
1168             }
1169             int swingModifiers = SwingEvents.fxKeyModsToKeyMods(event);
1170             int swingKeyCode = event.getCode().getCode();
1171             char swingChar = event.getCharacter().charAt(0);
1172 
1173             // A workaround. Some swing L&F's process mnemonics on KEY_PRESSED,
1174             // for which swing provides a keychar. Extracting it from the text.
1175             if (event.getEventType() == javafx.scene.input.KeyEvent.KEY_PRESSED) {
1176                 String text = event.getText();
1177                 if (text.length() == 1) {
1178                     swingChar = text.charAt(0);
1179                 }
1180             }
1181             long swingWhen = System.currentTimeMillis();
1182             java.awt.event.KeyEvent keyEvent = frame.createKeyEvent(frame,
1183                             swingID, swingWhen, swingModifiers, swingKeyCode, 
1184                             swingChar);
1185             AccessController.doPrivileged(new PostEventAction(keyEvent));
1186         }
1187     }
1188 }
< prev index next >