Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.embed.swt;
027
028import java.lang.reflect.Field;
029import java.nio.IntBuffer;
030
031import java.security.AccessController;
032import java.security.PrivilegedAction;
033import java.util.HashMap;
034import java.util.HashSet;
035import java.util.Map;
036import java.util.Set;
037
038import com.sun.glass.ui.Application;
039import com.sun.javafx.cursor.CursorFrame;
040import com.sun.javafx.cursor.CursorType;
041
042import javafx.application.Platform;
043import javafx.scene.Scene;
044import javafx.scene.input.TransferMode;
045
046import com.sun.javafx.application.PlatformImpl;
047import com.sun.javafx.embed.AbstractEvents;
048import com.sun.javafx.embed.EmbeddedSceneDragSourceInterface;
049import com.sun.javafx.embed.EmbeddedSceneDragStartListenerInterface;
050import com.sun.javafx.embed.EmbeddedSceneDropTargetInterface;
051import com.sun.javafx.embed.EmbeddedSceneInterface;
052import com.sun.javafx.embed.EmbeddedStageInterface;
053import com.sun.javafx.embed.HostInterface;
054import com.sun.javafx.stage.EmbeddedWindow;
055
056import org.eclipse.swt.dnd.DND;
057import org.eclipse.swt.dnd.DragSource;
058import org.eclipse.swt.dnd.DragSourceListener;
059import org.eclipse.swt.dnd.DropTarget;
060import org.eclipse.swt.dnd.DropTargetEvent;
061import org.eclipse.swt.dnd.DropTargetListener;
062import org.eclipse.swt.dnd.FileTransfer;
063import org.eclipse.swt.dnd.HTMLTransfer;
064import org.eclipse.swt.dnd.ImageTransfer;
065import org.eclipse.swt.dnd.RTFTransfer;
066import org.eclipse.swt.dnd.TextTransfer;
067import org.eclipse.swt.dnd.Transfer;
068import org.eclipse.swt.dnd.TransferData;
069import org.eclipse.swt.dnd.URLTransfer;
070import org.eclipse.swt.events.ControlEvent;
071import org.eclipse.swt.events.DisposeEvent;
072import org.eclipse.swt.events.DisposeListener;
073import org.eclipse.swt.events.FocusEvent;
074import org.eclipse.swt.events.KeyEvent;
075import org.eclipse.swt.events.KeyListener;
076import org.eclipse.swt.events.MenuDetectEvent;
077import org.eclipse.swt.events.MenuDetectListener;
078import org.eclipse.swt.events.MouseEvent;
079import org.eclipse.swt.events.MouseListener;
080import org.eclipse.swt.events.MouseMoveListener;
081import org.eclipse.swt.events.PaintEvent;
082import org.eclipse.swt.events.PaintListener;
083
084import org.eclipse.swt.widgets.Canvas;
085import org.eclipse.swt.widgets.Composite;
086import org.eclipse.swt.widgets.Display;
087
088import org.eclipse.swt.graphics.Image;
089import org.eclipse.swt.graphics.ImageData;
090import org.eclipse.swt.graphics.PaletteData;
091import org.eclipse.swt.graphics.Point;
092import org.eclipse.swt.graphics.Rectangle;
093
094import org.eclipse.swt.SWT;
095import org.eclipse.swt.events.ControlListener;
096import org.eclipse.swt.events.FocusListener;
097import org.eclipse.swt.events.MouseTrackListener;
098import org.eclipse.swt.events.MouseWheelListener;
099
100/**
101 * {@code FXCanvas} is a component to embed JavaFX content into
102 * SWT applications. The content to be displayed is specified
103 * with the {@link #setScene} method that accepts an instance of
104 * JavaFX {@code Scene}. After the scene is assigned, it gets
105 * repainted automatically. All the input and focus events are
106 * forwarded to the scene transparently to the developer.
107 * <p>
108 * Here is a typical pattern how {@code FXCanvas} can used:
109 * <pre>
110 *    public class Test {
111 *        private static Scene createScene() {
112 *            Group group = new Group();
113 *            Scene scene = new Scene(group);
114 *            Button button = new Button("JFX Button");
115 *            group.getChildren().add(button);
116 *            return scene;
117 *        }
118 *    
119 *        public static void main(String[] args) {
120 *            Display display = new Display();
121 *            Shell shell = new Shell(display);
122 *            shell.setLayout(new FillLayout());
123 *            FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
124 *            Scene scene = createScene();
125 *            canvas.setScene(scene);
126 *            shell.open();
127 *            while (!shell.isDisposed()) {
128 *                if (!display.readAndDispatch()) display.sleep();
129 *            }
130 *            display.dispose();
131 *        }
132 *    }
133 * </pre>
134 *
135 * 
136 */
137public class FXCanvas extends Canvas {
138
139    private HostContainer hostContainer;
140    private volatile EmbeddedWindow stage;
141    private volatile Scene scene;
142    private EmbeddedStageInterface stagePeer;
143    private EmbeddedSceneInterface scenePeer;
144
145    private int pWidth = 0;
146    private int pHeight = 0;
147    
148    private volatile int pPreferredWidth = -1;
149    private volatile int pPreferredHeight = -1;
150    
151    private IntBuffer pixelsBuf = null;
152    
153    static Transfer [] StandardTransfers = new Transfer [] {
154        TextTransfer.getInstance(),
155        RTFTransfer.getInstance(),
156        HTMLTransfer.getInstance(),
157        URLTransfer.getInstance(),
158        ImageTransfer.getInstance(),
159        FileTransfer.getInstance(),
160    };
161    static Transfer [] CustomTransfers = new Transfer [0];
162        
163    static Transfer [] getAllTransfers () {
164        Transfer [] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length];
165        System.arraycopy(StandardTransfers, 0, transfers, 0, StandardTransfers.length);
166        System.arraycopy(CustomTransfers, 0, transfers, StandardTransfers.length, CustomTransfers.length);
167        return transfers;
168    }
169    
170    static Transfer getCustomTransfer(String mime) {
171        for (int i=0; i<CustomTransfers.length; i++) {
172            if (((CustomTransfer)CustomTransfers[i]).getMime().equals(mime)) {
173                return CustomTransfers[i];
174            }
175        }
176        Transfer transfer = new CustomTransfer (mime, mime);
177        Transfer [] newCustom = new Transfer [CustomTransfers.length + 1];
178        System.arraycopy(CustomTransfers, 0, newCustom, 0, CustomTransfers.length);
179        newCustom[CustomTransfers.length] = transfer;
180        CustomTransfers = newCustom;
181        return transfer;
182    }
183    
184    /**
185     * @inheritDoc
186     */
187    public FXCanvas(Composite parent, int style) {
188        super(parent, style | SWT.NO_BACKGROUND);
189        initFx();
190        hostContainer = new HostContainer();
191        registerEventListeners();
192    }
193
194    private static void initFx() {
195        AccessController.doPrivileged(new PrivilegedAction<Void>() {
196            public Void run() {
197                System.setProperty("javafx.embed.isEventThread", "true");
198                return null;
199            }
200        });
201        Map map = Application.getDeviceDetails();
202        if (map == null) {
203            Application.setDeviceDetails(map = new HashMap());
204        }
205        if (map.get("javafx.embed.eventProc") == null) {
206            long eventProc = 0;
207            try {
208                Field field = Display.class.getDeclaredField("eventProc");
209                field.setAccessible(true);
210                if (field.getType() == int.class) {
211                    eventProc = field.getLong(Display.getDefault());
212                } else {
213                    if (field.getType() == long.class) {
214                        eventProc = field.getLong(Display.getDefault());
215                    }
216                }
217            } catch (Throwable th) {
218                //Fail silently
219            }
220            map.put("javafx.embed.eventProc", eventProc);
221        }
222        // Note that calling PlatformImpl.startup more than once is OK
223        PlatformImpl.startup(new Runnable() {
224            @Override
225            public void run() {
226            }
227        });
228    }
229    
230    /**
231     * {@inheritDoc}
232     */
233    public Point computeSize (int wHint, int hHint, boolean changed) {
234        checkWidget();
235        if (wHint == -1 && hHint == -1) {
236            if (pPreferredWidth != -1 && pPreferredHeight != -1) {
237                return new Point (pPreferredWidth, pPreferredHeight);
238            }
239        }
240        return super.computeSize(wHint, hHint, changed);
241    }
242    
243    /**
244     * Returns the JavaFX scene attached to this {@code FXCanvas}.
245     *
246     * @return the {@code Scene} attached to this {@code FXCanvas}
247     */
248    public Scene getScene() {
249        checkWidget();
250        return scene;
251    }
252
253    /**
254     * Attaches a {@code Scene} object to display in this {@code
255     * FXCanvas}. This method must called either on the JavaFX
256     * JavaFX application thread (which is the same as the SWT
257     * event dispatch thread).
258     *
259     * @param newScene a scene to display in this {@code FXCanvas}
260     *
261     * @see javafx.application.Platform#isFxApplicationThread()
262     */
263    public void setScene(final Scene newScene) {
264        checkWidget();
265
266        if ((stage == null) && (newScene != null)) {
267            stage = new EmbeddedWindow(hostContainer);
268            stage.show();
269        }
270        scene = newScene;
271        if (stage != null) {
272            stage.setScene(newScene);
273        }
274        if ((stage != null) && (newScene == null)) {
275            stage.hide();
276            stage = null;
277        }
278    }
279
280    // Note that removing the listeners is unnecessary
281    private void registerEventListeners() {
282        addDisposeListener(new DisposeListener() {
283            @Override
284            public void widgetDisposed(DisposeEvent de) {
285                FXCanvas.this.widgetDisposed(de);
286            }
287        });
288
289        addPaintListener(new PaintListener() {
290            @Override
291            public void paintControl(PaintEvent pe) {
292                FXCanvas.this.paintControl(pe);
293            }
294        });
295
296        addMouseListener(new MouseListener() {
297            @Override
298            public void mouseDoubleClick(MouseEvent me) {
299                // Clicks and double-clicks are handled in FX
300            }
301            @Override
302            public void mouseDown(MouseEvent me) {
303                FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_PRESSED);
304            }
305            @Override
306            public void mouseUp(MouseEvent me) {
307                FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_RELEASED);
308            }
309        });
310
311        addMouseMoveListener(new MouseMoveListener() {
312            @Override
313            public void mouseMove(MouseEvent me) {
314                if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
315                    FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_DRAGGED);
316                } else {
317                    FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
318                }
319            }
320        });
321
322        addMouseWheelListener(new MouseWheelListener() {
323            @Override
324            public void mouseScrolled(MouseEvent me) {
325                FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_WHEEL);
326            }
327        });
328
329        addMouseTrackListener(new MouseTrackListener() {
330            @Override
331            public void mouseEnter(MouseEvent me) {
332                FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_ENTERED);
333            }
334            @Override
335            public void mouseExit(MouseEvent me) {
336                FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_EXITED);
337            }
338            @Override
339            public void mouseHover(MouseEvent me) {
340                // No mouse hovering in FX
341            }
342        });
343
344        addControlListener(new ControlListener() {
345            @Override
346            public void controlMoved(ControlEvent ce) {
347                FXCanvas.this.sendMoveEventToFX();
348            }
349            @Override
350            public void controlResized(ControlEvent ce) {
351                FXCanvas.this.sendResizeEventToFX();
352            }
353        });
354
355        addFocusListener(new FocusListener() {
356            @Override
357            public void focusGained(FocusEvent fe) {
358                FXCanvas.this.sendFocusEventToFX(fe, true);
359            }
360            @Override
361            public void focusLost(FocusEvent fe) {
362                FXCanvas.this.sendFocusEventToFX(fe, false);
363            }
364        });
365
366        addKeyListener(new KeyListener() {
367            @Override
368            public void keyPressed(KeyEvent e) {
369                FXCanvas.this.sendKeyEventToFX(e, SWT.KeyDown);
370                
371            }
372            @Override
373            public void keyReleased(KeyEvent e) {
374                FXCanvas.this.sendKeyEventToFX(e, SWT.KeyUp);
375            }
376        });
377        
378        addMenuDetectListener(new MenuDetectListener() {
379            @Override
380            public void menuDetected(MenuDetectEvent e) {
381                FXCanvas.this.sendMenuEventToFX(e);
382            }
383        });
384    }
385
386    private void widgetDisposed(DisposeEvent de) {
387        if (stage != null) {
388            stage.hide();
389        }
390    }
391
392    int lastWidth, lastHeight;
393    IntBuffer lastPixelsBuf =  null;
394    private void paintControl(PaintEvent pe) {
395        if ((scenePeer == null) || (pixelsBuf == null)) {
396            return;
397        }
398
399        // if we can't get the pixels, draw the bits that were there before
400        IntBuffer buffer = pixelsBuf;
401        int width = pWidth, height = pHeight;
402        if (scenePeer.getPixels(pixelsBuf, pWidth, pHeight)) {
403            width = lastWidth = pWidth;
404            height = lastHeight = pHeight;
405            buffer = lastPixelsBuf = pixelsBuf;
406        } else {
407            if (lastPixelsBuf == null) return;
408            width = lastWidth;
409            height = lastHeight;
410            buffer = lastPixelsBuf;
411        }
412
413        // Consider optimizing this
414        ImageData imageData = null;
415        if ("win32".equals(SWT.getPlatform())) {
416            PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
417            int scanline = width * 4;
418            byte[] dstData = new byte[scanline * height];
419            int[] srcData = buffer.array();
420            int dp = 0, sp = 0;
421            for (int y = 0; y < height; y++) {
422                for (int x = 0; x < width; x++) {
423                    int p = srcData[sp++];
424                    dstData[dp++] = (byte) (p & 0xFF); //dst:blue
425                    dstData[dp++] = (byte)((p >> 8) & 0xFF); //dst:green
426                    dstData[dp++] = (byte)((p >> 16) & 0xFF); //dst:green
427                    dstData[dp++] = (byte)0x00; //alpha
428                }
429            }
430            /*ImageData*/ imageData = new ImageData(width, height, 32, palette, 4, dstData);
431        } else {
432            PaletteData palette = new PaletteData(0x00ff0000, 0x0000ff00, 0x000000ff);
433            /*ImageData*/  imageData = new ImageData(width, height, 32, palette);
434            imageData.setPixels(0, 0,width * height, buffer.array(), 0);
435        }
436
437        Image image = new Image(Display.getDefault(), imageData);
438        pe.gc.drawImage(image, 0, 0);
439        image.dispose();
440    }
441    
442    private void sendMoveEventToFX() {
443        if ((stagePeer == null) /*|| !isShowing()*/) {
444            return;
445        }
446        Rectangle rect = getClientArea();
447        Point los = toDisplay(rect.x, rect.y);
448        stagePeer.setLocation(los.x, los.y);
449    }
450    
451    private void sendMouseEventToFX(MouseEvent me, int embedMouseType) {
452        if (scenePeer == null) {
453            return;
454        }
455
456        Point los = toDisplay(me.x, me.y);
457        boolean primaryBtnDown = (me.stateMask & SWT.BUTTON1) != 0;
458        boolean middleBtnDown = (me.stateMask & SWT.BUTTON2) != 0;
459        boolean secondaryBtnDown = (me.stateMask & SWT.BUTTON3) != 0;
460        boolean shift = (me.stateMask & SWT.SHIFT) != 0;
461        boolean control = (me.stateMask & SWT.CONTROL) != 0;
462        boolean alt = (me.stateMask & SWT.ALT) != 0;
463        boolean meta = (me.stateMask & SWT.COMMAND) != 0;
464        switch (embedMouseType) {
465            case AbstractEvents.MOUSEEVENT_PRESSED:
466                primaryBtnDown |= me.button == 1;
467                middleBtnDown |= me.button == 2;
468                secondaryBtnDown |= me.button == 3;
469                break;
470            case AbstractEvents.MOUSEEVENT_RELEASED:
471                primaryBtnDown &= me.button != 1;
472                middleBtnDown &= me.button != 2;
473                secondaryBtnDown &= me.button != 3;
474                break;
475            default:
476                break;
477        }
478        scenePeer.mouseEvent(
479                embedMouseType,
480                SWTEvents.mouseButtonToEmbedMouseButton(me.button, me.stateMask),
481                primaryBtnDown, middleBtnDown, secondaryBtnDown,
482                me.count,
483                me.x, me.y,
484                los.x, los.y,
485                shift, control, alt, meta,
486                SWTEvents.getWheelRotation(me, embedMouseType), false);  // TODO: popup trigger
487    }
488    
489    private void sendKeyEventToFX(final KeyEvent e, int type) {
490        if (scenePeer == null /*|| !isFxEnabled()*/) {
491            return;
492        }
493        int stateMask = e.stateMask;
494        if (type == SWT.KeyDown) {
495            if (e.keyCode == SWT.SHIFT) stateMask |= SWT.SHIFT;
496            if (e.keyCode == SWT.CONTROL) stateMask |= SWT.CONTROL;
497            if (e.keyCode == SWT.ALT) stateMask |= SWT.ALT;
498            if (e.keyCode == SWT.COMMAND) stateMask |= SWT.COMMAND;
499        } else {
500            if (e.keyCode == SWT.SHIFT) stateMask &= ~SWT.SHIFT;
501            if (e.keyCode == SWT.CONTROL) stateMask &= ~SWT.CONTROL;
502            if (e.keyCode == SWT.ALT) stateMask &= ~SWT.ALT;
503            if (e.keyCode == SWT.COMMAND) stateMask &= ~SWT.COMMAND;
504        }
505        int keyCode = SWTEvents.keyCodeToEmbedKeyCode(e.keyCode);
506        scenePeer.keyEvent(
507                SWTEvents.keyIDToEmbedKeyType(type),
508                keyCode, new char[0],
509                SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
510        if (e.character != '\0' && type == SWT.KeyDown) {
511            char[] chars = new char[] { e.character };
512            scenePeer.keyEvent(
513                    AbstractEvents.KEYEVENT_TYPED,
514                    e.keyCode, chars,
515                    SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
516        }
517    }
518    
519    private void sendMenuEventToFX(MenuDetectEvent me) {
520        if (scenePeer == null /*|| !isFxEnabled()*/) {
521            return;
522        }
523        Point pt = toControl(me.x, me.y);
524        scenePeer.menuEvent(pt.x, pt.y, me.x, me.y, false);
525    }
526
527    private void sendResizeEventToFX() {
528        
529        // force the panel to draw right away (avoid black rectangle)
530        redraw();
531        update();
532        
533        pWidth = getClientArea().width;
534        pHeight = getClientArea().height;
535
536        if ((pWidth <= 0) || (pHeight <= 0)) {
537            pixelsBuf = lastPixelsBuf = null;
538        } else {
539            pixelsBuf = IntBuffer.allocate(pWidth * pHeight);
540        }
541
542        if (scenePeer == null) {
543            return;
544        }
545
546        stagePeer.setSize(pWidth, pHeight);
547        scenePeer.setSize(pWidth, pHeight);
548    }
549
550    private void sendFocusEventToFX(FocusEvent fe, boolean focused) {
551        if ((stage == null) || (stagePeer == null)) {
552            return;
553        }
554        int focusCause = (focused ?
555                          AbstractEvents.FOCUSEVENT_ACTIVATED :
556                          AbstractEvents.FOCUSEVENT_DEACTIVATED);
557        stagePeer.setFocused(focused, focusCause);
558    }
559
560    private class HostContainer implements HostInterface {
561
562        @Override
563        public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
564            stagePeer = embeddedStage;
565            if (stagePeer == null) {
566                return;
567            }
568            if (pWidth > 0 && pHeight > 0) {
569                stagePeer.setSize(pWidth, pHeight);
570            }
571            if (FXCanvas.this.isFocusControl()) {
572                stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
573            }
574            sendMoveEventToFX();
575            sendResizeEventToFX();
576        }
577
578        TransferMode getTransferMode(int bits) {
579            switch (bits) {
580                case DND.DROP_COPY:
581                    return TransferMode.COPY;
582                case DND.DROP_MOVE:
583                case DND.DROP_TARGET_MOVE:
584                    return TransferMode.MOVE;
585                case DND.DROP_LINK:
586                    return TransferMode.LINK;
587                default:
588                   return null;
589            }
590        }
591        
592        Set<TransferMode> getTransferModes(int bits) {
593            Set<TransferMode> set = new HashSet<TransferMode>();
594            if ((bits & DND.DROP_COPY) != 0) set.add(TransferMode.COPY);
595            if ((bits & DND.DROP_MOVE) != 0) set.add(TransferMode.MOVE);
596            if ((bits & DND.DROP_TARGET_MOVE) != 0) set.add(TransferMode.MOVE);
597            if ((bits & DND.DROP_LINK) != 0) set.add(TransferMode.LINK);
598            return set;
599        }
600
601        // Consider using dragAction
602        private DragSource createDragSource(final EmbeddedSceneDragSourceInterface fxDragSource, TransferMode dragAction) {
603            Transfer [] transfers = getTransferTypes(fxDragSource.getMimeTypes());
604            if (transfers.length == 0) return null;
605            int dragOperation = getDragActions(fxDragSource.getSupportedActions());
606            final DragSource dragSource = new DragSource(FXCanvas.this, dragOperation);
607            dragSource.setTransfer(transfers);
608            dragSource.addDragListener(new DragSourceListener() {
609                public void dragFinished(org.eclipse.swt.dnd.DragSourceEvent event) {
610                    dragSource.dispose();
611                    fxDragSource.dragDropEnd(getTransferMode(event.detail));
612                }
613                public void dragSetData(org.eclipse.swt.dnd.DragSourceEvent event) {
614                    Transfer [] transfers = dragSource.getTransfer();
615                    for (int i=0; i<transfers.length; i++) {
616                        if (transfers[i].isSupportedType(event.dataType)) {
617                            String mime = getMime(transfers[i]);
618                            if (mime != null) {
619                                event.doit = true;
620                                event.data = fxDragSource.getData(mime);
621                                return;
622                            }
623                        }
624                        event.doit = false;
625                    }
626                }
627                public void dragStart(org.eclipse.swt.dnd.DragSourceEvent event) {
628                }
629            });
630            return dragSource;
631        }
632
633        int getDragAction(TransferMode tm) {
634            if (tm == null) return DND.DROP_NONE;
635            switch (tm) {
636                case COPY: return DND.DROP_COPY;
637                case MOVE: return DND.DROP_MOVE;
638                case LINK: return DND.DROP_LINK;
639                default:
640                    throw new IllegalArgumentException("Invalid transfer mode");
641            }
642        }
643        
644        int getDragActions(Set<TransferMode> set) {
645            int result = 0;
646            for (TransferMode mode : set) {
647                result |= getDragAction(mode);
648            }
649            return result;
650        }
651        
652        Transfer getTransferType(String mime) {
653            if (mime.equals("text/plain")) return TextTransfer.getInstance();
654            if (mime.equals("text/rtf")) return RTFTransfer.getInstance();
655            if (mime.equals("text/html")) return HTMLTransfer.getInstance();
656            if (mime.equals("text/uri-list")) return URLTransfer.getInstance();
657            if (mime.equals("application/x-java-rawimage")) return ImageTransfer.getInstance();
658            if (mime.equals("application/x-java-file-list") || mime.equals("java.file-list")) {
659                return FileTransfer.getInstance();
660            }
661            return getCustomTransfer(mime);
662        }
663        
664        Transfer [] getTransferTypes(String [] mimeTypes) {
665            int count= 0;
666            Transfer [] transfers = new Transfer [mimeTypes.length];
667            for (int i=0; i<mimeTypes.length; i++) {
668                Transfer transfer = getTransferType(mimeTypes[i]);
669                if (transfer != null) transfers [count++] = transfer;
670            }
671            if (count != mimeTypes.length) {
672                Transfer [] newTransfers = new Transfer[count];
673                System.arraycopy(transfers, 0, newTransfers, 0, count);
674                transfers = newTransfers;
675            }
676            return transfers;
677        }
678        
679        String getMime(Transfer transfer) {
680            if (transfer.equals(TextTransfer.getInstance())) return "text/plain";
681            if (transfer.equals(RTFTransfer.getInstance())) return "text/rtf"; ;
682            if (transfer.equals( HTMLTransfer.getInstance())) return "text/html";
683            if (transfer.equals(URLTransfer.getInstance())) return "text/uri-list";
684            if (transfer.equals( ImageTransfer.getInstance())) return "application/x-java-rawimage";
685            if (transfer.equals(FileTransfer.getInstance())) return "application/x-java-file-list";
686            if (transfer instanceof CustomTransfer) return ((CustomTransfer)transfer).getMime();
687            return null;
688        }
689        
690        String [] getMimes(Transfer [] transfers, TransferData data) {
691            int count= 0;
692            String [] result = new String [transfers.length];
693            for (int i=0; i<transfers.length; i++) {
694                if (transfers[i].isSupportedType(data)) {
695                    result [count++] = getMime (transfers [i]);
696                }
697            }
698            if (count != result.length) {
699                String [] newResult = new String[count];
700                System.arraycopy(result, 0, newResult, 0, count);
701                result = newResult;
702            }
703            return result;
704        }
705
706        DropTarget createDropTarget(EmbeddedSceneInterface embeddedScene) {
707            final DropTarget dropTarget = new DropTarget(FXCanvas.this, DND.DROP_COPY | DND.DROP_LINK | DND.DROP_MOVE);
708            final EmbeddedSceneDropTargetInterface fxDropTarget = embeddedScene.createDropTarget();
709            dropTarget.setTransfer(getAllTransfers());
710            dropTarget.addDropListener(new DropTargetListener() {
711                Object data;
712                TransferData [] transferData;
713                TransferData currentTransferData;
714                boolean ignoreLeave;
715                int detail = DND.DROP_NONE, operations = DND.DROP_NONE;
716                EmbeddedSceneDragSourceInterface fxDragSource = new EmbeddedSceneDragSourceInterface() {
717                    public Set<TransferMode> getSupportedActions() {
718                        return getTransferModes(operations);
719                    }
720                    public Object getData(String mimeType) {
721                        // NOTE: get the data for the requested mime type, not the default data
722                        return data;
723                    }
724                    public String[] getMimeTypes() {
725                        if (currentTransferData == null) return new String [0];
726                        return getMimes(getAllTransfers(), currentTransferData);
727                    }
728                    public boolean isMimeTypeAvailable(String mimeType) {
729                        String [] mimes = getMimeTypes();
730                        for (int i=0; i<mimes.length; i++) {
731                            if (mimes[i].equals(mimeType)) return true;
732                        }
733                        return false;
734                    }
735                    public void dragDropEnd(TransferMode performedAction) {
736                        data = null;
737                        transferData = null;
738                        currentTransferData = null;
739                    }
740                };
741                public void dragEnter(DropTargetEvent event) {
742                    dropTarget.setTransfer(getAllTransfers());
743                    detail = event.detail;
744                    operations = event.operations;
745                    dragOver (event, true, detail);
746                }
747                public void dragLeave(DropTargetEvent event) {
748                    detail = operations = DND.DROP_NONE;
749                    data = null;
750                    transferData = null;
751                    currentTransferData = null;
752                    getDisplay().asyncExec(new Runnable () {
753                        public void run () {
754                            if (ignoreLeave) return;
755                            fxDropTarget.handleDragLeave();
756                        }
757                    });
758                }
759                public void dragOperationChanged(DropTargetEvent event) {
760                    detail = event.detail;
761                    operations = event.operations;
762                    dragOver(event, false, detail);
763                }
764                public void dragOver(DropTargetEvent event) {
765                    operations = event.operations;
766                    dragOver (event, false, detail);
767                }
768                public void dragOver(DropTargetEvent event, boolean enter, int detail) {
769                    transferData = event.dataTypes;
770                    currentTransferData = event.currentDataType;
771                    Point pt = toControl(event.x, event.y);
772                    if (detail == DND.DROP_NONE) detail = DND.DROP_COPY;
773                    TransferMode acceptedMode, recommendedMode = getTransferMode(detail);
774                    if (enter) {
775                        acceptedMode = fxDropTarget.handleDragEnter(pt.x, pt.y, event.x, event.y, recommendedMode, fxDragSource);
776                    } else {
777                        acceptedMode = fxDropTarget.handleDragOver(pt.x, pt.y, event.x, event.y, recommendedMode);
778                    }
779                    event.detail = getDragAction(acceptedMode);
780                }
781                public void drop(DropTargetEvent event) {
782                    detail = event.detail;
783                    operations = event.operations;
784                    data = event.data;
785                    transferData = event.dataTypes;
786                    currentTransferData = event.currentDataType;
787                    Point pt = toControl(event.x, event.y);
788                    TransferMode recommendedDropAction = getTransferMode(event.detail);
789                    TransferMode acceptedMode = fxDropTarget.handleDragDrop(pt.x, pt.y, event.x, event.y, recommendedDropAction);
790                    event.detail = getDragAction(acceptedMode);
791                    data = null;
792                    transferData = null;
793                    currentTransferData = null;
794                }
795                public void dropAccept(DropTargetEvent event) {
796                    ignoreLeave = true;
797                }
798            });
799            return dropTarget;
800        }
801
802        DropTarget dropTarget;
803        @Override
804        public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) {
805            scenePeer = embeddedScene;
806            if (scenePeer == null) {
807                return;
808            }
809            if (pWidth > 0 && pHeight > 0) {
810                scenePeer.setSize(pWidth, pHeight);
811            }
812            scenePeer.setDragStartListener(new EmbeddedSceneDragStartListenerInterface() {
813                @Override
814                public void dragStarted(final EmbeddedSceneDragSourceInterface fxDragSource, final TransferMode dragAction) {
815                    Platform.runLater(new Runnable ()  {
816                        public void run () {
817                            DragSource dragSource = createDragSource(fxDragSource, dragAction);
818                            if (dragSource == null) {
819                                fxDragSource.dragDropEnd(null);
820                            } else {
821                                if (dropTarget != null) dropTarget.setTransfer(getAllTransfers());
822                                FXCanvas.this.notifyListeners(SWT.DragDetect, null);
823                            }
824                        }
825                    });
826                }
827            });
828            if (dropTarget != null) dropTarget.dispose();
829            dropTarget = createDropTarget(embeddedScene);
830        }
831
832        @Override
833        public boolean requestFocus() {
834            Display.getDefault().asyncExec(new Runnable() {
835                @Override
836                public void run() {
837                    if (isDisposed()) return;
838                    FXCanvas.this.forceFocus();
839                }
840            });
841            return true;
842        }
843
844        @Override
845        public boolean traverseFocusOut(boolean bln) {
846            // RT-18085: not implemented
847            return true;
848        }
849
850        Object lock = new Object();
851        boolean queued = false;
852        public void repaint() {
853            synchronized (lock) {
854                if (queued) return;
855                queued = true;
856                Display.getDefault().asyncExec(new Runnable() {
857                    @Override
858                    public void run() {
859                        try {
860                            if (isDisposed()) return;
861                            FXCanvas.this.redraw();
862                            FXCanvas.this.sendMoveEventToFX();
863                        } finally {
864                            synchronized (lock) {
865                                queued = false;
866                            }
867                        }
868                    }
869                });
870            }
871        }
872
873        @Override
874        public void setPreferredSize(int width, int height) {
875            FXCanvas.this.pPreferredWidth = width;
876            FXCanvas.this.pPreferredHeight = height;
877            //FXCanvas.this.getShell().layout(new Control []{FXCanvas.this}, SWT.DEFER);
878        }
879
880        @Override
881        public void setEnabled(boolean bln) {
882            FXCanvas.this.setEnabled(bln);
883        }
884
885        @Override
886        public void setCursor(CursorFrame cursorFrame) {
887            FXCanvas.this.setCursor(getPlatformCursor(cursorFrame));
888        }
889
890        private org.eclipse.swt.graphics.Cursor getPlatformCursor(final CursorFrame cursorFrame) {
891            /*
892             * On the Mac, setting the cursor during drag and drop clears the move
893             * and link indicators.  The fix is to set the default cursor for the
894             * control (which is null) when the FX explicitly requests the default
895             * cursor.  This will preserve the drag and drop indicators.
896             */
897            if (cursorFrame.getCursorType() == CursorType.DEFAULT) {
898                return null;
899            }
900            final org.eclipse.swt.graphics.Cursor cachedPlatformCursor =
901                    cursorFrame.getPlatformCursor(org.eclipse.swt.graphics.Cursor.class);
902            if (cachedPlatformCursor != null) {
903                // platform cursor already cached
904                return cachedPlatformCursor;
905            }
906
907            // platform cursor not cached yet
908            final org.eclipse.swt.graphics.Cursor platformCursor = 
909                    SWTCursors.embedCursorToCursor(cursorFrame);
910            cursorFrame.setPlatforCursor(org.eclipse.swt.graphics.Cursor.class, platformCursor);
911
912            return platformCursor;
913        }
914
915        @Override
916        public boolean grabFocus() {
917            // RT-27949: not implemented
918            return true;
919        }
920
921        @Override
922        public void ungrabFocus() {
923            // RT-27949: not implemented
924        }
925    }
926}