1 /*
   2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.embed.swt;
  27 
  28 import com.sun.glass.ui.Application;
  29 import com.sun.glass.ui.Pixels;
  30 import com.sun.javafx.cursor.CursorFrame;
  31 import com.sun.javafx.cursor.CursorType;
  32 import com.sun.javafx.embed.AbstractEvents;
  33 import com.sun.javafx.embed.EmbeddedSceneDSInterface;
  34 import com.sun.javafx.embed.EmbeddedSceneDTInterface;
  35 import com.sun.javafx.embed.EmbeddedSceneInterface;
  36 import com.sun.javafx.embed.EmbeddedStageInterface;
  37 import com.sun.javafx.embed.HostInterface;
  38 import com.sun.javafx.stage.EmbeddedWindow;
  39 
  40 import java.lang.reflect.Field;
  41 import java.lang.reflect.Method;
  42 import java.nio.ByteBuffer;
  43 import java.nio.IntBuffer;
  44 import java.security.AccessController;
  45 import java.security.PrivilegedAction;
  46 import java.util.ArrayList;
  47 import java.util.Arrays;
  48 import java.util.HashSet;
  49 import java.util.Set;
  50 import java.util.concurrent.CountDownLatch;
  51 
  52 import javafx.application.Platform;
  53 import javafx.beans.NamedArg;
  54 import javafx.scene.Scene;
  55 import javafx.scene.input.TransferMode;
  56 import javafx.stage.Window;
  57 import javafx.util.FXPermission;
  58 
  59 import org.eclipse.swt.SWT;
  60 import org.eclipse.swt.dnd.DND;
  61 import org.eclipse.swt.dnd.DragSource;
  62 import org.eclipse.swt.dnd.DragSourceListener;
  63 import org.eclipse.swt.dnd.DropTarget;
  64 import org.eclipse.swt.dnd.DropTargetEvent;
  65 import org.eclipse.swt.dnd.DropTargetListener;
  66 import org.eclipse.swt.dnd.FileTransfer;
  67 import org.eclipse.swt.dnd.HTMLTransfer;
  68 import org.eclipse.swt.dnd.ImageTransfer;
  69 import org.eclipse.swt.dnd.RTFTransfer;
  70 import org.eclipse.swt.dnd.TextTransfer;
  71 import org.eclipse.swt.dnd.Transfer;
  72 import org.eclipse.swt.dnd.TransferData;
  73 import org.eclipse.swt.dnd.URLTransfer;
  74 import org.eclipse.swt.events.ControlEvent;
  75 import org.eclipse.swt.events.ControlListener;
  76 import org.eclipse.swt.events.DisposeEvent;
  77 import org.eclipse.swt.events.DisposeListener;
  78 import org.eclipse.swt.events.FocusEvent;
  79 import org.eclipse.swt.events.FocusListener;
  80 import org.eclipse.swt.events.KeyEvent;
  81 import org.eclipse.swt.events.KeyListener;
  82 import org.eclipse.swt.events.MenuDetectEvent;
  83 import org.eclipse.swt.events.MouseEvent;
  84 import org.eclipse.swt.events.MouseListener;
  85 import org.eclipse.swt.events.MouseTrackListener;
  86 import org.eclipse.swt.events.PaintEvent;
  87 import org.eclipse.swt.graphics.Image;
  88 import org.eclipse.swt.graphics.ImageData;
  89 import org.eclipse.swt.graphics.PaletteData;
  90 import org.eclipse.swt.graphics.Point;
  91 import org.eclipse.swt.graphics.RGB;
  92 import org.eclipse.swt.graphics.Rectangle;
  93 import org.eclipse.swt.widgets.Canvas;
  94 import org.eclipse.swt.widgets.Composite;
  95 import org.eclipse.swt.widgets.Control;
  96 import org.eclipse.swt.widgets.Display;
  97 import org.eclipse.swt.widgets.Event;
  98 import org.eclipse.swt.widgets.Listener;
  99 import org.eclipse.swt.widgets.Shell;
 100 
 101 /**
 102  * {@code FXCanvas} is a component to embed JavaFX content into
 103  * SWT applications. The content to be displayed is specified
 104  * with the {@link #setScene} method that accepts an instance of
 105  * JavaFX {@code Scene}. After the scene is attached, it gets
 106  * repainted automatically. All the input and focus events are
 107  * forwarded to the scene transparently to the developer.
 108  * <p>
 109  * Here is a typical pattern how {@code FXCanvas} can used:
 110  * <pre>
 111  *    public class Test {
 112  *        private static Scene createScene() {
 113  *            Group group = new Group();
 114  *            Scene scene = new Scene(group);
 115  *            Button button = new Button("JFX Button");
 116  *            group.getChildren().add(button);
 117  *            return scene;
 118  *        }
 119  *
 120  *        public static void main(String[] args) {
 121  *            Display display = new Display();
 122  *            Shell shell = new Shell(display);
 123  *            shell.setLayout(new FillLayout());
 124  *            FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
 125  *            Scene scene = createScene();
 126  *            canvas.setScene(scene);
 127  *            shell.open();
 128  *            while (!shell.isDisposed()) {
 129  *                if (!display.readAndDispatch()) display.sleep();
 130  *            }
 131  *            display.dispose();
 132  *        }
 133  *    }
 134  * </pre>
 135  *
 136  *
 137  * @since JavaFX 2.0
 138  */
 139 public class FXCanvas extends Canvas {
 140 
 141     // Internal permission used by FXCanvas (SWT interop)
 142     private static final FXPermission FXCANVAS_PERMISSION =
 143             new FXPermission("accessFXCanvasInternals");
 144 
 145     private HostContainer hostContainer;
 146     private volatile EmbeddedWindow stage;
 147     private volatile Scene scene;
 148     private EmbeddedStageInterface stagePeer;
 149     private EmbeddedSceneInterface scenePeer;
 150 
 151     private int pWidth = 0;
 152     private int pHeight = 0;
 153 
 154     private volatile int pPreferredWidth = -1;
 155     private volatile int pPreferredHeight = -1;
 156 
 157     private IntBuffer pixelsBuf = null;
 158 
 159     // This filter runs when any widget is moved
 160     Listener moveFilter = event -> {
 161         // If a parent has moved, send a move event to FX
 162         Control control = FXCanvas.this;
 163         while (control != null) {
 164             if (control == event.widget) {
 165                 sendMoveEventToFX();
 166                 break;
 167             }
 168             control = control.getParent();
 169         };
 170     };
 171 
 172     private double getScaleFactor() {
 173         if (SWT.getPlatform().equals("cocoa")) {
 174             if (windowField == null || screenMethod == null || backingScaleFactorMethod == null) {
 175                 return 1.0;
 176             }
 177             try {
 178                 Object nsWindow = windowField.get(this.getShell());
 179                 Object nsScreen = screenMethod.invoke(nsWindow);
 180                 Object bsFactor = backingScaleFactorMethod.invoke(nsScreen);
 181                 return ((Double) bsFactor).doubleValue();
 182             } catch (Exception e) {
 183                 // FAIL silently should the reflection fail
 184             }
 185         }
 186         return 1.0;
 187     }
 188 
 189     private DropTarget dropTarget;
 190 
 191     static Transfer [] StandardTransfers = new Transfer [] {
 192         TextTransfer.getInstance(),
 193         RTFTransfer.getInstance(),
 194         HTMLTransfer.getInstance(),
 195         URLTransfer.getInstance(),
 196         ImageTransfer.getInstance(),
 197         FileTransfer.getInstance(),
 198     };
 199     static Transfer [] CustomTransfers = new Transfer [0];
 200 
 201     static Transfer [] getAllTransfers () {
 202         Transfer [] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length];
 203         System.arraycopy(StandardTransfers, 0, transfers, 0, StandardTransfers.length);
 204         System.arraycopy(CustomTransfers, 0, transfers, StandardTransfers.length, CustomTransfers.length);
 205         return transfers;
 206     }
 207 
 208     static Transfer getCustomTransfer(String mime) {
 209         for (int i=0; i<CustomTransfers.length; i++) {
 210             if (((CustomTransfer)CustomTransfers[i]).getMime().equals(mime)) {
 211                 return CustomTransfers[i];
 212             }
 213         }
 214         Transfer transfer = new CustomTransfer (mime, mime);
 215         Transfer [] newCustom = new Transfer [CustomTransfers.length + 1];
 216         System.arraycopy(CustomTransfers, 0, newCustom, 0, CustomTransfers.length);
 217         newCustom[CustomTransfers.length] = transfer;
 218         CustomTransfers = newCustom;
 219         return transfer;
 220     }
 221 
 222     private static Field windowField;
 223     private static Method windowMethod;
 224     private static Method screenMethod;
 225     private static Method backingScaleFactorMethod;
 226 
 227     static {
 228         if (SWT.getPlatform().equals("cocoa")) {
 229             try {
 230                 windowField = Shell.class.getDeclaredField("window");
 231                 windowField.setAccessible(true);
 232 
 233                 Class nsViewClass = Class.forName("org.eclipse.swt.internal.cocoa.NSView");
 234                 windowMethod = nsViewClass.getDeclaredMethod("window");
 235                 windowMethod.setAccessible(true);
 236 
 237                 Class nsWindowClass = Class.forName("org.eclipse.swt.internal.cocoa.NSWindow");
 238                 screenMethod = nsWindowClass.getDeclaredMethod("screen");
 239                 screenMethod.setAccessible(true);
 240 
 241                 Class nsScreenClass = Class.forName("org.eclipse.swt.internal.cocoa.NSScreen");
 242                 backingScaleFactorMethod = nsScreenClass.getDeclaredMethod("backingScaleFactor");
 243                 backingScaleFactorMethod.setAccessible(true);
 244             } catch (Exception e) {
 245                 //Fail silently.  If we can't get the methods, then the current version of SWT has no retina support
 246             }
 247         }
 248         initFx();
 249     }
 250 
 251     /**
 252      * @inheritDoc
 253      */
 254     public FXCanvas(@NamedArg("parent") Composite parent, @NamedArg("style") int style) {
 255         super(parent, style | SWT.NO_BACKGROUND);
 256         setApplicationName(Display.getAppName());
 257         hostContainer = new HostContainer();
 258         registerEventListeners();
 259         Display display = parent.getDisplay();
 260         display.addFilter(SWT.Move, moveFilter);
 261     }
 262 
 263     /**
 264      * Retrieves the {@code FXCanvas} embedding the given {@code Scene},
 265      * that is the {@code FXCanvas} to which the given {@code Scene} was
 266      * attached using {@link #setScene}.
 267      *
 268      * @param scene the {@code Scene} whose embedding {@code FXCanvas}
 269      *              instance is to be retrieved
 270      * @return the {@code FXCanvas} to which the given {@code Scene} is
 271      * attached, or null if the given {@code Scene} is not attached to an
 272      * {@code FXCanvas}.
 273      *
 274      * @since 9
 275      */
 276     public static FXCanvas getFXCanvas(Scene scene) {
 277         Window window = scene.getWindow();
 278         if (window != null && window instanceof EmbeddedWindow) {
 279             HostInterface hostInterface = ((EmbeddedWindow) window).getHost();
 280             if (hostInterface instanceof HostContainer) {
 281                 // Obtain FXCanvas as the enclosing instance of the FXCanvas$HostContainer
 282                 // that is host of the embedded Scene's EmbeddedWindow.
 283                 return ((HostContainer)hostInterface).fxCanvas;
 284             }
 285         }
 286         return null;
 287     }
 288 
 289     private static void initFx() {
 290         // NOTE: no internal "com.sun.*" packages can be accessed until after
 291         // the JavaFX platform is initialized. The list of needed internal
 292         // packages is kept in the PlatformImpl class.
 293         long eventProc = 0;
 294         try {
 295             Field field = Display.class.getDeclaredField("eventProc");
 296             field.setAccessible(true);
 297             if (field.getType() == int.class) {
 298                 eventProc = field.getInt(Display.getDefault());
 299             } else {
 300                 if (field.getType() == long.class) {
 301                     eventProc = field.getLong(Display.getDefault());
 302                 }
 303             }
 304         } catch (Throwable th) {
 305             //Fail silently
 306         }
 307         final String eventProcStr = String.valueOf(eventProc);
 308 
 309         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 310             System.setProperty("com.sun.javafx.application.type", "FXCanvas");
 311             System.setProperty("javafx.embed.isEventThread", "true");
 312             System.setProperty("glass.win.uiScale", "100%");
 313             System.setProperty("glass.win.renderScale", "100%");
 314             System.setProperty("javafx.embed.eventProc", eventProcStr);
 315             return null;
 316         });
 317 
 318         final CountDownLatch startupLatch = new CountDownLatch(1);
 319         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 320             Platform.startup(() -> {
 321                 startupLatch.countDown();
 322             });
 323             return null;
 324         }, null, FXCANVAS_PERMISSION);
 325 
 326         try {
 327             startupLatch.await();
 328         } catch (InterruptedException ex) {
 329             throw new RuntimeException(ex);
 330         }
 331     }
 332 
 333     private void setApplicationName(String name) {
 334         Platform.runLater(()-> Application.GetApplication().setName(name));
 335     }
 336 
 337     static ArrayList<DropTarget> targets = new ArrayList<>();
 338 
 339     DropTarget getDropTarget() {
 340         return dropTarget;
 341     }
 342 
 343     void setDropTarget(DropTarget newTarget) {
 344         if (dropTarget != null) {
 345             targets.remove(dropTarget);
 346             dropTarget.dispose();
 347         }
 348         dropTarget = newTarget;
 349         if (dropTarget != null) {
 350             targets.add(dropTarget);
 351         }
 352     }
 353 
 354     static void updateDropTarget() {
 355         // Update all drop targets rather than just this target
 356         //
 357         // In order for a drop target to recognise a custom format,
 358         // the format must be registered and the transfer type added
 359         // to the list of transfers that the target accepts.  This
 360         // must happen before the drag and drop operations starts
 361         // or the drop target will not accept the format.  Therefore,
 362         // set all transfers for all targets before any drag and drop
 363         // operation starts
 364         //
 365         for (DropTarget target : targets) {
 366             target.setTransfer(getAllTransfers());
 367         }
 368     }
 369 
 370     /**
 371      * {@inheritDoc}
 372      */
 373     public Point computeSize (int wHint, int hHint, boolean changed) {
 374         checkWidget();
 375         if (wHint == -1 && hHint == -1) {
 376             if (pPreferredWidth != -1 && pPreferredHeight != -1) {
 377                 return new Point (pPreferredWidth, pPreferredHeight);
 378             }
 379         }
 380         return super.computeSize(wHint, hHint, changed);
 381     }
 382 
 383     /**
 384      * Returns the JavaFX scene attached to this {@code FXCanvas}.
 385      *
 386      * @return the {@code Scene} attached to this {@code FXCanvas}
 387      */
 388     public Scene getScene() {
 389         checkWidget();
 390         return scene;
 391     }
 392 
 393     /**
 394      * Attaches a {@code Scene} object to display in this {@code
 395      * FXCanvas}. This method must called either on the JavaFX
 396      * JavaFX application thread (which is the same as the SWT
 397      * event dispatch thread).
 398      *
 399      * @param newScene a scene to display in this {@code FXCanvas}
 400      *
 401      * @see javafx.application.Platform#isFxApplicationThread()
 402      */
 403     public void setScene(final Scene newScene) {
 404         checkWidget();
 405 
 406         if ((stage == null) && (newScene != null)) {
 407             stage = new EmbeddedWindow(hostContainer);
 408             stage.show();
 409         }
 410         scene = newScene;
 411         if (stage != null) {
 412             stage.setScene(newScene);
 413         }
 414         if ((stage != null) && (newScene == null)) {
 415             stage.hide();
 416             stage = null;
 417         }
 418     }
 419 
 420     // Note that removing the listeners is unnecessary
 421     private void registerEventListeners() {
 422         addDisposeListener(new DisposeListener() {
 423             @Override
 424             public void widgetDisposed(DisposeEvent de) {
 425                 Display display = getDisplay();
 426                 display.removeFilter(SWT.Move, moveFilter);
 427                 FXCanvas.this.widgetDisposed(de);
 428             }
 429         });
 430 
 431         addPaintListener(pe -> {
 432             FXCanvas.this.paintControl(pe);
 433         });
 434 
 435         addMouseListener(new MouseListener() {
 436             @Override
 437             public void mouseDoubleClick(MouseEvent me) {
 438                 // Clicks and double-clicks are handled in FX
 439             }
 440             @Override
 441             public void mouseDown(MouseEvent me) {
 442                 // FX only supports 3 buttons so don't send the event for other buttons
 443                 if (me.button > 3) return;
 444                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_PRESSED);
 445             }
 446             @Override
 447             public void mouseUp(MouseEvent me) {
 448                 // FX only supports 3 buttons so don't send the event for other buttons
 449                 if (me.button > 3) return;
 450                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_RELEASED);
 451             }
 452         });
 453 
 454         addMouseMoveListener(me -> {
 455             if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
 456                 // FX only supports 3 buttons so don't send the event for other buttons
 457                 if ((me.stateMask & (SWT.BUTTON1 | SWT.BUTTON2 | SWT.BUTTON3)) != 0) {
 458                     FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_DRAGGED);
 459                 } else {
 460                     FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
 461                 }
 462             } else {
 463                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
 464             }
 465         });
 466 
 467         addListener(SWT.MouseVerticalWheel, e -> {
 468             FXCanvas.this.sendScrollEventToFX(e, AbstractEvents.MOUSEEVENT_VERTICAL_WHEEL);
 469         });
 470         addListener(SWT.MouseHorizontalWheel, e -> {
 471             FXCanvas.this.sendScrollEventToFX(e, AbstractEvents.MOUSEEVENT_HORIZONTAL_WHEEL);
 472         });
 473 
 474         addMouseTrackListener(new MouseTrackListener() {
 475             @Override
 476             public void mouseEnter(MouseEvent me) {
 477                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_ENTERED);
 478             }
 479             @Override
 480             public void mouseExit(MouseEvent me) {
 481                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_EXITED);
 482             }
 483             @Override
 484             public void mouseHover(MouseEvent me) {
 485                 // No mouse hovering in FX
 486             }
 487         });
 488 
 489         addControlListener(new ControlListener() {
 490             @Override
 491             public void controlMoved(ControlEvent ce) {
 492                 FXCanvas.this.sendMoveEventToFX();
 493             }
 494             @Override
 495             public void controlResized(ControlEvent ce) {
 496                 FXCanvas.this.sendResizeEventToFX();
 497             }
 498         });
 499 
 500         addFocusListener(new FocusListener() {
 501             @Override
 502             public void focusGained(FocusEvent fe) {
 503                 FXCanvas.this.sendFocusEventToFX(fe, true);
 504             }
 505             @Override
 506             public void focusLost(FocusEvent fe) {
 507                 FXCanvas.this.sendFocusEventToFX(fe, false);
 508             }
 509         });
 510 
 511         addKeyListener(new KeyListener() {
 512             @Override
 513             public void keyPressed(KeyEvent e) {
 514                 FXCanvas.this.sendKeyEventToFX(e, SWT.KeyDown);
 515 
 516             }
 517             @Override
 518             public void keyReleased(KeyEvent e) {
 519                 FXCanvas.this.sendKeyEventToFX(e, SWT.KeyUp);
 520             }
 521         });
 522 
 523         addMenuDetectListener(e -> {
 524             Runnable r = () -> {
 525                 if (isDisposed()) return;
 526                 FXCanvas.this.sendMenuEventToFX(e);
 527             };
 528             // In SWT, MenuDetect comes before the equivalent mouse event
 529             // On Mac, the order is MenuDetect, MouseDown, MouseUp.  FX
 530             // does not expect this order and when it gets the MouseDown,
 531             // it closes the menu.  The fix is to defer the MenuDetect
 532             // notification until after the MouseDown is sent to FX.
 533             if ("cocoa".equals(SWT.getPlatform())) {
 534                 getDisplay().asyncExec(r);
 535             } else {
 536                 r.run();
 537             }
 538         });
 539     }
 540 
 541     private void widgetDisposed(DisposeEvent de) {
 542         setDropTarget(null);
 543         if (stage != null) {
 544             stage.hide();
 545         }
 546     }
 547 
 548     double lastScaleFactor = 1.0;
 549     int lastWidth, lastHeight;
 550     IntBuffer lastPixelsBuf =  null;
 551     private void paintControl(PaintEvent pe) {
 552         if ((scenePeer == null) || (pixelsBuf == null)) {
 553             return;
 554         }
 555 
 556         double scaleFactor = getScaleFactor();
 557         if (lastScaleFactor != scaleFactor) {
 558             resizePixelBuffer(scaleFactor);
 559             lastScaleFactor = scaleFactor;
 560             scenePeer.setPixelScaleFactors((float)scaleFactor, (float)scaleFactor);
 561         }
 562 
 563         // if we can't get the pixels, draw the bits that were there before
 564         IntBuffer buffer = pixelsBuf;
 565         int width = pWidth, height = pHeight;
 566         if (scenePeer.getPixels(pixelsBuf, pWidth, pHeight)) {
 567             width = lastWidth = pWidth;
 568             height = lastHeight = pHeight;
 569             buffer = lastPixelsBuf = pixelsBuf;
 570         } else {
 571             if (lastPixelsBuf == null) return;
 572             width = lastWidth;
 573             height = lastHeight;
 574             buffer = lastPixelsBuf;
 575         }
 576         width = (int)Math.round(width * scaleFactor);
 577         height = (int)Math.round(height * scaleFactor);
 578 
 579         // Consider optimizing this
 580         ImageData imageData = null;
 581         if ("win32".equals(SWT.getPlatform())) {
 582             PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
 583             int scanline = width * 4;
 584             byte[] dstData = new byte[scanline * height];
 585             int[] srcData = buffer.array();
 586             int dp = 0, sp = 0;
 587             for (int y = 0; y < height; y++) {
 588                 for (int x = 0; x < width; x++) {
 589                     int p = srcData[sp++];
 590                     dstData[dp++] = (byte) (p & 0xFF); //dst:blue
 591                     dstData[dp++] = (byte)((p >> 8) & 0xFF); //dst:green
 592                     dstData[dp++] = (byte)((p >> 16) & 0xFF); //dst:green
 593                     dstData[dp++] = (byte)0x00; //alpha
 594                 }
 595             }
 596             /*ImageData*/ imageData = new ImageData(width, height, 32, palette, 4, dstData);
 597         } else {
 598             if (width * height > buffer.array().length) {
 599                 // We shouldn't be here...
 600                 System.err.println("FXCanvas.paintControl: scale mismatch!");
 601                 return;
 602             }
 603             PaletteData palette = new PaletteData(0x00ff0000, 0x0000ff00, 0x000000ff);
 604             /*ImageData*/  imageData = new ImageData(width, height, 32, palette);
 605             imageData.setPixels(0, 0,width * height, buffer.array(), 0);
 606         }
 607 
 608         Image image = new Image(Display.getDefault(), imageData);
 609         pe.gc.drawImage(image, 0, 0, width, height, 0, 0, pWidth, pHeight);
 610         image.dispose();
 611     }
 612 
 613     private void sendMoveEventToFX() {
 614         if ((stagePeer == null) /*|| !isShowing()*/) {
 615             return;
 616         }
 617         Rectangle rect = getClientArea();
 618         Point los = toDisplay(rect.x, rect.y);
 619         stagePeer.setLocation(los.x, los.y);
 620     }
 621 
 622     private void sendMouseEventToFX(MouseEvent me, int embedMouseType) {
 623         if (scenePeer == null) {
 624             return;
 625         }
 626 
 627         Point los = toDisplay(me.x, me.y);
 628         boolean primaryBtnDown = (me.stateMask & SWT.BUTTON1) != 0;
 629         boolean middleBtnDown = (me.stateMask & SWT.BUTTON2) != 0;
 630         boolean secondaryBtnDown = (me.stateMask & SWT.BUTTON3) != 0;
 631         boolean shift = (me.stateMask & SWT.SHIFT) != 0;
 632         boolean control = (me.stateMask & SWT.CONTROL) != 0;
 633         boolean alt = (me.stateMask & SWT.ALT) != 0;
 634         boolean meta = (me.stateMask & SWT.COMMAND) != 0;
 635         int button = me.button;
 636         switch (embedMouseType) {
 637             case AbstractEvents.MOUSEEVENT_PRESSED:
 638                 primaryBtnDown |= me.button == 1;
 639                 middleBtnDown |= me.button == 2;
 640                 secondaryBtnDown |= me.button == 3;
 641                 break;
 642             case AbstractEvents.MOUSEEVENT_RELEASED:
 643                 primaryBtnDown &= me.button != 1;
 644                 middleBtnDown &= me.button != 2;
 645                 secondaryBtnDown &= me.button != 3;
 646                 break;
 647             case AbstractEvents.MOUSEEVENT_CLICKED:
 648                 // Don't send click events to FX, as they are generated in Scene
 649                 return;
 650 
 651             case AbstractEvents.MOUSEEVENT_MOVED:
 652             case AbstractEvents.MOUSEEVENT_DRAGGED:
 653             case AbstractEvents.MOUSEEVENT_ENTERED:
 654             case AbstractEvents.MOUSEEVENT_EXITED:
 655                 // If this event was the result of mouse movement and has no
 656                 // button associated with it, then we look at the state to
 657                 // determine which button to report
 658                 if (button == 0) {
 659                     if ((me.stateMask & SWT.BUTTON1) != 0) {
 660                         button = 1;
 661                     } else if ((me.stateMask & SWT.BUTTON2) != 0) {
 662                         button = 2;
 663                     } else if ((me.stateMask & SWT.BUTTON3) != 0) {
 664                         button = 3;
 665                     }
 666                 }
 667                 break;
 668 
 669             default:
 670                 break;
 671         }
 672 
 673         scenePeer.mouseEvent(
 674                 embedMouseType,
 675                 SWTEvents.mouseButtonToEmbedMouseButton(button, me.stateMask),
 676                 primaryBtnDown, middleBtnDown, secondaryBtnDown,
 677                 me.x, me.y,
 678                 los.x, los.y,
 679                 shift, control, alt, meta,
 680                 false);  // RT-32990: popup trigger not implemented
 681     }
 682 
 683     private void sendScrollEventToFX(Event event, int type){
 684         if (scenePeer == null) {
 685             return;
 686         }
 687         Point los = toDisplay(event.x, event.y);
 688         scenePeer.scrollEvent(type,
 689                 (type == AbstractEvents.MOUSEEVENT_HORIZONTAL_WHEEL) ? -SWTEvents.getWheelRotation(event) : 0,
 690                 (type == AbstractEvents.MOUSEEVENT_VERTICAL_WHEEL) ? -SWTEvents.getWheelRotation(event) : 0,
 691                 event.x, event.y,
 692                 los.x, los.y,
 693                 (event.stateMask & SWT.SHIFT) != 0,
 694                 (event.stateMask & SWT.CONTROL) != 0,
 695                 (event.stateMask & SWT.ALT) != 0,
 696                 (event.stateMask & SWT.COMMAND) != 0);
 697     }
 698 
 699     private void sendKeyEventToFX(final KeyEvent e, int type) {
 700         if (scenePeer == null /*|| !isFxEnabled()*/) {
 701             return;
 702         }
 703         int stateMask = e.stateMask;
 704         if (type == SWT.KeyDown) {
 705             if (e.keyCode == SWT.SHIFT) stateMask |= SWT.SHIFT;
 706             if (e.keyCode == SWT.CONTROL) stateMask |= SWT.CONTROL;
 707             if (e.keyCode == SWT.ALT) stateMask |= SWT.ALT;
 708             if (e.keyCode == SWT.COMMAND) stateMask |= SWT.COMMAND;
 709         } else {
 710             if (e.keyCode == SWT.SHIFT) stateMask &= ~SWT.SHIFT;
 711             if (e.keyCode == SWT.CONTROL) stateMask &= ~SWT.CONTROL;
 712             if (e.keyCode == SWT.ALT) stateMask &= ~SWT.ALT;
 713             if (e.keyCode == SWT.COMMAND) stateMask &= ~SWT.COMMAND;
 714         }
 715         int keyCode = SWTEvents.keyCodeToEmbedKeyCode(e.keyCode);
 716         scenePeer.keyEvent(
 717                 SWTEvents.keyIDToEmbedKeyType(type),
 718                 keyCode, new char[0],
 719                 SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
 720         if (e.character != '\0' && type == SWT.KeyDown) {
 721             char[] chars = new char[] { e.character };
 722             scenePeer.keyEvent(
 723                     AbstractEvents.KEYEVENT_TYPED,
 724                     e.keyCode, chars,
 725                     SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
 726         }
 727     }
 728 
 729     private void sendMenuEventToFX(MenuDetectEvent me) {
 730         if (scenePeer == null /*|| !isFxEnabled()*/) {
 731             return;
 732         }
 733         Point pt = toControl(me.x, me.y);
 734         scenePeer.menuEvent(pt.x, pt.y, me.x, me.y, false);
 735     }
 736 
 737     private void sendResizeEventToFX() {
 738 
 739         // force the panel to draw right away (avoid black rectangle)
 740         redraw();
 741         update();
 742 
 743         pWidth = getClientArea().width;
 744         pHeight = getClientArea().height;
 745 
 746         resizePixelBuffer(lastScaleFactor);
 747 
 748         if (scenePeer == null) {
 749             return;
 750         }
 751 
 752         stagePeer.setSize(pWidth, pHeight);
 753         scenePeer.setSize(pWidth, pHeight);
 754     }
 755 
 756     private void resizePixelBuffer(double newScaleFactor) {
 757         lastPixelsBuf = null;
 758         if ((pWidth <= 0) || (pHeight <= 0)) {
 759             pixelsBuf = null;
 760         } else {
 761             pixelsBuf = IntBuffer.allocate((int)Math.round(pWidth * newScaleFactor) *
 762                                            (int)Math.round(pHeight * newScaleFactor));
 763             // The bg color may show through on resize. See RT-34380.
 764             RGB rgb = getBackground().getRGB();
 765             Arrays.fill(pixelsBuf.array(), rgb.red << 16 | rgb.green << 8 | rgb.blue);
 766         }
 767     }
 768 
 769     private void sendFocusEventToFX(FocusEvent fe, boolean focused) {
 770         if ((stage == null) || (stagePeer == null)) {
 771             return;
 772         }
 773         int focusCause = (focused ?
 774                           AbstractEvents.FOCUSEVENT_ACTIVATED :
 775                           AbstractEvents.FOCUSEVENT_DEACTIVATED);
 776         stagePeer.setFocused(focused, focusCause);
 777     }
 778 
 779     private class HostContainer implements HostInterface {
 780 
 781         final FXCanvas fxCanvas = FXCanvas.this;
 782 
 783         @Override
 784         public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
 785             stagePeer = embeddedStage;
 786             if (stagePeer == null) {
 787                 return;
 788             }
 789             if (pWidth > 0 && pHeight > 0) {
 790                 stagePeer.setSize(pWidth, pHeight);
 791             }
 792             if (FXCanvas.this.isFocusControl()) {
 793                 stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
 794             }
 795             sendMoveEventToFX();
 796             sendResizeEventToFX();
 797         }
 798 
 799         TransferMode getTransferMode(int bits) {
 800             switch (bits) {
 801                 case DND.DROP_COPY:
 802                     return TransferMode.COPY;
 803                 case DND.DROP_MOVE:
 804                 case DND.DROP_TARGET_MOVE:
 805                     return TransferMode.MOVE;
 806                 case DND.DROP_LINK:
 807                     return TransferMode.LINK;
 808                 default:
 809                    return null;
 810             }
 811         }
 812 
 813         Set<TransferMode> getTransferModes(int bits) {
 814             Set<TransferMode> set = new HashSet<TransferMode>();
 815             if ((bits & DND.DROP_COPY) != 0) set.add(TransferMode.COPY);
 816             if ((bits & DND.DROP_MOVE) != 0) set.add(TransferMode.MOVE);
 817             if ((bits & DND.DROP_TARGET_MOVE) != 0) set.add(TransferMode.MOVE);
 818             if ((bits & DND.DROP_LINK) != 0) set.add(TransferMode.LINK);
 819             return set;
 820         }
 821 
 822         ImageData createImageData(Pixels pixels) {
 823             if (pixels == null) return null;
 824             int width = pixels.getWidth();
 825             int height = pixels.getHeight();
 826             int bpr = width * 4;
 827             int dataSize = bpr * height;
 828             byte[] buffer = new byte[dataSize];
 829             byte[] alphaData = new byte[width * height];
 830             if (pixels.getBytesPerComponent() == 1) {
 831                 // ByteBgraPre
 832                 ByteBuffer pixbuf = (ByteBuffer) pixels.getPixels();
 833                 for (int y = 0, offset = 0, alphaOffset = 0; y < height; y++) {
 834                     for (int x = 0; x < width; x++, offset += 4) {
 835                         byte b = pixbuf.get();
 836                         byte g = pixbuf.get();
 837                         byte r = pixbuf.get();
 838                         byte a = pixbuf.get();
 839                         // non premultiplied ?
 840                         alphaData[alphaOffset++] = a;
 841                         buffer[offset] = b;
 842                         buffer[offset + 1] = g;
 843                         buffer[offset + 2] = r;
 844                         buffer[offset + 3] = 0;// alpha
 845                     }
 846                 }
 847             } else if (pixels.getBytesPerComponent() == 4) {
 848                 // IntArgbPre
 849                 IntBuffer pixbuf = (IntBuffer) pixels.getPixels();
 850                 for (int y = 0, offset = 0, alphaOffset = 0; y < height; y++) {
 851                     for (int x = 0; x < width; x++, offset += 4) {
 852                         int pixel = pixbuf.get();
 853                         byte b = (byte) (pixel & 0xFF);
 854                         byte g = (byte) ((pixel >> 8) & 0xFF);
 855                         byte r = (byte) ((pixel >> 16) & 0xFF);
 856                         byte a = (byte) ((pixel >> 24) & 0xFF);
 857                         // non premultiplied ?
 858                         alphaData[alphaOffset++] = a;
 859                         buffer[offset] = b;
 860                         buffer[offset + 1] = g;
 861                         buffer[offset + 2] = r;
 862                         buffer[offset + 3] = 0;// alpha
 863                     }
 864                 }
 865             } else {
 866                 return null;
 867             }
 868             PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
 869             ImageData imageData = new ImageData(width, height, 32, palette, 4, buffer);
 870             imageData.alphaData = alphaData;
 871             return imageData;
 872         }
 873 
 874         // Consider using dragAction
 875         private DragSource createDragSource(final EmbeddedSceneDSInterface fxDragSource, TransferMode dragAction) {
 876             Transfer [] transfers = getTransferTypes(fxDragSource.getMimeTypes());
 877             if (transfers.length == 0) return null;
 878             int dragOperation = getDragActions(fxDragSource.getSupportedActions());
 879             final DragSource dragSource = new DragSource(FXCanvas.this, dragOperation);
 880             dragSource.setTransfer(transfers);
 881             dragSource.addDragListener(new DragSourceListener() {
 882                 public void dragFinished(org.eclipse.swt.dnd.DragSourceEvent event) {
 883                     dragSource.dispose();
 884                     fxDragSource.dragDropEnd(getTransferMode(event.detail));
 885                 }
 886                 public void dragSetData(org.eclipse.swt.dnd.DragSourceEvent event) {
 887                     Transfer [] transfers = dragSource.getTransfer();
 888                     for (int i=0; i<transfers.length; i++) {
 889                         if (transfers[i].isSupportedType(event.dataType)) {
 890                             String mime = getMime(transfers[i]);
 891                             if (mime != null) {
 892                                 event.doit = true;
 893                                 event.data = fxDragSource.getData(mime);
 894                                 if (event.data instanceof Pixels) {
 895                                     event.data = createImageData((Pixels)event.data);
 896                                 }
 897                                 return;
 898                             }
 899                         }
 900                         event.doit = false;
 901                     }
 902                 }
 903                 public void dragStart(org.eclipse.swt.dnd.DragSourceEvent event) {
 904                 }
 905             });
 906             return dragSource;
 907         }
 908 
 909         int getDragAction(TransferMode tm) {
 910             if (tm == null) return DND.DROP_NONE;
 911             switch (tm) {
 912                 case COPY: return DND.DROP_COPY;
 913                 case MOVE: return DND.DROP_MOVE;
 914                 case LINK: return DND.DROP_LINK;
 915                 default:
 916                     throw new IllegalArgumentException("Invalid transfer mode");
 917             }
 918         }
 919 
 920         int getDragActions(Set<TransferMode> set) {
 921             int result = 0;
 922             for (TransferMode mode : set) {
 923                 result |= getDragAction(mode);
 924             }
 925             return result;
 926         }
 927 
 928         Transfer getTransferType(String mime) {
 929             if (mime.equals("text/plain")) return TextTransfer.getInstance();
 930             if (mime.equals("text/rtf")) return RTFTransfer.getInstance();
 931             if (mime.equals("text/html")) return HTMLTransfer.getInstance();
 932             if (mime.equals("text/uri-list")) return URLTransfer.getInstance();
 933             if (mime.equals("application/x-java-rawimage")) return ImageTransfer.getInstance();
 934             if (mime.equals("application/x-java-file-list") || mime.equals("java.file-list")) {
 935                 return FileTransfer.getInstance();
 936             }
 937             return getCustomTransfer(mime);
 938         }
 939 
 940         Transfer [] getTransferTypes(String [] mimeTypes) {
 941             int count= 0;
 942             Transfer [] transfers = new Transfer [mimeTypes.length];
 943             for (int i=0; i<mimeTypes.length; i++) {
 944                 Transfer transfer = getTransferType(mimeTypes[i]);
 945                 if (transfer != null) transfers [count++] = transfer;
 946             }
 947             if (count != mimeTypes.length) {
 948                 Transfer [] newTransfers = new Transfer[count];
 949                 System.arraycopy(transfers, 0, newTransfers, 0, count);
 950                 transfers = newTransfers;
 951             }
 952             return transfers;
 953         }
 954 
 955         String getMime(Transfer transfer) {
 956             if (transfer.equals(TextTransfer.getInstance())) return "text/plain";
 957             if (transfer.equals(RTFTransfer.getInstance())) return "text/rtf"; ;
 958             if (transfer.equals( HTMLTransfer.getInstance())) return "text/html";
 959             if (transfer.equals(URLTransfer.getInstance())) return "text/uri-list";
 960             if (transfer.equals( ImageTransfer.getInstance())) return "application/x-java-rawimage";
 961             if (transfer.equals(FileTransfer.getInstance())) return "application/x-java-file-list";
 962             if (transfer instanceof CustomTransfer) return ((CustomTransfer)transfer).getMime();
 963             return null;
 964         }
 965 
 966         String [] getMimes(Transfer [] transfers, TransferData data) {
 967             int count= 0;
 968             String [] result = new String [transfers.length];
 969             for (int i=0; i<transfers.length; i++) {
 970                 if (transfers[i].isSupportedType(data)) {
 971                     result [count++] = getMime (transfers [i]);
 972                 }
 973             }
 974             if (count != result.length) {
 975                 String [] newResult = new String[count];
 976                 System.arraycopy(result, 0, newResult, 0, count);
 977                 result = newResult;
 978             }
 979             return result;
 980         }
 981 
 982         DropTarget createDropTarget(EmbeddedSceneInterface embeddedScene) {
 983             final DropTarget dropTarget = new DropTarget(FXCanvas.this, DND.DROP_COPY | DND.DROP_LINK | DND.DROP_MOVE);
 984             final EmbeddedSceneDTInterface fxDropTarget = embeddedScene.createDropTarget();
 985             dropTarget.setTransfer(getAllTransfers());
 986             dropTarget.addDropListener(new DropTargetListener() {
 987                 Object data;
 988                 // In SWT, the list of available types that the source can provide
 989                 // is part of the event.  FX queries this directly from the operating
 990                 // system and bypasses SWT.  This variable is commented out to remind
 991                 // us of this potential inconsistency.
 992                 //
 993                 //TransferData [] transferData;
 994                 TransferData currentTransferData;
 995                 boolean ignoreLeave;
 996                 int detail = DND.DROP_NONE, operations = DND.DROP_NONE;
 997                 EmbeddedSceneDSInterface fxDragSource = new EmbeddedSceneDSInterface() {
 998                     public Set<TransferMode> getSupportedActions() {
 999                         return getTransferModes(operations);
1000                     }
1001                     public Object getData(String mimeType) {
1002                         // NOTE: get the data for the requested mime type, not the default data
1003                         return data;
1004                     }
1005                     public String[] getMimeTypes() {
1006                         if (currentTransferData == null) return new String [0];
1007                         return getMimes(getAllTransfers(), currentTransferData);
1008                     }
1009                     public boolean isMimeTypeAvailable(String mimeType) {
1010                         String [] mimes = getMimeTypes();
1011                         for (int i=0; i<mimes.length; i++) {
1012                             if (mimes[i].equals(mimeType)) return true;
1013                         }
1014                         return false;
1015                     }
1016                     public void dragDropEnd(TransferMode performedAction) {
1017                         data = null;
1018                         //transferData = null;
1019                         currentTransferData = null;
1020                     }
1021                 };
1022                 public void dragEnter(DropTargetEvent event) {
1023                     ignoreLeave = false;
1024                     dropTarget.setTransfer(getAllTransfers());
1025                     detail = event.detail;
1026                     operations = event.operations;
1027                     dragOver (event, true, detail);
1028                 }
1029                 public void dragLeave(DropTargetEvent event) {
1030                     detail = operations = DND.DROP_NONE;
1031                     data = null;
1032                     //transferData = null;
1033                     currentTransferData = null;
1034                     getDisplay().asyncExec(() -> {
1035                         if (ignoreLeave) return;
1036                         fxDropTarget.handleDragLeave();
1037                     });
1038                 }
1039                 public void dragOperationChanged(DropTargetEvent event) {
1040                     detail = event.detail;
1041                     operations = event.operations;
1042                     dragOver(event, false, detail);
1043                 }
1044                 public void dragOver(DropTargetEvent event) {
1045                     operations = event.operations;
1046                     dragOver (event, false, detail);
1047                 }
1048                 public void dragOver(DropTargetEvent event, boolean enter, int detail) {
1049                     //transferData = event.dataTypes;
1050                     currentTransferData = event.currentDataType;
1051                     Point pt = toControl(event.x, event.y);
1052                     if (detail == DND.DROP_NONE) detail = DND.DROP_COPY;
1053                     TransferMode acceptedMode, recommendedMode = getTransferMode(detail);
1054                     if (enter) {
1055                         acceptedMode = fxDropTarget.handleDragEnter(pt.x, pt.y, event.x, event.y, recommendedMode, fxDragSource);
1056                     } else {
1057                         acceptedMode = fxDropTarget.handleDragOver(pt.x, pt.y, event.x, event.y, recommendedMode);
1058                     }
1059                     event.detail = getDragAction(acceptedMode);
1060                 }
1061                 public void drop(DropTargetEvent event) {
1062                     detail = event.detail;
1063                     operations = event.operations;
1064                     data = event.data;
1065                     //transferData = event.dataTypes;
1066                     currentTransferData = event.currentDataType;
1067                     Point pt = toControl(event.x, event.y);
1068                     TransferMode recommendedDropAction = getTransferMode(event.detail);
1069                     TransferMode acceptedMode = fxDropTarget.handleDragDrop(pt.x, pt.y, event.x, event.y, recommendedDropAction);
1070                     event.detail = getDragAction(acceptedMode);
1071                     data = null;
1072                     //transferData = null;
1073                     currentTransferData = null;
1074                 }
1075                 public void dropAccept(DropTargetEvent event) {
1076                     ignoreLeave = true;
1077                 }
1078             });
1079             return dropTarget;
1080         }
1081 
1082         @Override
1083         public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) {
1084             scenePeer = embeddedScene;
1085             if (scenePeer == null) {
1086                 return;
1087             }
1088             if (pWidth > 0 && pHeight > 0) {
1089                 scenePeer.setSize(pWidth, pHeight);
1090             }
1091             double scaleFactor = getScaleFactor();
1092             resizePixelBuffer(scaleFactor);
1093             lastScaleFactor = scaleFactor;
1094             scenePeer.setPixelScaleFactors((float)scaleFactor, (float)scaleFactor);
1095             scenePeer.setDragStartListener((fxDragSource, dragAction) -> {
1096                 Platform.runLater(() -> {
1097                     DragSource dragSource = createDragSource(fxDragSource, dragAction);
1098                     if (dragSource == null) {
1099                         fxDragSource.dragDropEnd(null);
1100                     } else {
1101                         updateDropTarget();
1102                         FXCanvas.this.notifyListeners(SWT.DragDetect, null);
1103                     }
1104                 });
1105             });
1106             //Force the old drop target to be disposed before creating a new one
1107             setDropTarget(null);
1108             setDropTarget(createDropTarget(embeddedScene));
1109         }
1110 
1111         @Override
1112         public boolean requestFocus() {
1113             Display.getDefault().asyncExec(() -> {
1114                 if (isDisposed()) return;
1115                 FXCanvas.this.forceFocus();
1116             });
1117             return true;
1118         }
1119 
1120         @Override
1121         public boolean traverseFocusOut(boolean bln) {
1122             // RT-18085: not implemented
1123             return true;
1124         }
1125 
1126         Object lock = new Object();
1127         boolean queued = false;
1128         public void repaint() {
1129             synchronized (lock) {
1130                 if (queued) return;
1131                 queued = true;
1132                 Display.getDefault().asyncExec(() -> {
1133                     try {
1134                         if (isDisposed()) return;
1135                         FXCanvas.this.redraw();
1136                     } finally {
1137                         synchronized (lock) {
1138                             queued = false;
1139                         }
1140                     }
1141                 });
1142             }
1143         }
1144 
1145         @Override
1146         public void setPreferredSize(int width, int height) {
1147             FXCanvas.this.pPreferredWidth = width;
1148             FXCanvas.this.pPreferredHeight = height;
1149             //FXCanvas.this.getShell().layout(new Control []{FXCanvas.this}, SWT.DEFER);
1150         }
1151 
1152         @Override
1153         public void setEnabled(boolean bln) {
1154             FXCanvas.this.setEnabled(bln);
1155         }
1156 
1157         @Override
1158         public void setCursor(CursorFrame cursorFrame) {
1159             FXCanvas.this.setCursor(getPlatformCursor(cursorFrame));
1160         }
1161 
1162         private org.eclipse.swt.graphics.Cursor getPlatformCursor(final CursorFrame cursorFrame) {
1163             /*
1164              * On the Mac, setting the cursor during drag and drop clears the move
1165              * and link indicators.  The fix is to set the default cursor for the
1166              * control (which is null) when the FX explicitly requests the default
1167              * cursor.  This will preserve the drag and drop indicators.
1168              */
1169             if (cursorFrame.getCursorType() == CursorType.DEFAULT) {
1170                 return null;
1171             }
1172             final org.eclipse.swt.graphics.Cursor cachedPlatformCursor =
1173                     cursorFrame.getPlatformCursor(org.eclipse.swt.graphics.Cursor.class);
1174             if (cachedPlatformCursor != null) {
1175                 // platform cursor already cached
1176                 return cachedPlatformCursor;
1177             }
1178 
1179             // platform cursor not cached yet
1180             final org.eclipse.swt.graphics.Cursor platformCursor =
1181                     SWTCursors.embedCursorToCursor(cursorFrame);
1182             cursorFrame.setPlatforCursor(org.eclipse.swt.graphics.Cursor.class, platformCursor);
1183 
1184             return platformCursor;
1185         }
1186 
1187         @Override
1188         public boolean grabFocus() {
1189             // RT-27949: not implemented
1190             return true;
1191         }
1192 
1193         @Override
1194         public void ungrabFocus() {
1195             // RT-27949: not implemented
1196         }
1197     }
1198 }