package sun.lwawt.macosx;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.accessibility.*;
import java.util.Map;
import java.util.concurrent.Callable;
import sun.awt.AWTAccessor;
import sun.awt.dnd.*;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformWindow;
public final class CDragSourceContextPeer extends SunDragSourceContextPeer {
private static final CDragSourceContextPeer fInstance = new CDragSourceContextPeer(null);
private Image fDragImage;
private CImage fDragCImage;
private Point fDragImageOffset;
private static Component hoveringComponent = null;
private static double fMaxImageSize = 128.0;
static {
String propValue = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("apple.awt.dnd.defaultDragImageSize"));
if (propValue != null) {
try {
double value = Double.parseDouble(propValue);
if (value > 0) {
fMaxImageSize = value;
}
} catch(NumberFormatException e) {}
}
}
private CDragSourceContextPeer(DragGestureEvent dge) {
super(dge);
}
public static CDragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
fInstance.setTrigger(dge);
return fInstance;
}
public void startDrag(DragSourceContext dsc, Cursor cursor, Image dragImage, Point dragImageOffset) throws InvalidDnDOperationException {
fDragImage = dragImage;
fDragImageOffset = dragImageOffset;
super.startDrag(dsc, cursor, dragImage, dragImageOffset);
}
protected void startDrag(Transferable transferable, long[] formats, Map<Long, DataFlavor> formatMap) {
DragGestureEvent trigger = getTrigger();
InputEvent triggerEvent = trigger.getTriggerEvent();
Point dragOrigin = new Point(trigger.getDragOrigin());
@SuppressWarnings("deprecation")
int extModifiers = (triggerEvent.getModifiers() | triggerEvent.getModifiersEx());
long timestamp = triggerEvent.getWhen();
int clickCount = ((triggerEvent instanceof MouseEvent) ? (((MouseEvent) triggerEvent).getClickCount()) : 1);
Component component = trigger.getComponent();
Point loc = component.getLocation();
Component rootComponent = component;
while (!(rootComponent instanceof Window)) {
dragOrigin.translate(loc.x, loc.y);
rootComponent = rootComponent.getParent();
loc = rootComponent.getLocation();
}
if (fDragImage == null)
this.setDefaultDragImage(component);
Point dragImageOffset;
if (fDragImage != null) {
try {
fDragCImage = CImage.getCreator().createFromImageImmediately(fDragImage);
} catch(Exception e) {
throw new InvalidDnDOperationException("Drag image can not be created.");
}
if (fDragCImage == null) {
throw new InvalidDnDOperationException("Drag image is not ready.");
}
dragImageOffset = fDragImageOffset;
} else {
fDragCImage = null;
dragImageOffset = new Point(0, 0);
}
try {
LWComponentPeer<?, ?> peer = AWTAccessor.getComponentAccessor()
.getPeer(rootComponent);
PlatformWindow platformWindow = peer.getPlatformWindow();
long nativeViewPtr = CPlatformWindow.getNativeViewPtr(platformWindow);
if (nativeViewPtr == 0L) throw new InvalidDnDOperationException("Unsupported platform window implementation");
final long nativeDragSource = createNativeDragSource(component, nativeViewPtr, transferable, triggerEvent,
(int) (dragOrigin.getX()), (int) (dragOrigin.getY()), extModifiers,
clickCount, timestamp, fDragCImage != null ? fDragCImage.ptr : 0L, dragImageOffset.x, dragImageOffset.y,
getDragSourceContext().getSourceActions(), formats, formatMap);
if (nativeDragSource == 0)
throw new InvalidDnDOperationException("");
setNativeContext(nativeDragSource);
}
catch (Exception e) {
throw new InvalidDnDOperationException("failed to create native peer: " + e);
}
SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(transferable);
CCursorManager.getInstance().setCursor(getCursor());
try {
Runnable dragRunnable = () -> {
final long nativeDragSource = getNativeContext();
try {
doDragging(nativeDragSource);
} catch (Exception e) {
e.printStackTrace();
} finally {
releaseNativeDragSource(nativeDragSource);
fDragImage = null;
if (fDragCImage != null) {
fDragCImage.dispose();
fDragCImage = null;
}
}
};
new Thread(null, dragRunnable, "Drag", 0, false).start();
} catch (Exception e) {
final long nativeDragSource = getNativeContext();
setNativeContext(0);
releaseNativeDragSource(nativeDragSource);
SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(null);
throw new InvalidDnDOperationException("failed to start dragging thread: " + e);
}
}
private void setDefaultDragImage(Component component) {
boolean handled = false;
if (component.isLightweight()) {
if (component instanceof JTextComponent) {
this.setDefaultDragImage((JTextComponent) component);
handled = true;
} else if (component instanceof JTree) {
this.setDefaultDragImage((JTree) component);
handled = true;
} else if (component instanceof JTable) {
this.setDefaultDragImage((JTable) component);
handled = true;
} else if (component instanceof JList) {
this.setDefaultDragImage((JList) component);
handled = true;
}
}
if (handled == false)
this.setDefaultDragImage();
}
@SuppressWarnings("deprecation")
private void setDefaultDragImage(JTextComponent component) {
DragGestureEvent trigger = getTrigger();
int selectionStart = component.getSelectionStart();
int selectionEnd = component.getSelectionEnd();
boolean handled = false;
int index = component.viewToModel(trigger.getDragOrigin());
if ((selectionStart < selectionEnd) && (index >= selectionStart) && (index <= selectionEnd)) {
try {
Rectangle selectionStartBounds = component.modelToView(selectionStart);
Rectangle selectionEndBounds = component.modelToView(selectionEnd);
Rectangle selectionBounds = null;
if (selectionStartBounds.y == selectionEndBounds.y) {
selectionBounds = new Rectangle(selectionStartBounds.x, selectionStartBounds.y,
selectionEndBounds.x - selectionStartBounds.x + selectionEndBounds.width,
selectionEndBounds.y - selectionStartBounds.y + selectionEndBounds.height);
}
else {
AccessibleContext ctx = component.getAccessibleContext();
AccessibleText at = (AccessibleText) ctx;
selectionBounds = component.modelToView(selectionStart);
for (int i = selectionStart + 1; i <= selectionEnd; i++) {
Rectangle charBounds = at.getCharacterBounds(i);
if (charBounds != null) {
selectionBounds.add(charBounds);
}
}
}
this.setOutlineDragImage(selectionBounds);
handled = true;
}
catch (BadLocationException exc) {
}
}
if (handled == false)
this.setDefaultDragImage();
}
private void setDefaultDragImage(JTree component) {
Rectangle selectedOutline = null;
int[] selectedRows = component.getSelectionRows();
for (int i=0; i<selectedRows.length; i++) {
Rectangle r = component.getRowBounds(selectedRows[i]);
if (selectedOutline == null)
selectedOutline = r;
else
selectedOutline.add(r);
}
if (selectedOutline != null) {
this.setOutlineDragImage(selectedOutline);
} else {
this.setDefaultDragImage();
}
}
private void setDefaultDragImage(JTable component) {
Rectangle selectedOutline = null;
int[] selectedRows = component.getSelectedRows();
int[] selectedColumns = component.getSelectedColumns();
for (int row=0; row<selectedRows.length; row++) {
for (int col=0; col<selectedColumns.length; col++) {
Rectangle r = component.getCellRect(selectedRows[row], selectedColumns[col], true);
if (selectedOutline == null)
selectedOutline = r;
else
selectedOutline.add(r);
}
}
if (selectedOutline != null) {
this.setOutlineDragImage(selectedOutline);
} else {
this.setDefaultDragImage();
}
}
private void setDefaultDragImage(JList<?> component) {
Rectangle selectedOutline = null;
int[] selectedIndices = component.getSelectedIndices();
if (selectedIndices.length > 0)
selectedOutline = component.getCellBounds(selectedIndices[0], selectedIndices[selectedIndices.length-1]);
if (selectedOutline != null) {
this.setOutlineDragImage(selectedOutline);
} else {
this.setDefaultDragImage();
}
}
private void setDefaultDragImage() {
DragGestureEvent trigger = this.getTrigger();
Component comp = trigger.getComponent();
setOutlineDragImage(new Rectangle(0, 0, comp.getWidth(), comp.getHeight()), true);
}
private void setOutlineDragImage(Rectangle outline) {
setOutlineDragImage(outline, false);
}
private void setOutlineDragImage(Rectangle outline, Boolean shouldScale) {
int width = (int)outline.getWidth();
int height = (int)outline.getHeight();
double scale = 1.0;
if (shouldScale) {
final int area = width * height;
final int maxArea = (int)(fMaxImageSize * fMaxImageSize);
if (area > maxArea) {
scale = (double)area / (double)maxArea;
width /= scale;
height /= scale;
}
}
if (width <=0) width = 1;
if (height <=0) height = 1;
DragGestureEvent trigger = this.getTrigger();
Component comp = trigger.getComponent();
Point compOffset = comp.getLocation();
if (comp instanceof JComponent) {
Rectangle visibleBounds = ((JComponent) comp).getVisibleRect();
Rectangle clipedOutline = outline.intersection(visibleBounds);
if (clipedOutline.isEmpty() == false)
outline = clipedOutline;
outline.translate(compOffset.x, compOffset.y);
}
GraphicsConfiguration config = comp.getGraphicsConfiguration();
BufferedImage dragImage = config.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
Color paint = Color.gray;
BasicStroke stroke = new BasicStroke(2.0f);
int halfLineWidth = (int) (stroke.getLineWidth() + 1) / 2;
Graphics2D g2 = (Graphics2D) dragImage.getGraphics();
g2.setPaint(paint);
g2.setStroke(stroke);
g2.drawRect(halfLineWidth, halfLineWidth, width - 2 * halfLineWidth - 1, height - 2 * halfLineWidth - 1);
g2.dispose();
fDragImage = dragImage;
Point dragOrigin = trigger.getDragOrigin();
Point dragImageOffset = new Point(outline.x - dragOrigin.x, outline.y - dragOrigin.y);
if (comp instanceof JComponent) {
dragImageOffset.translate(-compOffset.x, -compOffset.y);
}
if (shouldScale) {
dragImageOffset.x /= scale;
dragImageOffset.y /= scale;
}
fDragImageOffset = dragImageOffset;
}
private void dragMouseMoved(final int targetActions,
final int modifiers,
final int x, final int y) {
try {
Component componentAt = LWCToolkit.invokeAndWait(
new Callable<Component>() {
@Override
public Component call() {
LWWindowPeer mouseEventComponent = LWWindowPeer.getWindowUnderCursor();
if (mouseEventComponent == null) {
return null;
}
Component root = SwingUtilities.getRoot(mouseEventComponent.getTarget());
if (root == null) {
return null;
}
Point rootLocation = root.getLocationOnScreen();
return getDropTargetAt(root, x - rootLocation.x, y - rootLocation.y);
}
}, getComponent());
if(componentAt != hoveringComponent) {
if(hoveringComponent != null) {
dragExit(x, y);
}
if(componentAt != null) {
dragEnter(targetActions, modifiers, x, y);
}
hoveringComponent = componentAt;
}
postDragSourceDragEvent(targetActions, modifiers, x, y,
DISPATCH_MOUSE_MOVED);
} catch (Exception e) {
throw new InvalidDnDOperationException("Failed to handle DragMouseMoved event");
}
}
private static Component getDropTargetAt(Component root, int x, int y) {
if (!root.contains(x, y) || !root.isEnabled() || !root.isVisible()) {
return null;
}
if (root.getDropTarget() != null && root.getDropTarget().isActive()) {
return root;
}
if (root instanceof Container) {
for (Component comp : ((Container) root).getComponents()) {
Point loc = comp.getLocation();
Component dropTarget = getDropTargetAt(comp, x - loc.x, y - loc.y);
if (dropTarget != null) {
return dropTarget;
}
}
}
return null;
}
private void resetHovering() {
hoveringComponent = null;
}
@Override
protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) {
CCursorManager.getInstance().setCursor(c);
}
private native long createNativeDragSource(Component component, long nativePeer, Transferable transferable,
InputEvent triggerEvent, int dragPosX, int dragPosY, int extModifiers, int clickCount, long timestamp,
long nsDragImagePtr, int dragImageOffsetX, int dragImageOffsetY,
int sourceActions, long[] formats, Map<Long, DataFlavor> formatMap);
private native void doDragging(long nativeDragSource);
private native void releaseNativeDragSource(long nativeDragSource);
}