< 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);
 106  *
 107  *             StackPane pane = new StackPane();
 108  *             pane.getChildren().add(swingNode);


 112  *         }
 113  *
 114  *         private void createAndSetSwingContent(final SwingNode swingNode) {
 115  *             SwingUtilities.invokeLater(new Runnable() {
 116  *                 @Override
 117  *                 public void run() {
 118  *                     swingNode.setContent(new JButton("Click me!"));
 119  *                 }
 120  *             });
 121  *         }
 122  *
 123  *         public static void main(String[] args) {
 124  *             launch(args);
 125  *         }
 126  *     }
 127  * </pre>
 128  * @since JavaFX 8.0
 129  */
 130 public class SwingNode extends Node {
 131     private static boolean isThreadMerged;

 132 
 133     static {








 134         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 135             public Object run() {
 136                 isThreadMerged = Boolean.valueOf(
 137                         System.getProperty("javafx.embed.singleThread"));
 138                 return null;
 139             }
 140         });
 141 

 142          // This is used by classes in different packages to get access to
 143          // private and package private methods.
 144         SwingNodeHelper.setSwingNodeAccessor(new SwingNodeHelper.SwingNodeAccessor() {
 145             @Override
 146             public NGNode doCreatePeer(Node node) {
 147                 return ((SwingNode) node).doCreatePeer();
 148             }
 149 
 150             @Override
 151             public void doUpdatePeer(Node node) {
 152                 ((SwingNode) node).doUpdatePeer();
 153             }
 154 
 155             @Override
 156             public BaseBounds doComputeGeomBounds(Node node,
 157                     BaseBounds bounds, BaseTransform tx) {
 158                 return ((SwingNode) node).doComputeGeomBounds(bounds, tx);
 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     }
 276 
 277     /**
 278      * Returns the {@code JComponent} instance attached to this {@code SwingNode}.
 279      * <p>
 280      * The method can be called either on the JavaFX Application thread or the Event Dispatch thread.
 281      * Note however, that access to a Swing component must occur from the Event Dispatch thread
 282      * according to the Swing threading restrictions.
 283      *
 284      * @see java.awt.EventQueue#isDispatchThread()
 285      * @see javafx.application.Platform#isFxApplicationThread()
 286      *
 287      * @return the Swing component attached to this {@code SwingNode}
 288      */
 289     public JComponent getContent() {
 290         return content;
 291     }
 292 
 293     private static final class OptionalMethod<T> {
 294         private final Method method;


 313             return method != null;
 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);
 383 
 384             SwingNodeDisposer disposeRec = new SwingNodeDisposer(lwFrame);
 385             Disposer.addRecord(this, disposeRec);
 386 
 387             if (getScene() != null) {
 388                 notifyNativeHandle(getScene().getWindow());
 389             }
 390 
 391             SwingFXUtils.runOnFxThread(() -> {
 392                 locateLwFrame(); // initialize location
 393 
 394                 if (focusedProperty().get()) {
 395                     activateLwFrame(true);
 396                 }
 397             });
 398         }
 399     }
 400 
 401     private List<Runnable> peerRequests = new ArrayList<>();
 402 
 403     /*
 404      * Called on EDT
 405      */
 406     void setImageBuffer(final int[] data,
 407                         final int x, final int y,
 408                         final int w, final int h,
 409                         final int linestride,
 410                         final double scaleX,
 411                         final double scaleY)
 412     {
 413         Runnable r = () -> peer.setImageBuffer(IntBuffer.wrap(data), x, y, w, h,
 414                                 w, h, linestride, scaleX, scaleY);
 415         SwingFXUtils.runOnFxThread(() -> {
 416             if (peer != null) {
 417                 r.run();
 418             } else {
 419                 peerRequests.clear();
 420                 peerRequests.add(r);
 421             }
 422         });
 423     }
 424 
 425     /*
 426      * Called on EDT
 427      */
 428     void setImageBounds(final int x, final int y, final int w, final int h) {
 429         Runnable r = () -> peer.setImageBounds(x, y, w, h, w, h);
 430         SwingFXUtils.runOnFxThread(() -> {
 431             if (peer != null) {
 432                 r.run();
 433             } else {
 434                 peerRequests.add(r);
 435             }
 436         });
 437     }
 438 
 439     /*
 440      * Called on EDT
 441      */
 442     void repaintDirtyRegion(final int dirtyX, final int dirtyY, final int dirtyWidth, final int dirtyHeight) {
 443         Runnable r = () -> {
 444             peer.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 445             NodeHelper.markDirty(this, DirtyBits.NODE_CONTENTS);
 446         };
 447         SwingFXUtils.runOnFxThread(() -> {
 448             if (peer != null) {
 449                 r.run();
 450             } else {
 451                 peerRequests.add(r);
 452             }
 453         });
 454     }
 455 
 456     @Override public boolean isResizable() {
 457         return true;
 458     }
 459 
 460     /**
 461      * Invoked by the {@code SwingNode}'s parent during layout to set the {@code SwingNode}'s
 462      * width and height. <b>Applications should not invoke this method directly</b>.
 463      * If an application needs to directly set the size of the {@code SwingNode}, it should
 464      * set the Swing component's minimum/preferred/maximum size constraints which will
 465      * be propagated correspondingly to the {@code SwingNode} and it's parent will honor those
 466      * settings during layout.
 467      *
 468      * @param width the target layout bounds width
 469      * @param height the target layout bounds height
 470      */
 471     @Override public void resize(final double width, final double height) {
 472         super.resize(width, height);
 473         if (width != this.fxWidth || height != this.fxHeight) {
 474             this.fxWidth = width;
 475             this.fxHeight = height;
 476             NodeHelper.geomChanged(this);
 477             NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY);
 478             SwingFXUtils.runOnEDT(() -> {
 479                 if (lwFrame != null) {
 480                     locateLwFrame();
 481                 }
 482             });
 483         }
 484     }
 485 
 486     /**
 487      * Returns the {@code SwingNode}'s preferred width for use in layout calculations.
 488      * This value corresponds to the preferred width of the Swing component.
 489      *
 490      * @return the preferred width that the node should be resized to during layout
 491      */
 492     @Override
 493     public double prefWidth(double height) {
 494         return swingPrefWidth;
 495     }
 496 
 497     /**
 498      * Returns the {@code SwingNode}'s preferred height for use in layout calculations.


 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) {
 583             addWindowListeners(newValue);
 584         }
 585     };
 586 
 587     private void removeSceneListeners(Scene scene) {
 588         Window window = scene.getWindow();
 589         if (window != null) {


 592         scene.windowProperty().removeListener(sceneWindowListener);
 593     }
 594 
 595     private void addSceneListeners(final Scene scene) {
 596         Window window = scene.getWindow();
 597         if (window != null) {
 598             addWindowListeners(window);
 599             notifyNativeHandle(window);
 600         }
 601         scene.windowProperty().addListener(sceneWindowListener);
 602     }
 603 
 604     private void addWindowListeners(final Window window) {
 605         window.xProperty().addListener(locationListener);
 606         window.yProperty().addListener(locationListener);
 607         window.widthProperty().addListener(locationListener);
 608         window.heightProperty().addListener(locationListener);
 609         window.renderScaleXProperty().addListener(locationListener);
 610         window.addEventHandler(FocusUngrabEvent.FOCUS_UNGRAB, ungrabHandler);
 611         window.showingProperty().addListener(windowVisibleListener);
 612         setLwFrameScale(window.getRenderScaleX(), window.getRenderScaleY());
 613     }
 614 
 615     private void removeWindowListeners(final Window window) {
 616         window.xProperty().removeListener(locationListener);
 617         window.yProperty().removeListener(locationListener);
 618         window.widthProperty().removeListener(locationListener);
 619         window.heightProperty().removeListener(locationListener);
 620         window.renderScaleXProperty().removeListener(locationListener);
 621         window.removeEventHandler(FocusUngrabEvent.FOCUS_UNGRAB, ungrabHandler);
 622         window.showingProperty().removeListener(windowVisibleListener);
 623     }
 624 
 625     /*
 626      * Note: This method MUST only be called via its accessor method.
 627      */
 628     private NGNode doCreatePeer() {
 629         peer = new NGExternalNode();
 630         peer.setLock(paintLock);
 631         for (Runnable request : peerRequests) {
 632             request.run();
 633         }
 634         peerRequests = null;
 635 
 636         if (getScene() != null) {
 637             addSceneListeners(getScene());
 638         }
 639 
 640         sceneProperty().addListener((observable, oldValue, newValue) -> {
 641             if (oldValue != null) {
 642                 // Removed from scene
 643                 removeSceneListeners(oldValue);
 644                 disposeLwFrame();
 645             }
 646             if (newValue != null) {
 647                 // Added to another scene
 648                 if (content != null && lwFrame == null) {
 649                     setContent(content);
 650                 }
 651                 addSceneListeners(newValue);
 652             }
 653         });
 654 
 655         NodeHelper.treeVisibleProperty(this).addListener((observable, oldValue, newValue) -> {
 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());
 701         final int frameW = (int) (fxWidth);
 702         final int frameH = (int) (fxHeight);
 703 
 704         SwingFXUtils.runOnEDT(() -> {
 705             if (lwFrame != null) {
 706                 if (jlfNotifyDisplayChanged.isIntegerApi()) {
 707                     jlfNotifyDisplayChanged.invoke(lwFrame,
 708                             (int)Math.round(renderScaleX));
 709                 } else {
 710                     jlfNotifyDisplayChanged.invoke(lwFrame, renderScaleX,
 711                                                                   renderScaleY);
 712                 }
 713                 lwFrame.setBounds(frameX, frameY, frameW, frameH);
 714                 jlfSetHostBounds.invoke(lwFrame, windowX, windowY,
 715                     windowW, windowH);
 716             }
 717         });
 718     }
 719 
 720     private void activateLwFrame(final boolean activate) {
 721         if (lwFrame == null) {
 722             return;
 723         }
 724         if (PlatformUtil.isLinux()) {
 725             // Workaround to block FocusOut/FocusIn notifications from Unity
 726             // focus grabbing upon Alt press
 727             if (deactivate == null || !deactivate.isRunning()) {
 728                 if (!activate) {
 729                     deactivate = new Timer(50, (e) -> {
 730                         {
 731                             if (lwFrame != null) {
 732                                 lwFrame.emulateActivation(false);
 733                             }
 734                         }
 735                     });
 736                     deactivate.start();
 737                     return;
 738                 }
 739             } else {
 740                 deactivate.stop();
 741             }
 742         }
 743 
 744         SwingFXUtils.runOnEDT(() -> {
 745             if (lwFrame != null) {
 746                 lwFrame.emulateActivation(activate);
 747             }
 748         });
 749     }
 750 
 751     private void disposeLwFrame() {
 752         if (lwFrame == null) {
 753             return;
 754         }
 755         SwingFXUtils.runOnEDT(() -> {
 756             if (lwFrame != null) {
 757                 lwFrame.dispose();
 758                 lwFrame = null;
 759             }
 760         });
 761     }
 762 
 763     private void setLwFrameVisible(final boolean visible) {
 764         if (lwFrame == null) {
 765             return;
 766         }
 767         SwingFXUtils.runOnEDT(() -> {
 768             if (lwFrame != null) {
 769                 lwFrame.setVisible(visible);
 770             }
 771         });
 772     }
 773 
 774     private void setLwFrameScale(final double scaleX, final double scaleY) {
 775         if (lwFrame == null) {
 776             return;
 777         }
 778         SwingFXUtils.runOnEDT(() -> {
 779             if (lwFrame != null) {
 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         }
 903         @Override
 904         public void focusGrabbed() {
 905             SwingFXUtils.runOnFxThread(() -> {
 906                 // On X11 grab is limited to a single XDisplay connection,
 907                 // so we can't delegate it to another GUI toolkit.
 908                 if (PlatformUtil.isLinux()) return;
 909 
 910                 SwingNode swingNode = swingNodeRef.get();
 911                 if (swingNode != null) {
 912                     Scene scene = swingNode.getScene();
 913                     if (scene != null &&
 914                             scene.getWindow() != null &&
 915                             WindowHelper.getPeer(scene.getWindow()) != null) {
 916                         WindowHelper.getPeer(scene.getWindow()).grabFocus();
 917                         swingNode.grabbed = true;
 918                     }
 919                 }
 920             });
 921         }
 922         @Override
 923         public void focusUngrabbed() {
 924             SwingFXUtils.runOnFxThread(() -> {
 925                 SwingNode swingNode = swingNodeRef.get();
 926                 if (swingNode != null) {
 927                     swingNode.ungrabFocus(false);
 928                 }
 929             });
 930         }
 931         @Override
 932         public void preferredSizeChanged(final int width, final int height) {
 933             SwingFXUtils.runOnFxThread(() -> {
 934                 SwingNode swingNode = swingNodeRef.get();
 935                 if (swingNode != null) {
 936                     swingNode.swingPrefWidth = width;
 937                     swingNode.swingPrefHeight = height;
 938                     NodeHelper.notifyLayoutBoundsChanged(swingNode);
 939                 }
 940             });
 941         }
 942         @Override
 943         public void maximumSizeChanged(final int width, final int height) {
 944             SwingFXUtils.runOnFxThread(() -> {
 945                 SwingNode swingNode = swingNodeRef.get();
 946                 if (swingNode != null) {
 947                     swingNode.swingMaxWidth = width;
 948                     swingNode.swingMaxHeight = height;
 949                     NodeHelper.notifyLayoutBoundsChanged(swingNode);
 950                 }
 951             });
 952         }
 953         @Override
 954         public void minimumSizeChanged(final int width, final int height) {
 955             SwingFXUtils.runOnFxThread(() -> {
 956                 SwingNode swingNode = swingNodeRef.get();
 957                 if (swingNode != null) {
 958                     swingNode.swingMinWidth = width;
 959                     swingNode.swingMinHeight = height;
 960                     NodeHelper.notifyLayoutBoundsChanged(swingNode);
 961                 }
 962             });
 963         }
 964 
 965         //@Override
 966         public void setCursor(Cursor cursor) {
 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;
1031         }
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.embed.swing.SwingEvents;
  84 import com.sun.javafx.embed.swing.SwingCursors;
  85 import static javafx.stage.WindowEvent.WINDOW_HIDDEN;
  86 
  87 import com.sun.javafx.embed.swing.InteropFactory;
  88 import com.sun.javafx.embed.swing.SwingNodeInterop;
  89 
  90 /**
  91  * This class is used to embed a Swing content into a JavaFX application.
  92  * The content to be displayed is specified with the {@link #setContent} method
  93  * that accepts an instance of Swing {@code JComponent}. The hierarchy of components
  94  * contained in the {@code JComponent} instance should not contain any heavyweight
  95  * components, otherwise {@code SwingNode} may fail to paint it. The content gets
  96  * repainted automatically. All the input and focus events are forwarded to the
  97  * {@code JComponent} instance transparently to the developer.
  98  * <p>
  99  * Here is a typical pattern which demonstrates how {@code SwingNode} can be used:
 100  * <pre>
 101  *     public class SwingFx extends Application {
 102  *
 103  *         @Override
 104  *         public void start(Stage stage) {
 105  *             final SwingNode swingNode = new SwingNode();
 106  *             createAndSetSwingContent(swingNode);
 107  *
 108  *             StackPane pane = new StackPane();
 109  *             pane.getChildren().add(swingNode);


 113  *         }
 114  *
 115  *         private void createAndSetSwingContent(final SwingNode swingNode) {
 116  *             SwingUtilities.invokeLater(new Runnable() {
 117  *                 @Override
 118  *                 public void run() {
 119  *                     swingNode.setContent(new JButton("Click me!"));
 120  *                 }
 121  *             });
 122  *         }
 123  *
 124  *         public static void main(String[] args) {
 125  *             launch(args);
 126  *         }
 127  *     }
 128  * </pre>
 129  * @since JavaFX 8.0
 130  */
 131 public class SwingNode extends Node {
 132     private static boolean isThreadMerged;
 133     private static SwingNodeInterop swiop;
 134 
 135     static {
 136         InteropFactory instance = null;
 137         try {
 138             instance = InteropFactory.getInstance();
 139         } catch (Exception e) {
 140             throw new ExceptionInInitializerError(e);
 141         }
 142         swiop = instance.createSwingNodeImpl();
 143 
 144         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 145             public Object run() {
 146                 isThreadMerged = Boolean.valueOf(
 147                         System.getProperty("javafx.embed.singleThread"));
 148                 return null;
 149             }
 150         });
 151 
 152 
 153          // This is used by classes in different packages to get access to
 154          // private and package private methods.
 155         SwingNodeHelper.setSwingNodeAccessor(new SwingNodeHelper.SwingNodeAccessor() {
 156             @Override
 157             public NGNode doCreatePeer(Node node) {
 158                 return ((SwingNode) node).doCreatePeer();
 159             }
 160 
 161             @Override
 162             public void doUpdatePeer(Node node) {
 163                 ((SwingNode) node).doUpdatePeer();
 164             }
 165 
 166             @Override
 167             public BaseBounds doComputeGeomBounds(Node node,
 168                     BaseBounds bounds, BaseTransform tx) {
 169                 return ((SwingNode) node).doComputeGeomBounds(bounds, tx);
 170             }
 171 
 172             @Override
 173             public boolean doComputeContains(Node node, double localX, double localY) {
 174                 return ((SwingNode) node).doComputeContains(localX, localY);
 175             }
 176 
 177             @Override
 178             public Object getLightweightFrame(SwingNode node) {
 179                 return node.getLightweightFrame();
 180             }
 181 
 182             @Override
 183             public ReentrantLock getPaintLock(SwingNode node) {
 184                 return node.getPaintLock();
 185             }
 186 
 187             @Override
 188             public void setImageBuffer(SwingNode node, final int[] data,
 189                                final int x, final int y,
 190                                final int w, final int h, final int linestride,
 191                                final double scaleX, final double scaleY) {
 192                 node.setImageBuffer(data, x, y, w, h, linestride, scaleX, scaleY);
 193             }
 194 
 195             @Override
 196             public void setImageBounds(SwingNode node, final int x, final int y,
 197                                final int w, final int h) {
 198                 node.setImageBounds(x, y, w, h);
 199             }
 200 
 201             @Override
 202             public void repaintDirtyRegion(SwingNode node, final int dirtyX, final int dirtyY,
 203                                final int dirtyWidth, final int dirtyHeight) {
 204                 node.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 205             }
 206 
 207             @Override
 208             public void ungrabFocus(SwingNode node, boolean postUngrabEvent) {
 209                 node.ungrabFocus(postUngrabEvent);
 210             }
 211 
 212             @Override
 213             public void setSwingPrefWidth(SwingNode node, int swingPrefWidth) {
 214                 node.swingPrefWidth = swingPrefWidth;
 215             }
 216 
 217             @Override
 218             public void setSwingPrefHeight(SwingNode node, int swingPrefHeight) {
 219                 node.swingPrefHeight = swingPrefHeight;
 220             }
 221 
 222             @Override
 223             public void setSwingMaxWidth(SwingNode node, int swingMaxWidth) {
 224                 node.swingMaxWidth = swingMaxWidth;
 225             }
 226 
 227             @Override
 228             public void setSwingMaxHeight(SwingNode node, int swingMaxHeight) {
 229                 node.swingMaxHeight = swingMaxHeight;
 230             }
 231 
 232             @Override
 233             public void setSwingMinWidth(SwingNode node, int swingMinWidth) {
 234                 node.swingMinWidth = swingMinWidth;
 235             }
 236 
 237             @Override
 238             public void setSwingMinHeight(SwingNode node, int swingMinHeight) {
 239                 node.swingMinHeight = swingMinHeight;
 240             }
 241 
 242             @Override
 243             public void setGrabbed(SwingNode node, boolean grab) {
 244                 node.grabbed = grab;
 245             }
 246         });
 247     }
 248 
 249     private double fxWidth;
 250     private double fxHeight;

 251     private int swingPrefWidth;
 252     private int swingPrefHeight;
 253     private int swingMaxWidth;
 254     private int swingMaxHeight;
 255     private int swingMinWidth;
 256     private int swingMinHeight;
 257 
 258     private volatile JComponent content;
 259     private volatile Object lwFrame;
 260     private final Object getLightweightFrame() { return lwFrame; }
 261 
 262     private NGExternalNode peer;
 263 
 264     private final ReentrantLock paintLock = new ReentrantLock();
 265 
 266     private ReentrantLock getPaintLock() {
 267         return paintLock;
 268     }
 269 
 270     private boolean skipBackwardUnrgabNotification;
 271     private boolean grabbed; // lwframe initiated grab

 272 
 273     {
 274         // To initialize the class helper at the begining each constructor of this class
 275         SwingNodeHelper.initHelper(this);
 276     }
 277 
 278     /**
 279      * Constructs a new instance of {@code SwingNode}.
 280      */
 281     public SwingNode() {
 282         setFocusTraversable(true);
 283         setEventHandler(MouseEvent.ANY, new SwingMouseEventHandler());
 284         setEventHandler(KeyEvent.ANY, new SwingKeyEventHandler());
 285         setEventHandler(ScrollEvent.SCROLL, new SwingScrollEventHandler());
 286 
 287         focusedProperty().addListener((observable, oldValue, newValue) -> {
 288              swiop.activateLwFrame(lwFrame, newValue);
 289         });
 290 
 291         //Workaround for RT-34170
 292         javafx.scene.text.Font.getFamilies();
 293     }
 294 
 295 
 296     private EventHandler windowHiddenHandler = (Event event) -> {
 297         if (lwFrame != null &&  event.getTarget() instanceof Window) {
 298             final Window w = (Window) event.getTarget();
 299             TKStage tk = WindowHelper.getPeer(w);
 300             if (tk != null) {
 301                 if (isThreadMerged) {
 302                     swiop.overrideNativeWindowHandle(0L, null);
 303                 } else {
 304                     // Postpone actual window closing to ensure that
 305                     // a native window handler is valid on a Swing side
 306                     tk.postponeClose();
 307                     SwingNodeHelper.runOnEDT(() -> {
 308                         swiop.overrideNativeWindowHandle(0L,
 309                             (Runnable) () -> SwingNodeHelper.runOnFxThread(
 310                                         () -> tk.closePostponed()));
 311                     });
 312                 }
 313             }
 314         }
 315 
 316     };
 317 
 318     private Window hWindow = null;
 319     private void notifyNativeHandle(Window window) {
 320         if (hWindow != window) {
 321             if (hWindow != null) {
 322                 hWindow.removeEventHandler(WINDOW_HIDDEN, windowHiddenHandler);
 323             }
 324             if (window != null) {
 325                 window.addEventHandler(WINDOW_HIDDEN, windowHiddenHandler);
 326             }
 327             hWindow = window;
 328         }
 329 
 330         if (lwFrame != null) {
 331             long rawHandle = 0L;
 332             if (window != null) {
 333                 TKStage tkStage = WindowHelper.getPeer(window);
 334                 if (tkStage != null) {
 335                     rawHandle = tkStage.getRawHandle();
 336                 }
 337             }
 338             swiop.overrideNativeWindowHandle(rawHandle, null);
 339         }
 340     }
 341 
 342     /**
 343      * Attaches a {@code JComponent} instance to display in this {@code SwingNode}.
 344      * <p>
 345      * The method can be called either on the JavaFX Application thread or the Event Dispatch thread.
 346      * Note however, that access to a Swing component must occur from the Event Dispatch thread
 347      * according to the Swing threading restrictions.
 348      *
 349      * @param content a Swing component to display in this {@code SwingNode}
 350      *
 351      * @see java.awt.EventQueue#isDispatchThread()
 352      * @see javafx.application.Platform#isFxApplicationThread()
 353      */
 354     public void setContent(final JComponent content) {
 355         this.content = content;
 356 
 357         SwingNodeHelper.runOnEDT(() -> setContentImpl(content));
 358     }
 359 
 360     /**
 361      * Returns the {@code JComponent} instance attached to this {@code SwingNode}.
 362      * <p>
 363      * The method can be called either on the JavaFX Application thread or the Event Dispatch thread.
 364      * Note however, that access to a Swing component must occur from the Event Dispatch thread
 365      * according to the Swing threading restrictions.
 366      *
 367      * @see java.awt.EventQueue#isDispatchThread()
 368      * @see javafx.application.Platform#isFxApplicationThread()
 369      *
 370      * @return the Swing component attached to this {@code SwingNode}
 371      */
 372     public JComponent getContent() {
 373         return content;
 374     }
 375 
 376     private static final class OptionalMethod<T> {
 377         private final Method method;


 396             return method != null;
 397         }
 398 
 399         public boolean isIntegerApi() {
 400             return isIntegerAPI;
 401         }
 402 
 403         public Object invoke(T object, Object... args) {
 404             if (method != null) {
 405                 try {
 406                     return method.invoke(object, args);
 407                 } catch (Throwable ex) {
 408                     throw new RuntimeException("Error when calling " + object.getClass().getName() + "." + method.getName() + "().", ex);
 409                 }
 410             } else {
 411                 return null;
 412             }
 413         }
 414     }
 415 




















 416     /*
 417      * Called on EDT
 418      */
 419     private void setContentImpl(JComponent content) {
 420         if (lwFrame != null) {
 421             swiop.disposeLwFrame();
 422             lwFrame = null;
 423         }
 424         if (content != null) {
 425             lwFrame = swiop.createLightweightFrame();
 426 
 427             SwingNodeWindowFocusListener snfListener =
 428                                  new SwingNodeWindowFocusListener(this);
 429             swiop.addWindowFocusListener(lwFrame, snfListener);
 430 
 431             if (getScene() != null) {
 432                 Window window = getScene().getWindow();
 433                 if (window != null) {
 434                     swiop.notifyDisplayChanged(lwFrame, window);







 435                 }
 436             }
 437             swiop.setContent(lwFrame, swiop.createSwingNodeContent(content, this));
 438             swiop.setVisible(lwFrame, true);
 439 
 440             Disposer.addRecord(this, swiop.createSwingNodeDisposer(lwFrame));

 441 
 442             if (getScene() != null) {
 443                 notifyNativeHandle(getScene().getWindow());
 444             }
 445 
 446             SwingNodeHelper.runOnFxThread(() -> {
 447                 locateLwFrame();// initialize location
 448 
 449                 if (focusedProperty().get()) {
 450                     swiop.activateLwFrame(lwFrame, true);
 451                 }
 452             });
 453         }
 454     }
 455 
 456     private List<Runnable> peerRequests = new ArrayList<>();
 457 
 458     /*
 459      * Called on EDT
 460      */
 461     void setImageBuffer(final int[] data,
 462                         final int x, final int y,
 463                         final int w, final int h,
 464                         final int linestride,
 465                         final double scaleX,
 466                         final double scaleY)
 467     {
 468         Runnable r = () -> peer.setImageBuffer(IntBuffer.wrap(data), x, y, w, h,
 469                                 w, h, linestride, scaleX, scaleY);
 470         SwingNodeHelper.runOnFxThread(() -> {
 471             if (peer != null) {
 472                 r.run();
 473             } else {
 474                 peerRequests.clear();
 475                 peerRequests.add(r);
 476             }
 477         });
 478     }
 479 
 480     /*
 481      * Called on EDT
 482      */
 483     void setImageBounds(final int x, final int y, final int w, final int h) {
 484         Runnable r = () -> peer.setImageBounds(x, y, w, h, w, h);
 485         SwingNodeHelper.runOnFxThread(() -> {
 486             if (peer != null) {
 487                 r.run();
 488             } else {
 489                 peerRequests.add(r);
 490             }
 491         });
 492     }
 493 
 494     /*
 495      * Called on EDT
 496      */
 497     void repaintDirtyRegion(final int dirtyX, final int dirtyY, final int dirtyWidth, final int dirtyHeight) {
 498         Runnable r = () -> {
 499             peer.repaintDirtyRegion(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 500             NodeHelper.markDirty(this, DirtyBits.NODE_CONTENTS);
 501         };
 502         SwingNodeHelper.runOnFxThread(() -> {
 503             if (peer != null) {
 504                 r.run();
 505             } else {
 506                 peerRequests.add(r);
 507             }
 508         });
 509     }
 510 
 511     @Override public boolean isResizable() {
 512         return true;
 513     }
 514 
 515     /**
 516      * Invoked by the {@code SwingNode}'s parent during layout to set the {@code SwingNode}'s
 517      * width and height. <b>Applications should not invoke this method directly</b>.
 518      * If an application needs to directly set the size of the {@code SwingNode}, it should
 519      * set the Swing component's minimum/preferred/maximum size constraints which will
 520      * be propagated correspondingly to the {@code SwingNode} and it's parent will honor those
 521      * settings during layout.
 522      *
 523      * @param width the target layout bounds width
 524      * @param height the target layout bounds height
 525      */
 526     @Override public void resize(final double width, final double height) {
 527         super.resize(width, height);
 528         if (width != this.fxWidth || height != this.fxHeight) {
 529             this.fxWidth = width;
 530             this.fxHeight = height;
 531             NodeHelper.geomChanged(this);
 532             NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY);
 533             SwingNodeHelper.runOnEDT(() -> {
 534                 if (lwFrame != null) {
 535                     locateLwFrame();
 536                 }
 537             });
 538         }
 539     }
 540 
 541     /**
 542      * Returns the {@code SwingNode}'s preferred width for use in layout calculations.
 543      * This value corresponds to the preferred width of the Swing component.
 544      *
 545      * @return the preferred width that the node should be resized to during layout
 546      */
 547     @Override
 548     public double prefWidth(double height) {
 549         return swingPrefWidth;
 550     }
 551 
 552     /**
 553      * Returns the {@code SwingNode}'s preferred height for use in layout calculations.


 597      * @return the minimum height that the node should be resized to during layout
 598      */
 599     @Override public double minHeight(double width) {
 600         return swingMinHeight;
 601     }
 602 
 603     /*
 604      * Note: This method MUST only be called via its accessor method.
 605      */
 606     private boolean doComputeContains(double localX, double localY) {
 607         return true;
 608     }
 609 
 610     private final InvalidationListener locationListener = observable -> {
 611         locateLwFrame();
 612     };
 613 
 614     private final EventHandler<FocusUngrabEvent> ungrabHandler = event -> {
 615         if (!skipBackwardUnrgabNotification) {
 616             if (lwFrame != null) {
 617                 AccessController.doPrivileged(new PostEventAction(
 618                     swiop.createUngrabEvent(lwFrame)));
 619             }
 620         }
 621     };
 622 
 623     private final ChangeListener<Boolean> windowVisibleListener = (observable, oldValue, newValue) -> {
 624         if (!newValue) {
 625             swiop.disposeLwFrame();
 626         } else {
 627             setContent(content);
 628         }
 629     };
 630 
 631     private final ChangeListener<Window> sceneWindowListener = (observable, oldValue, newValue) -> {
 632         if (oldValue != null) {
 633             removeWindowListeners(oldValue);
 634         }
 635 
 636         notifyNativeHandle(newValue);
 637 
 638         if (newValue != null) {
 639             addWindowListeners(newValue);
 640         }
 641     };
 642 
 643     private void removeSceneListeners(Scene scene) {
 644         Window window = scene.getWindow();
 645         if (window != null) {


 648         scene.windowProperty().removeListener(sceneWindowListener);
 649     }
 650 
 651     private void addSceneListeners(final Scene scene) {
 652         Window window = scene.getWindow();
 653         if (window != null) {
 654             addWindowListeners(window);
 655             notifyNativeHandle(window);
 656         }
 657         scene.windowProperty().addListener(sceneWindowListener);
 658     }
 659 
 660     private void addWindowListeners(final Window window) {
 661         window.xProperty().addListener(locationListener);
 662         window.yProperty().addListener(locationListener);
 663         window.widthProperty().addListener(locationListener);
 664         window.heightProperty().addListener(locationListener);
 665         window.renderScaleXProperty().addListener(locationListener);
 666         window.addEventHandler(FocusUngrabEvent.FOCUS_UNGRAB, ungrabHandler);
 667         window.showingProperty().addListener(windowVisibleListener);
 668         swiop.setLwFrameScale(window.getRenderScaleX(), window.getRenderScaleY());
 669     }
 670 
 671     private void removeWindowListeners(final Window window) {
 672         window.xProperty().removeListener(locationListener);
 673         window.yProperty().removeListener(locationListener);
 674         window.widthProperty().removeListener(locationListener);
 675         window.heightProperty().removeListener(locationListener);
 676         window.renderScaleXProperty().removeListener(locationListener);
 677         window.removeEventHandler(FocusUngrabEvent.FOCUS_UNGRAB, ungrabHandler);
 678         window.showingProperty().removeListener(windowVisibleListener);
 679     }
 680 
 681     /*
 682      * Note: This method MUST only be called via its accessor method.
 683      */
 684     private NGNode doCreatePeer() {
 685         peer = new NGExternalNode();
 686         peer.setLock(paintLock);
 687         for (Runnable request : peerRequests) {
 688             request.run();
 689         }
 690         peerRequests = null;
 691 
 692         if (getScene() != null) {
 693             addSceneListeners(getScene());
 694         }
 695 
 696         sceneProperty().addListener((observable, oldValue, newValue) -> {
 697             if (oldValue != null) {
 698                 // Removed from scene
 699                 removeSceneListeners(oldValue);
 700                 swiop.disposeLwFrame();
 701             }
 702             if (newValue != null) {
 703                 // Added to another scene
 704                 if (content != null && lwFrame == null) {
 705                     setContent(content);
 706                 }
 707                 addSceneListeners(newValue);
 708             }
 709         });
 710 
 711         NodeHelper.treeVisibleProperty(this).addListener((observable, oldValue, newValue) -> {
 712             swiop.setLwFrameVisible(newValue);
 713         });
 714 
 715         return peer;
 716     }
 717 
 718     /*
 719      * Note: This method MUST only be called via its accessor method.
 720      */
 721     private void doUpdatePeer() {
 722         if (NodeHelper.isDirty(this, DirtyBits.NODE_VISIBLE)
 723                 || NodeHelper.isDirty(this, DirtyBits.NODE_BOUNDS)) {
 724             locateLwFrame(); // initialize location
 725         }
 726         if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
 727             peer.markContentDirty();
 728         }
 729     }
 730 








 731     private void locateLwFrame() {
 732         if (getScene() == null
 733                 || lwFrame == null
 734                 || getScene().getWindow() == null
 735                 || !getScene().getWindow().isShowing()) {
 736             // Not initialized yet. Skip the update to set the real values later
 737             return;
 738         }
 739         Window w = getScene().getWindow();
 740         double renderScaleX = w.getRenderScaleX();
 741         double renderScaleY = w.getRenderScaleY();
 742         final Point2D loc = localToScene(0, 0);
 743         final int windowX = (int) (w.getX());
 744         final int windowY = (int) (w.getY());
 745         final int windowW = (int) (w.getWidth());
 746         final int windowH = (int) (w.getHeight());
 747         final int frameX = (int) Math.round(w.getX() + getScene().getX() + loc.getX());
 748         final int frameY = (int) Math.round(w.getY() + getScene().getY() + loc.getY());
 749         final int frameW = (int) (fxWidth);
 750         final int frameH = (int) (fxHeight);
 751 
 752         SwingNodeHelper.runOnEDT(() -> {
 753             if (lwFrame != null) {
 754                 swiop.notifyDisplayChanged(lwFrame, w);
 755                 swiop.setBounds(lwFrame, frameX, frameY, frameW, frameH);
 756                 swiop.setHostBounds(lwFrame, w);













































































 757             }
 758         });
 759     }
 760 
 761     /*
 762      * Note: This method MUST only be called via its accessor method.
 763      */
 764     private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
 765         bounds.deriveWithNewBounds(0, 0, 0, (float)fxWidth, (float)fxHeight, 0);
 766         tx.transform(bounds, bounds);
 767         return bounds;
 768     }
 769 













 770 
 771     private static class SwingNodeWindowFocusListener implements WindowFocusListener {
 772         private WeakReference<SwingNode> swingNodeRef;
 773 
 774         SwingNodeWindowFocusListener(SwingNode swingNode) {
 775             this.swingNodeRef = new WeakReference<SwingNode>(swingNode);
 776         }
 777 
 778         @Override
 779         public void windowGainedFocus(WindowEvent e) {
 780             SwingNodeHelper.runOnFxThread(() -> {
 781                 SwingNode swingNode = swingNodeRef.get();
 782                 if (swingNode != null) {
 783                     swingNode.requestFocus();
 784                 }
 785             });
 786         }
 787 
 788         @Override
 789         public void windowLostFocus(WindowEvent e) {
 790             SwingNodeHelper.runOnFxThread(() -> {
 791                 SwingNode swingNode = swingNodeRef.get();
 792                 if (swingNode != null) {
 793                     swingNode.ungrabFocus(true);
 794                 }
 795             });
 796         }
 797     }
 798 
















































































































































































 799     private void ungrabFocus(boolean postUngrabEvent) {
 800         // On X11 grab is limited to a single XDisplay connection,
 801         // so we can't delegate it to another GUI toolkit.
 802         if (PlatformUtil.isLinux()) return;
 803 
 804         if (grabbed &&
 805             getScene() != null &&
 806             getScene().getWindow() != null &&
 807             WindowHelper.getPeer(getScene().getWindow()) != null)
 808         {
 809             skipBackwardUnrgabNotification = !postUngrabEvent;
 810             WindowHelper.getPeer(getScene().getWindow()).ungrabFocus();
 811             skipBackwardUnrgabNotification = false;
 812             grabbed = false;
 813         }
 814     }
 815 
 816     private class PostEventAction implements PrivilegedAction<Void> {
 817         private AWTEvent event;
 818         PostEventAction(AWTEvent event) {
 819             this.event = event;
 820         }
 821         @Override
 822         public Void run() {
 823             EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
 824             eq.postEvent(event);
 825             return null;
 826         }
 827     }
 828 
 829     private class SwingMouseEventHandler implements EventHandler<MouseEvent> {
 830         private final Set<MouseButton> mouseClickedAllowed = new HashSet<>();
 831 
 832         @Override
 833         public void handle(MouseEvent event) {
 834         Object frame = swiop.getLightweightFrame();
 835             if (frame == null) {
 836                 return;
 837             }
 838             int swingID = SwingEvents.fxMouseEventTypeToMouseID(event);
 839             if (swingID < 0) {
 840                 return;
 841             }
 842 
 843             // Prevent ancestors of the SwingNode from stealing the focus
 844             event.consume();
 845 
 846             final EventType<?> type = event.getEventType();
 847             if (type == MouseEvent.MOUSE_PRESSED) {
 848                 mouseClickedAllowed.add(event.getButton());
 849             } else if (type == MouseEvent.MOUSE_RELEASED) {
 850                 // RELEASED comes before CLICKED, so we don't remove the button from the set
 851                 //mouseClickedAllowed.remove(event.getButton());
 852             } else if (type == MouseEvent.MOUSE_DRAGGED) {
 853                 // This is what AWT/Swing do
 854                 mouseClickedAllowed.clear();
 855             } else if (type == MouseEvent.MOUSE_CLICKED) {
 856                 if (event.getClickCount() == 1 && !mouseClickedAllowed.contains(event.getButton())) {
 857                     // RT-34610: In FX, CLICKED events are generated even after dragging the mouse pointer
 858                     // Note that this is only relevant for single clicks. Double clicks use a smudge factor.
 859                     return;
 860                 }
 861                 mouseClickedAllowed.remove(event.getButton());
 862             }
 863             int swingModifiers = SwingEvents.fxMouseModsToMouseMods(event);
 864             boolean swingPopupTrigger = event.isPopupTrigger();
 865             int swingButton = SwingEvents.fxMouseButtonToMouseButton(event);
 866             long swingWhen = System.currentTimeMillis();
 867             int relX = (int) Math.round(event.getX());
 868             int relY = (int) Math.round(event.getY());
 869             int absX = (int) Math.round(event.getScreenX());
 870             int absY = (int) Math.round(event.getScreenY());
 871             java.awt.event.MouseEvent mouseEvent =
 872                     swiop.createMouseEvent(
 873                         frame, swingID, swingWhen, swingModifiers,
 874                         relX, relY, absX, absY,
 875                         event.getClickCount(), swingPopupTrigger, swingButton);
 876             AccessController.doPrivileged(new PostEventAction(mouseEvent));
 877         }
 878     }
 879 
 880     private class SwingScrollEventHandler implements EventHandler<ScrollEvent> {
 881         @Override
 882         public void handle(ScrollEvent event) {
 883         Object frame = swiop.getLightweightFrame();
 884             if (frame == null) {
 885                 return;
 886             }
 887 
 888             int swingModifiers = SwingEvents.fxScrollModsToMouseWheelMods(event);
 889             final boolean isShift = (swingModifiers & InputEvent.SHIFT_DOWN_MASK) != 0;
 890 
 891             // Vertical scroll.
 892             if (!isShift && event.getDeltaY() != 0.0) {
 893                 sendMouseWheelEvent(frame, event.getX(), event.getY(),
 894                         swingModifiers, event.getDeltaY() / event.getMultiplierY());
 895             }
 896             // Horizontal scroll or shirt+vertical scroll.
 897             final double delta = isShift && event.getDeltaY() != 0.0
 898                                   ? event.getDeltaY() / event.getMultiplierY()
 899                                   : event.getDeltaX() / event.getMultiplierX();
 900             if (delta != 0.0) {
 901                 swingModifiers |= InputEvent.SHIFT_DOWN_MASK;
 902                 sendMouseWheelEvent(frame, event.getX(), event.getY(),
 903                         swingModifiers, delta);
 904             }
 905         }
 906 
 907         private void sendMouseWheelEvent(Object source, double fxX, double fxY, int swingModifiers, double delta) {
 908             int wheelRotation = (int) delta;
 909             int signum = (int) Math.signum(delta);
 910             if (signum * delta < 1) {
 911                 wheelRotation = signum;
 912             }
 913             int x = (int) Math.round(fxX);
 914             int y = (int) Math.round(fxY);
 915             MouseWheelEvent mouseWheelEvent =
 916                 swiop.createMouseWheelEvent(source, swingModifiers, x, y, -wheelRotation);


 917             AccessController.doPrivileged(new PostEventAction(mouseWheelEvent));
 918         }
 919     }
 920 
 921     private class SwingKeyEventHandler implements EventHandler<KeyEvent> {
 922         @Override
 923         public void handle(KeyEvent event) {
 924         Object frame = swiop.getLightweightFrame();
 925             if (frame == null) {
 926                 return;
 927             }
 928             if (event.getCharacter().isEmpty()) {
 929                 // TODO: should we post an "empty" character?
 930                 return;
 931             }
 932             // Don't let Arrows, Tab, Shift+Tab traverse focus out.
 933             if (event.getCode() == KeyCode.LEFT  ||
 934                 event.getCode() == KeyCode.RIGHT ||
 935                 event.getCode() == KeyCode.UP ||
 936                 event.getCode() == KeyCode.DOWN ||
 937                 event.getCode() == KeyCode.TAB)
 938             {
 939                 event.consume();
 940             }
 941 
 942             int swingID = SwingEvents.fxKeyEventTypeToKeyID(event);
 943             if (swingID < 0) {
 944                 return;
 945             }
 946             int swingModifiers = SwingEvents.fxKeyModsToKeyMods(event);
 947             int swingKeyCode = event.getCode().getCode();
 948             char swingChar = event.getCharacter().charAt(0);
 949 
 950             // A workaround. Some swing L&F's process mnemonics on KEY_PRESSED,
 951             // for which swing provides a keychar. Extracting it from the text.
 952             if (event.getEventType() == javafx.scene.input.KeyEvent.KEY_PRESSED) {
 953                 String text = event.getText();
 954                 if (text.length() == 1) {
 955                     swingChar = text.charAt(0);
 956                 }
 957             }
 958             long swingWhen = System.currentTimeMillis();
 959             java.awt.event.KeyEvent keyEvent = swiop.createKeyEvent(frame,
 960                 swingID, swingWhen, swingModifiers, swingKeyCode,
 961                 swingChar);
 962             AccessController.doPrivileged(new PostEventAction(keyEvent));
 963         }
 964     }
 965 }
 966 
< prev index next >