/*
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.embed.swt;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.sun.glass.ui.Application;
import com.sun.glass.ui.Pixels;
import com.sun.javafx.cursor.CursorFrame;
import com.sun.javafx.cursor.CursorType;
import com.sun.javafx.embed.EmbeddedSceneDSInterface;
import com.sun.javafx.embed.HostDragStartListener;
import javafx.application.Platform;
import javafx.beans.NamedArg;
import javafx.scene.Scene;
import javafx.scene.input.TransferMode;
import com.sun.javafx.application.PlatformImpl;
import com.sun.javafx.embed.AbstractEvents;
import com.sun.javafx.embed.EmbeddedSceneDTInterface;
import com.sun.javafx.embed.EmbeddedSceneInterface;
import com.sun.javafx.embed.EmbeddedStageInterface;
import com.sun.javafx.embed.HostInterface;
import com.sun.javafx.stage.EmbeddedWindow;
import java.lang.reflect.Method;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.HTMLTransfer;
import org.eclipse.swt.dnd.ImageTransfer;
import org.eclipse.swt.dnd.RTFTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.dnd.URLTransfer;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.GCData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.MouseWheelListener;
/**
* {@code FXCanvas} is a component to embed JavaFX content into
* SWT applications. The content to be displayed is specified
* with the {@link #setScene} method that accepts an instance of
* JavaFX {@code Scene}. After the scene is assigned, it gets
* repainted automatically. All the input and focus events are
* forwarded to the scene transparently to the developer.
*
* Here is a typical pattern how {@code FXCanvas} can used:
*
* public class Test {
* private static Scene createScene() {
* Group group = new Group();
* Scene scene = new Scene(group);
* Button button = new Button("JFX Button");
* group.getChildren().add(button);
* return scene;
* }
*
* public static void main(String[] args) {
* Display display = new Display();
* Shell shell = new Shell(display);
* shell.setLayout(new FillLayout());
* FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
* Scene scene = createScene();
* canvas.setScene(scene);
* shell.open();
* while (!shell.isDisposed()) {
* if (!display.readAndDispatch()) display.sleep();
* }
* display.dispose();
* }
* }
*
*
*
* @since JavaFX 2.0
*/
public class FXCanvas extends Canvas {
private HostContainer hostContainer;
private volatile EmbeddedWindow stage;
private volatile Scene scene;
private EmbeddedStageInterface stagePeer;
private EmbeddedSceneInterface scenePeer;
private int pWidth = 0;
private int pHeight = 0;
private volatile int pPreferredWidth = -1;
private volatile int pPreferredHeight = -1;
private IntBuffer pixelsBuf = null;
// This filter runs when any widget is moved
Listener moveFilter = event -> {
// If a parent has moved, send a move event to FX
Control control = FXCanvas.this;
while (control != null) {
if (control == event.widget) {
sendMoveEventToFX();
break;
}
control = control.getParent();
};
};
private double getScaleFactor() {
if (SWT.getPlatform().equals("cocoa")) {
if (windowField == null || screenMethod == null || backingScaleFactorMethod == null) {
return 1.0;
}
try {
Object nsWindow = windowField.get(this.getShell());
Object nsScreen = screenMethod.invoke(nsWindow);
Object bsFactor = backingScaleFactorMethod.invoke(nsScreen);
return ((Double) bsFactor).doubleValue();
} catch (Exception e) {
// FAIL silently should the reflection fail
}
}
return 1.0;
}
private DropTarget dropTarget;
static Transfer [] StandardTransfers = new Transfer [] {
TextTransfer.getInstance(),
RTFTransfer.getInstance(),
HTMLTransfer.getInstance(),
URLTransfer.getInstance(),
ImageTransfer.getInstance(),
FileTransfer.getInstance(),
};
static Transfer [] CustomTransfers = new Transfer [0];
static Transfer [] getAllTransfers () {
Transfer [] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length];
System.arraycopy(StandardTransfers, 0, transfers, 0, StandardTransfers.length);
System.arraycopy(CustomTransfers, 0, transfers, StandardTransfers.length, CustomTransfers.length);
return transfers;
}
static Transfer getCustomTransfer(String mime) {
for (int i=0; i) () -> {
System.setProperty("javafx.embed.isEventThread", "true");
System.setProperty("glass.win.uiScale", "100%");
System.setProperty("glass.win.renderScale", "100%");
return null;
});
Map map = Application.getDeviceDetails();
if (map == null) {
Application.setDeviceDetails(map = new HashMap());
}
if (map.get("javafx.embed.eventProc") == null) {
long eventProc = 0;
try {
Field field = Display.class.getDeclaredField("eventProc");
field.setAccessible(true);
if (field.getType() == int.class) {
eventProc = field.getLong(Display.getDefault());
} else {
if (field.getType() == long.class) {
eventProc = field.getLong(Display.getDefault());
}
}
} catch (Throwable th) {
//Fail silently
}
map.put("javafx.embed.eventProc", eventProc);
}
// Note that calling PlatformImpl.startup more than once is OK
PlatformImpl.startup(() -> {
Application.GetApplication().setName(Display.getAppName());
});
}
static ArrayList targets = new ArrayList<>();
DropTarget getDropTarget() {
return dropTarget;
}
void setDropTarget(DropTarget newTarget) {
if (dropTarget != null) {
targets.remove(dropTarget);
dropTarget.dispose();
}
dropTarget = newTarget;
if (dropTarget != null) {
targets.add(dropTarget);
}
}
static void updateDropTarget() {
// Update all drop targets rather than just this target
//
// In order for a drop target to recognise a custom format,
// the format must be registered and the transfer type added
// to the list of transfers that the target accepts. This
// must happen before the drag and drop operations starts
// or the drop target will not accept the format. Therefore,
// set all transfers for all targets before any drag and drop
// operation starts
//
for (DropTarget target : targets) {
target.setTransfer(getAllTransfers());
}
}
/**
* {@inheritDoc}
*/
public Point computeSize (int wHint, int hHint, boolean changed) {
checkWidget();
if (wHint == -1 && hHint == -1) {
if (pPreferredWidth != -1 && pPreferredHeight != -1) {
return new Point (pPreferredWidth, pPreferredHeight);
}
}
return super.computeSize(wHint, hHint, changed);
}
/**
* Returns the JavaFX scene attached to this {@code FXCanvas}.
*
* @return the {@code Scene} attached to this {@code FXCanvas}
*/
public Scene getScene() {
checkWidget();
return scene;
}
/**
* Attaches a {@code Scene} object to display in this {@code
* FXCanvas}. This method must called either on the JavaFX
* JavaFX application thread (which is the same as the SWT
* event dispatch thread).
*
* @param newScene a scene to display in this {@code FXCanvas}
*
* @see javafx.application.Platform#isFxApplicationThread()
*/
public void setScene(final Scene newScene) {
checkWidget();
if ((stage == null) && (newScene != null)) {
stage = new EmbeddedWindow(hostContainer);
stage.show();
}
scene = newScene;
if (stage != null) {
stage.setScene(newScene);
}
if ((stage != null) && (newScene == null)) {
stage.hide();
stage = null;
}
}
// Note that removing the listeners is unnecessary
private void registerEventListeners() {
addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent de) {
Display display = getDisplay();
display.removeFilter(SWT.Move, moveFilter);
FXCanvas.this.widgetDisposed(de);
}
});
addPaintListener(pe -> {
FXCanvas.this.paintControl(pe);
});
addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent me) {
// Clicks and double-clicks are handled in FX
}
@Override
public void mouseDown(MouseEvent me) {
// FX only supports 3 buttons so don't send the event for other buttons
if (me.button > 3) return;
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_PRESSED);
}
@Override
public void mouseUp(MouseEvent me) {
// FX only supports 3 buttons so don't send the event for other buttons
if (me.button > 3) return;
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_RELEASED);
}
});
addMouseMoveListener(me -> {
if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
// FX only supports 3 buttons so don't send the event for other buttons
if ((me.stateMask & (SWT.BUTTON1 | SWT.BUTTON2 | SWT.BUTTON3)) != 0) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_DRAGGED);
} else {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
}
} else {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
}
});
addMouseWheelListener(me -> {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_WHEEL);
});
addMouseTrackListener(new MouseTrackListener() {
@Override
public void mouseEnter(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_ENTERED);
}
@Override
public void mouseExit(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_EXITED);
}
@Override
public void mouseHover(MouseEvent me) {
// No mouse hovering in FX
}
});
addControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent ce) {
FXCanvas.this.sendMoveEventToFX();
}
@Override
public void controlResized(ControlEvent ce) {
FXCanvas.this.sendResizeEventToFX();
}
});
addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent fe) {
FXCanvas.this.sendFocusEventToFX(fe, true);
}
@Override
public void focusLost(FocusEvent fe) {
FXCanvas.this.sendFocusEventToFX(fe, false);
}
});
addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
FXCanvas.this.sendKeyEventToFX(e, SWT.KeyDown);
}
@Override
public void keyReleased(KeyEvent e) {
FXCanvas.this.sendKeyEventToFX(e, SWT.KeyUp);
}
});
addMenuDetectListener(e -> {
Runnable r = () -> {
if (isDisposed()) return;
FXCanvas.this.sendMenuEventToFX(e);
};
// In SWT, MenuDetect comes before the equivalent mouse event
// On Mac, the order is MenuDetect, MouseDown, MouseUp. FX
// does not expect this order and when it gets the MouseDown,
// it closes the menu. The fix is to defer the MenuDetect
// notification until after the MouseDown is sent to FX.
if ("cocoa".equals(SWT.getPlatform())) {
getDisplay().asyncExec(r);
} else {
r.run();
}
});
}
private void widgetDisposed(DisposeEvent de) {
setDropTarget(null);
if (stage != null) {
stage.hide();
}
}
double lastScaleFactor = 1.0;
int lastWidth, lastHeight;
IntBuffer lastPixelsBuf = null;
private void paintControl(PaintEvent pe) {
if ((scenePeer == null) || (pixelsBuf == null)) {
return;
}
double scaleFactor = getScaleFactor();
if (lastScaleFactor != scaleFactor) {
resizePixelBuffer(scaleFactor);
lastScaleFactor = scaleFactor;
scenePeer.setPixelScaleFactors((float)scaleFactor, (float)scaleFactor);
}
// if we can't get the pixels, draw the bits that were there before
IntBuffer buffer = pixelsBuf;
int width = pWidth, height = pHeight;
if (scenePeer.getPixels(pixelsBuf, pWidth, pHeight)) {
width = lastWidth = pWidth;
height = lastHeight = pHeight;
buffer = lastPixelsBuf = pixelsBuf;
} else {
if (lastPixelsBuf == null) return;
width = lastWidth;
height = lastHeight;
buffer = lastPixelsBuf;
}
width = (int)Math.round(width * scaleFactor);
height = (int)Math.round(height * scaleFactor);
// Consider optimizing this
ImageData imageData = null;
if ("win32".equals(SWT.getPlatform())) {
PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
int scanline = width * 4;
byte[] dstData = new byte[scanline * height];
int[] srcData = buffer.array();
int dp = 0, sp = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int p = srcData[sp++];
dstData[dp++] = (byte) (p & 0xFF); //dst:blue
dstData[dp++] = (byte)((p >> 8) & 0xFF); //dst:green
dstData[dp++] = (byte)((p >> 16) & 0xFF); //dst:green
dstData[dp++] = (byte)0x00; //alpha
}
}
/*ImageData*/ imageData = new ImageData(width, height, 32, palette, 4, dstData);
} else {
if (width * height > buffer.array().length) {
// We shouldn't be here...
System.err.println("FXCanvas.paintControl: scale mismatch!");
return;
}
PaletteData palette = new PaletteData(0x00ff0000, 0x0000ff00, 0x000000ff);
/*ImageData*/ imageData = new ImageData(width, height, 32, palette);
imageData.setPixels(0, 0,width * height, buffer.array(), 0);
}
Image image = new Image(Display.getDefault(), imageData);
pe.gc.drawImage(image, 0, 0, width, height, 0, 0, pWidth, pHeight);
image.dispose();
}
private void sendMoveEventToFX() {
if ((stagePeer == null) /*|| !isShowing()*/) {
return;
}
Rectangle rect = getClientArea();
Point los = toDisplay(rect.x, rect.y);
stagePeer.setLocation(los.x, los.y);
}
private void sendMouseEventToFX(MouseEvent me, int embedMouseType) {
if (scenePeer == null) {
return;
}
Point los = toDisplay(me.x, me.y);
boolean primaryBtnDown = (me.stateMask & SWT.BUTTON1) != 0;
boolean middleBtnDown = (me.stateMask & SWT.BUTTON2) != 0;
boolean secondaryBtnDown = (me.stateMask & SWT.BUTTON3) != 0;
boolean shift = (me.stateMask & SWT.SHIFT) != 0;
boolean control = (me.stateMask & SWT.CONTROL) != 0;
boolean alt = (me.stateMask & SWT.ALT) != 0;
boolean meta = (me.stateMask & SWT.COMMAND) != 0;
int button = me.button;
switch (embedMouseType) {
case AbstractEvents.MOUSEEVENT_PRESSED:
primaryBtnDown |= me.button == 1;
middleBtnDown |= me.button == 2;
secondaryBtnDown |= me.button == 3;
break;
case AbstractEvents.MOUSEEVENT_RELEASED:
primaryBtnDown &= me.button != 1;
middleBtnDown &= me.button != 2;
secondaryBtnDown &= me.button != 3;
break;
case AbstractEvents.MOUSEEVENT_CLICKED:
// Don't send click events to FX, as they are generated in Scene
return;
case AbstractEvents.MOUSEEVENT_MOVED:
case AbstractEvents.MOUSEEVENT_DRAGGED:
case AbstractEvents.MOUSEEVENT_ENTERED:
case AbstractEvents.MOUSEEVENT_EXITED:
// If this event was the result of mouse movement and has no
// button associated with it, then we look at the state to
// determine which button to report
if (button == 0) {
if ((me.stateMask & SWT.BUTTON1) != 0) {
button = 1;
} else if ((me.stateMask & SWT.BUTTON2) != 0) {
button = 2;
} else if ((me.stateMask & SWT.BUTTON3) != 0) {
button = 3;
}
}
break;
default:
break;
}
scenePeer.mouseEvent(
embedMouseType,
SWTEvents.mouseButtonToEmbedMouseButton(button, me.stateMask),
primaryBtnDown, middleBtnDown, secondaryBtnDown,
me.x, me.y,
los.x, los.y,
shift, control, alt, meta,
SWTEvents.getWheelRotation(me, embedMouseType),
false); // RT-32990: popup trigger not implemented
}
private void sendKeyEventToFX(final KeyEvent e, int type) {
if (scenePeer == null /*|| !isFxEnabled()*/) {
return;
}
int stateMask = e.stateMask;
if (type == SWT.KeyDown) {
if (e.keyCode == SWT.SHIFT) stateMask |= SWT.SHIFT;
if (e.keyCode == SWT.CONTROL) stateMask |= SWT.CONTROL;
if (e.keyCode == SWT.ALT) stateMask |= SWT.ALT;
if (e.keyCode == SWT.COMMAND) stateMask |= SWT.COMMAND;
} else {
if (e.keyCode == SWT.SHIFT) stateMask &= ~SWT.SHIFT;
if (e.keyCode == SWT.CONTROL) stateMask &= ~SWT.CONTROL;
if (e.keyCode == SWT.ALT) stateMask &= ~SWT.ALT;
if (e.keyCode == SWT.COMMAND) stateMask &= ~SWT.COMMAND;
}
int keyCode = SWTEvents.keyCodeToEmbedKeyCode(e.keyCode);
scenePeer.keyEvent(
SWTEvents.keyIDToEmbedKeyType(type),
keyCode, new char[0],
SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
if (e.character != '\0' && type == SWT.KeyDown) {
char[] chars = new char[] { e.character };
scenePeer.keyEvent(
AbstractEvents.KEYEVENT_TYPED,
e.keyCode, chars,
SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
}
}
private void sendMenuEventToFX(MenuDetectEvent me) {
if (scenePeer == null /*|| !isFxEnabled()*/) {
return;
}
Point pt = toControl(me.x, me.y);
scenePeer.menuEvent(pt.x, pt.y, me.x, me.y, false);
}
private void sendResizeEventToFX() {
// force the panel to draw right away (avoid black rectangle)
redraw();
update();
pWidth = getClientArea().width;
pHeight = getClientArea().height;
resizePixelBuffer(lastScaleFactor);
if (scenePeer == null) {
return;
}
stagePeer.setSize(pWidth, pHeight);
scenePeer.setSize(pWidth, pHeight);
}
private void resizePixelBuffer(double newScaleFactor) {
lastPixelsBuf = null;
if ((pWidth <= 0) || (pHeight <= 0)) {
pixelsBuf = null;
} else {
pixelsBuf = IntBuffer.allocate((int)Math.round(pWidth * newScaleFactor) *
(int)Math.round(pHeight * newScaleFactor));
// The bg color may show through on resize. See RT-34380.
RGB rgb = getBackground().getRGB();
Arrays.fill(pixelsBuf.array(), rgb.red << 16 | rgb.green << 8 | rgb.blue);
}
}
private void sendFocusEventToFX(FocusEvent fe, boolean focused) {
if ((stage == null) || (stagePeer == null)) {
return;
}
int focusCause = (focused ?
AbstractEvents.FOCUSEVENT_ACTIVATED :
AbstractEvents.FOCUSEVENT_DEACTIVATED);
stagePeer.setFocused(focused, focusCause);
}
private class HostContainer implements HostInterface {
@Override
public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
stagePeer = embeddedStage;
if (stagePeer == null) {
return;
}
if (pWidth > 0 && pHeight > 0) {
stagePeer.setSize(pWidth, pHeight);
}
if (FXCanvas.this.isFocusControl()) {
stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
}
sendMoveEventToFX();
sendResizeEventToFX();
}
TransferMode getTransferMode(int bits) {
switch (bits) {
case DND.DROP_COPY:
return TransferMode.COPY;
case DND.DROP_MOVE:
case DND.DROP_TARGET_MOVE:
return TransferMode.MOVE;
case DND.DROP_LINK:
return TransferMode.LINK;
default:
return null;
}
}
Set getTransferModes(int bits) {
Set set = new HashSet();
if ((bits & DND.DROP_COPY) != 0) set.add(TransferMode.COPY);
if ((bits & DND.DROP_MOVE) != 0) set.add(TransferMode.MOVE);
if ((bits & DND.DROP_TARGET_MOVE) != 0) set.add(TransferMode.MOVE);
if ((bits & DND.DROP_LINK) != 0) set.add(TransferMode.LINK);
return set;
}
ImageData createImageData(Pixels pixels) {
if (pixels == null) return null;
int width = pixels.getWidth();
int height = pixels.getHeight();
int bpr = width * 4;
int dataSize = bpr * height;
byte[] buffer = new byte[dataSize];
byte[] alphaData = new byte[width * height];
if (pixels.getBytesPerComponent() == 1) {
// ByteBgraPre
ByteBuffer pixbuf = (ByteBuffer) pixels.getPixels();
for (int y = 0, offset = 0, alphaOffset = 0; y < height; y++) {
for (int x = 0; x < width; x++, offset += 4) {
byte b = pixbuf.get();
byte g = pixbuf.get();
byte r = pixbuf.get();
byte a = pixbuf.get();
// non premultiplied ?
alphaData[alphaOffset++] = a;
buffer[offset] = b;
buffer[offset + 1] = g;
buffer[offset + 2] = r;
buffer[offset + 3] = 0;// alpha
}
}
} else if (pixels.getBytesPerComponent() == 4) {
// IntArgbPre
IntBuffer pixbuf = (IntBuffer) pixels.getPixels();
for (int y = 0, offset = 0, alphaOffset = 0; y < height; y++) {
for (int x = 0; x < width; x++, offset += 4) {
int pixel = pixbuf.get();
byte b = (byte) (pixel & 0xFF);
byte g = (byte) ((pixel >> 8) & 0xFF);
byte r = (byte) ((pixel >> 16) & 0xFF);
byte a = (byte) ((pixel >> 24) & 0xFF);
// non premultiplied ?
alphaData[alphaOffset++] = a;
buffer[offset] = b;
buffer[offset + 1] = g;
buffer[offset + 2] = r;
buffer[offset + 3] = 0;// alpha
}
}
} else {
return null;
}
PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
ImageData imageData = new ImageData(width, height, 32, palette, 4, buffer);
imageData.alphaData = alphaData;
return imageData;
}
// Consider using dragAction
private DragSource createDragSource(final EmbeddedSceneDSInterface fxDragSource, TransferMode dragAction) {
Transfer [] transfers = getTransferTypes(fxDragSource.getMimeTypes());
if (transfers.length == 0) return null;
int dragOperation = getDragActions(fxDragSource.getSupportedActions());
final DragSource dragSource = new DragSource(FXCanvas.this, dragOperation);
dragSource.setTransfer(transfers);
dragSource.addDragListener(new DragSourceListener() {
public void dragFinished(org.eclipse.swt.dnd.DragSourceEvent event) {
dragSource.dispose();
fxDragSource.dragDropEnd(getTransferMode(event.detail));
}
public void dragSetData(org.eclipse.swt.dnd.DragSourceEvent event) {
Transfer [] transfers = dragSource.getTransfer();
for (int i=0; i set) {
int result = 0;
for (TransferMode mode : set) {
result |= getDragAction(mode);
}
return result;
}
Transfer getTransferType(String mime) {
if (mime.equals("text/plain")) return TextTransfer.getInstance();
if (mime.equals("text/rtf")) return RTFTransfer.getInstance();
if (mime.equals("text/html")) return HTMLTransfer.getInstance();
if (mime.equals("text/uri-list")) return URLTransfer.getInstance();
if (mime.equals("application/x-java-rawimage")) return ImageTransfer.getInstance();
if (mime.equals("application/x-java-file-list") || mime.equals("java.file-list")) {
return FileTransfer.getInstance();
}
return getCustomTransfer(mime);
}
Transfer [] getTransferTypes(String [] mimeTypes) {
int count= 0;
Transfer [] transfers = new Transfer [mimeTypes.length];
for (int i=0; i getSupportedActions() {
return getTransferModes(operations);
}
public Object getData(String mimeType) {
// NOTE: get the data for the requested mime type, not the default data
return data;
}
public String[] getMimeTypes() {
if (currentTransferData == null) return new String [0];
return getMimes(getAllTransfers(), currentTransferData);
}
public boolean isMimeTypeAvailable(String mimeType) {
String [] mimes = getMimeTypes();
for (int i=0; i {
if (ignoreLeave) return;
fxDropTarget.handleDragLeave();
});
}
public void dragOperationChanged(DropTargetEvent event) {
detail = event.detail;
operations = event.operations;
dragOver(event, false, detail);
}
public void dragOver(DropTargetEvent event) {
operations = event.operations;
dragOver (event, false, detail);
}
public void dragOver(DropTargetEvent event, boolean enter, int detail) {
//transferData = event.dataTypes;
currentTransferData = event.currentDataType;
Point pt = toControl(event.x, event.y);
if (detail == DND.DROP_NONE) detail = DND.DROP_COPY;
TransferMode acceptedMode, recommendedMode = getTransferMode(detail);
if (enter) {
acceptedMode = fxDropTarget.handleDragEnter(pt.x, pt.y, event.x, event.y, recommendedMode, fxDragSource);
} else {
acceptedMode = fxDropTarget.handleDragOver(pt.x, pt.y, event.x, event.y, recommendedMode);
}
event.detail = getDragAction(acceptedMode);
}
public void drop(DropTargetEvent event) {
detail = event.detail;
operations = event.operations;
data = event.data;
//transferData = event.dataTypes;
currentTransferData = event.currentDataType;
Point pt = toControl(event.x, event.y);
TransferMode recommendedDropAction = getTransferMode(event.detail);
TransferMode acceptedMode = fxDropTarget.handleDragDrop(pt.x, pt.y, event.x, event.y, recommendedDropAction);
event.detail = getDragAction(acceptedMode);
data = null;
//transferData = null;
currentTransferData = null;
}
public void dropAccept(DropTargetEvent event) {
ignoreLeave = true;
}
});
return dropTarget;
}
@Override
public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) {
scenePeer = embeddedScene;
if (scenePeer == null) {
return;
}
if (pWidth > 0 && pHeight > 0) {
scenePeer.setSize(pWidth, pHeight);
}
double scaleFactor = getScaleFactor();
resizePixelBuffer(scaleFactor);
lastScaleFactor = scaleFactor;
scenePeer.setPixelScaleFactors((float)scaleFactor, (float)scaleFactor);
scenePeer.setDragStartListener((fxDragSource, dragAction) -> {
Platform.runLater(() -> {
DragSource dragSource = createDragSource(fxDragSource, dragAction);
if (dragSource == null) {
fxDragSource.dragDropEnd(null);
} else {
updateDropTarget();
FXCanvas.this.notifyListeners(SWT.DragDetect, null);
}
});
});
//Force the old drop target to be disposed before creating a new one
setDropTarget(null);
setDropTarget(createDropTarget(embeddedScene));
}
@Override
public boolean requestFocus() {
Display.getDefault().asyncExec(() -> {
if (isDisposed()) return;
FXCanvas.this.forceFocus();
});
return true;
}
@Override
public boolean traverseFocusOut(boolean bln) {
// RT-18085: not implemented
return true;
}
Object lock = new Object();
boolean queued = false;
public void repaint() {
synchronized (lock) {
if (queued) return;
queued = true;
Display.getDefault().asyncExec(() -> {
try {
if (isDisposed()) return;
FXCanvas.this.redraw();
} finally {
synchronized (lock) {
queued = false;
}
}
});
}
}
@Override
public void setPreferredSize(int width, int height) {
FXCanvas.this.pPreferredWidth = width;
FXCanvas.this.pPreferredHeight = height;
//FXCanvas.this.getShell().layout(new Control []{FXCanvas.this}, SWT.DEFER);
}
@Override
public void setEnabled(boolean bln) {
FXCanvas.this.setEnabled(bln);
}
@Override
public void setCursor(CursorFrame cursorFrame) {
FXCanvas.this.setCursor(getPlatformCursor(cursorFrame));
}
private org.eclipse.swt.graphics.Cursor getPlatformCursor(final CursorFrame cursorFrame) {
/*
* On the Mac, setting the cursor during drag and drop clears the move
* and link indicators. The fix is to set the default cursor for the
* control (which is null) when the FX explicitly requests the default
* cursor. This will preserve the drag and drop indicators.
*/
if (cursorFrame.getCursorType() == CursorType.DEFAULT) {
return null;
}
final org.eclipse.swt.graphics.Cursor cachedPlatformCursor =
cursorFrame.getPlatformCursor(org.eclipse.swt.graphics.Cursor.class);
if (cachedPlatformCursor != null) {
// platform cursor already cached
return cachedPlatformCursor;
}
// platform cursor not cached yet
final org.eclipse.swt.graphics.Cursor platformCursor =
SWTCursors.embedCursorToCursor(cursorFrame);
cursorFrame.setPlatforCursor(org.eclipse.swt.graphics.Cursor.class, platformCursor);
return platformCursor;
}
@Override
public boolean grabFocus() {
// RT-27949: not implemented
return true;
}
@Override
public void ungrabFocus() {
// RT-27949: not implemented
}
}
}