blob: da677620e806e8574923dddb60b181239bc60200 [file] [log] [blame]
/*
* Copyright (c) 2011, 2014, 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.
*/
//#define DND_DEBUG TRUE
#import "java_awt_dnd_DnDConstants.h"
#import <Cocoa/Cocoa.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import "AWTEvent.h"
#import "AWTView.h"
#import "CDataTransferer.h"
#import "CDropTarget.h"
#import "CDragSource.h"
#import "DnDUtilities.h"
#import "ThreadUtilities.h"
// When sIsJavaDragging is true Java drag gesture has been recognized and a drag is/has been initialized.
// We must stop posting MouseEvent.MOUSE_DRAGGED events for the duration of the drag or all hell will break
// loose in shared code - tracking state going haywire.
static BOOL sIsJavaDragging;
@interface NSEvent(AWTAdditions)
+ (void)javaDraggingBegin;
+ (void)javaDraggingEnd;
@end
@implementation NSEvent(AWTAdditions)
+ (void)javaDraggingBegin
{
sIsJavaDragging = YES;
}
+ (void)javaDraggingEnd
{
// sIsJavaDragging is reset on mouseDown as well.
sIsJavaDragging = NO;
}
@end
JNF_CLASS_CACHE(DataTransfererClass, "sun/awt/datatransfer/DataTransferer");
JNF_CLASS_CACHE(CDragSourceContextPeerClass, "sun/lwawt/macosx/CDragSourceContextPeer");
JNF_CLASS_CACHE(CImageClass, "sun/lwawt/macosx/CImage");
static NSDragOperation sDragOperation;
static NSPoint sDraggingLocation;
static BOOL sNeedsEnter;
@interface CDragSource ()
// Updates from the destination to the source
- (void) postDragEnter;
- (void) postDragExit;
// Utility
- (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint) point;
@end
@implementation CDragSource
- (id) init:(jobject)jDragSourceContextPeer
component:(jobject)jComponent
control:(id)control
transferable:(jobject)jTransferable
triggerEvent:(jobject)jTrigger
dragPosX:(jint)dragPosX
dragPosY:(jint)dragPosY
modifiers:(jint)extModifiers
clickCount:(jint)clickCount
timeStamp:(jlong)timeStamp
dragImage:(jobject)jDragImage
dragImageOffsetX:(jint)jDragImageOffsetX
dragImageOffsetY:(jint)jDragImageOffsetY
sourceActions:(jint)jSourceActions
formats:(jlongArray)jFormats
formatMap:(jobject)jFormatMap
{
self = [super init];
DLog2(@"[CDragSource init]: %@\n", self);
fView = nil;
fComponent = nil;
// Construct the object if we have a valid model for it:
if (control != nil) {
JNIEnv *env = [ThreadUtilities getJNIEnv];
fComponent = JNFNewGlobalRef(env, jComponent);
fDragSourceContextPeer = JNFNewGlobalRef(env, jDragSourceContextPeer);
fTransferable = JNFNewGlobalRef(env, jTransferable);
fTriggerEvent = JNFNewGlobalRef(env, jTrigger);
if (jDragImage) {
JNF_MEMBER_CACHE(nsImagePtr, CImageClass, "ptr", "J");
jlong imgPtr = JNFGetLongField(env, jDragImage, nsImagePtr);
fDragImage = (NSImage*) jlong_to_ptr(imgPtr); // Double-casting prevents compiler 'd$|//
[fDragImage retain];
}
fDragImageOffset = NSMakePoint(jDragImageOffsetX, jDragImageOffsetY);
fSourceActions = jSourceActions;
fFormats = JNFNewGlobalRef(env, jFormats);
fFormatMap = JNFNewGlobalRef(env, jFormatMap);
fTriggerEventTimeStamp = timeStamp;
fDragPos = NSMakePoint(dragPosX, dragPosY);
fClickCount = clickCount;
fModifiers = extModifiers;
// Set this object as a dragging source:
fView = [(AWTView *) control retain];
[fView setDragSource:self];
// Let AWTEvent know Java drag is getting underway:
[NSEvent javaDraggingBegin];
}
else {
[self release];
self = nil;
}
return self;
}
- (void)removeFromView:(JNIEnv *)env
{
DLog2(@"[CDragSource removeFromView]: %@\n", self);
// Remove this dragging source from the view:
[((AWTView *) fView) setDragSource:nil];
// Clean up JNI refs
if (fComponent != NULL) {
JNFDeleteGlobalRef(env, fComponent);
fComponent = NULL;
}
if (fDragSourceContextPeer != NULL) {
JNFDeleteGlobalRef(env, fDragSourceContextPeer);
fDragSourceContextPeer = NULL;
}
if (fTransferable != NULL) {
JNFDeleteGlobalRef(env, fTransferable);
fTransferable = NULL;
}
if (fTriggerEvent != NULL) {
JNFDeleteGlobalRef(env, fTriggerEvent);
fTriggerEvent = NULL;
}
if (fFormats != NULL) {
JNFDeleteGlobalRef(env, fFormats);
fFormats = NULL;
}
if (fFormatMap != NULL) {
JNFDeleteGlobalRef(env, fFormatMap);
fFormatMap = NULL;
}
CFRelease(self); // GC
}
- (void)dealloc
{
DLog2(@"[CDragSource dealloc]: %@\n", self);
// Delete or release local data:
[fView release];
fView = nil;
[fDragImage release];
fDragImage = nil;
[super dealloc];
}
//- (void)finalize { [super finalize]; }
// Appropriated from Windows' awt_DataTransferer.cpp:
//
// * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value.
//
- (jobject)dataTransferer:(JNIEnv*)env
{
JNF_STATIC_MEMBER_CACHE(getInstanceMethod, DataTransfererClass, "getInstance", "()Lsun/awt/datatransfer/DataTransferer;");
return JNFCallStaticObjectMethod(env, getInstanceMethod);
}
// Appropriated from Windows' awt_DataTransferer.cpp:
//
// * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value.
//
- (jbyteArray)convertData:(jlong)format
{
JNIEnv* env = [ThreadUtilities getJNIEnv];
jobject transferer = [self dataTransferer:env];
jbyteArray data = nil;
if (transferer != NULL) {
JNF_MEMBER_CACHE(convertDataMethod, DataTransfererClass, "convertData", "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B");
data = JNFCallObjectMethod(env, transferer, convertDataMethod, fComponent, fTransferable, format, fFormatMap, (jboolean) TRUE);
}
return data;
}
// Encodes a byte array of zero-terminated filenames into an NSArray of NSStrings representing them.
// Borrowed and adapted from awt_DataTransferer.c, convertFileType().
- (id)getFileList:(jbyte *)jbytes dataLength:(jsize)jbytesLength
{
jsize strings = 0;
jsize i;
// Get number of filenames while making sure to skip over empty strings.
for (i = 1; i < jbytesLength; i++) {
if (jbytes[i] == '\0' && jbytes[i - 1] != '\0')
strings++;
}
// Create the file list to return:
NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:strings];
for (i = 0; i < jbytesLength; i++) {
char* start = (char *) &jbytes[i];
// Skip over empty strings:
if (start[0] == '\0') {
continue;
}
// Update the position marker:
i += strlen(start);
// Add this filename to the file list:
NSMutableString* fileName = [NSMutableString stringWithUTF8String:start];
// Decompose the filename
CFStringNormalize((CFMutableStringRef)fileName, kCFStringNormalizationFormD);
[fileList addObject:fileName];
}
// 03-01-09 Note: keep this around for debugging.
// return [NSArray arrayWithObjects:@"/tmp/foo1", @"/tmp/foo2", nil];
return ([fileList count] > 0 ? fileList : nil);
}
// Set up the pasteboard for dragging:
- (BOOL)declareTypesToPasteboard:(NSPasteboard *)pb withEnv:(JNIEnv *) env {
// 9-20-02 Note: leave this here for debugging:
//[pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: self];
//return TRUE;
// Get byte array elements:
jboolean isCopy;
jlong* jformats = (*env)->GetLongArrayElements(env, fFormats, &isCopy);
if (jformats == nil)
return FALSE;
// Allocate storage arrays for dragging types to register with the pasteboard:
jsize formatsLength = (*env)->GetArrayLength(env, fFormats);
NSMutableArray* pbTypes = [[NSMutableArray alloc] initWithCapacity:formatsLength];
// And assume there are no NS-type data: [Radar 3065621]
// This is to be able to drop transferables containing only a serialized object flavor, e.g.:
// "JAVA_DATAFLAVOR:application/x-java-serialized-object; class=java.awt.Label".
BOOL hasNSTypeData = false;
// Collect all supported types in a pasteboard format into the storage arrays:
jsize i;
for (i = 0; i < formatsLength; i++) {
jlong jformat = jformats[i];
if (jformat >= 0) {
NSString* type = formatForIndex(jformat);
// Add element type to the storage array.
if (type != nil) {
if ([type hasPrefix:@"JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref;"] == false) {
[pbTypes addObject:type];
// This is a good approximation if not perfect. A conclusive search would
// have to be done matching all defined strings in AppKit's commonStrings.h.
hasNSTypeData = [type hasPrefix:@"NS"] || [type hasPrefix:@"NeXT"] || [type hasPrefix:@"public."];
}
}
}
}
// 1-16-03 Note: [Radar 3065621]
// When TransferHandler is used with Swing components it puts only a type like this on the pasteboard:
// "JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref; class=java.lang.String"
// And there's similar type for serialized object only transferables.
// Since our drop targets aren't trained for arbitrary data types yet we need to fake an empty string
// which will cause drop target handlers to fire.
// KCH - 3550405 If the drag is between Swing components, formatsLength == 0, so expand the check.
// Also, use a custom format rather than NSString, since that will prevent random views from accepting the drag
if (hasNSTypeData == false && formatsLength >= 0) {
[pbTypes addObject:[DnDUtilities javaPboardType]];
}
(*env)->ReleaseLongArrayElements(env, fFormats, jformats, JNI_ABORT);
// Declare pasteboard types. If the types array is empty we still want to declare them
// as otherwise an old set of types/data would remain on the pasteboard.
NSUInteger typesCount = [pbTypes count];
[pb declareTypes:pbTypes owner: self];
// KCH - Lame conversion bug between Cocoa and Carbon drag types
// If I provide the filenames _right now_, NSFilenamesPboardType is properly converted to CoreDrag flavors
// If I try to wait until pasteboard:provideDataForType:, the conversion won't happen
// and pasteboard:provideDataForType: won't even get called! (unless I go over a Cocoa app)
if ([pbTypes containsObject:NSFilenamesPboardType]) {
[self pasteboard:pb provideDataForType:NSFilenamesPboardType];
}
[pbTypes release];
return typesCount > 0 ? TRUE : FALSE;
}
// This is an NSPasteboard callback. In declareTypesToPasteboard:withEnv:, we only declared the types
// When the AppKit DnD system actually needs the data, this method will be invoked.
// Note that if the transfer is handled entirely from Swing (as in a local-vm drag), this method may never be called.
- (void)pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type {
AWT_ASSERT_APPKIT_THREAD;
// 9-20-02 Note: leave this here for debugging:
//[pb setString: @"Hello, World!" forType: NSStringPboardType];
// return;
// Set up Java environment:
JNIEnv* env = [ThreadUtilities getJNIEnv];
id pbData = nil;
// Collect data in a pasteboard format:
jlong jformat = indexForFormat(type);
if (jformat >= 0) {
// Convert DataTransfer data to a Java byte array:
// Note that this will eventually call getTransferData()
jbyteArray jdata = [self convertData:jformat];
if (jdata != nil) {
jboolean isCopy;
jsize jdataLength = (*env)->GetArrayLength(env, jdata);
jbyte* jbytedata = (*env)->GetByteArrayElements(env, jdata, &isCopy);
if (jdataLength > 0 && jbytedata != nil) {
// Get element data to the storage array. For NSFilenamesPboardType type we use
// an NSArray-type data - NSData-type data would cause a crash.
if (type != nil) {
pbData = ([type isEqualTo:NSFilenamesPboardType]) ?
[self getFileList:jbytedata dataLength:jdataLength] :
[NSData dataWithBytes:jbytedata length:jdataLength];
}
}
(*env)->ReleaseByteArrayElements(env, jdata, jbytedata, JNI_ABORT);
(*env)->DeleteLocalRef(env, jdata);
}
}
// If we are the custom type that matches local-vm drags, set an empty NSData
if ( (pbData == nil) && ([type isEqualTo:[DnDUtilities javaPboardType]]) ) {
pbData = [NSData dataWithBytes:"" length:1];
}
// Add pasteboard data for the type:
// Remember, NSFilenamesPboardType's data is NSArray (property list), not NSData!
// We must use proper pb accessor depending on the data type.
if ([pbData isKindOfClass:[NSArray class]])
[pb setPropertyList:pbData forType:type];
else
[pb setData:pbData forType:type];
}
- (void)validateDragImage
{
// Make a small blank image if we don't have a drag image:
if (fDragImage == nil) {
// 9-30-02 Note: keep this around for debugging:
fDragImage = [[NSImage alloc] initWithSize:NSMakeSize(21, 21)];
NSSize imageSize = [fDragImage size];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:imageSize.width pixelsHigh:imageSize.height bitsPerSample:8 samplesPerPixel:4
hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:32];
[fDragImage addRepresentation:imageRep];
fDragImageOffset = NSMakePoint(0, 0);
[imageRep release];
}
}
- (NSEvent*)nsDragEvent:(BOOL)isDrag
{
// Get NSView for the drag source:
NSWindow* window = [fView window];
NSInteger windowNumber = [window windowNumber];
NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithWindow:window];
// Convert mouse coordinates to NS:
NSPoint eventLocation = [fView convertPoint:NSMakePoint(fDragPos.x, fDragPos.y) toView:nil];
eventLocation.y = [[fView window] frame].size.height - eventLocation.y;
// Convert fTriggerEventTimeStamp to NS - AWTEvent.h defines UTC(nsEvent) as ((jlong)[event timestamp] * 1000):
NSTimeInterval timeStamp = fTriggerEventTimeStamp / 1000;
// Convert fModifiers (extModifiers) to NS:
NSEventType mouseButtons = 0;
float pressure = 0.0;
if (isDrag) {
mouseButtons = (NSEventType) [DnDUtilities mapJavaExtModifiersToNSMouseDownButtons:fModifiers];
pressure = 1.0;
} else {
mouseButtons = (NSEventType) [DnDUtilities mapJavaExtModifiersToNSMouseUpButtons:fModifiers];
}
// Convert fModifiers (extModifiers) to NS:
NSUInteger modifiers = JavaModifiersToNsKeyModifiers(fModifiers, TRUE);
// Just a dummy value ...
NSInteger eventNumber = 0;
// Make a native autoreleased dragging event:
NSEvent* dragEvent = [NSEvent mouseEventWithType:mouseButtons location:eventLocation
modifierFlags:modifiers timestamp:timeStamp windowNumber:windowNumber context:graphicsContext
eventNumber:eventNumber clickCount:fClickCount pressure:pressure];
return dragEvent;
}
- (void)doDrag
{
AWT_ASSERT_APPKIT_THREAD;
DLog2(@"[CDragSource doDrag]: %@\n", self);
// Set up Java environment:
JNIEnv *env = [ThreadUtilities getJNIEnv];
// Set up the pasteboard:
NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard];
[self declareTypesToPasteboard:pb withEnv:env];
// Make a native autoreleased NS dragging event:
NSEvent *dragEvent = [self nsDragEvent:YES];
// Get NSView for the drag source:
NSView *view = fView;
// Make sure we have a valid drag image:
[self validateDragImage];
NSImage* dragImage = fDragImage;
// Get drag origin and offset:
NSPoint dragOrigin = [dragEvent locationInWindow];
dragOrigin.x += fDragImageOffset.x;
dragOrigin.y -= fDragImageOffset.y + [dragImage size].height;
// Drag offset values don't seem to matter:
NSSize dragOffset = NSMakeSize(0, 0);
// These variables should be set based on the transferable:
BOOL isFileDrag = FALSE;
BOOL fileDragPromises = FALSE;
DLog(@"[CDragSource drag]: calling dragImage/File:");
DLog3(@" - drag origin: %f, %f", fDragPos.x, fDragPos.y);
DLog5(@" - drag image: %f, %f (%f x %f)", fDragImageOffset.x, fDragImageOffset.y, [dragImage size].width, [dragImage size].height);
DLog3(@" - event point (window) %f, %f", [dragEvent locationInWindow].x, [dragEvent locationInWindow].y);
DLog3(@" - drag point (view) %f, %f", dragOrigin.x, dragOrigin.y);
// Set up the fDragKeyModifier, so we know if the operation has changed
// Set up the fDragMouseModifier, so we can |= it later (since CoreDrag doesn't tell us mouse states during a drag)
fDragKeyModifiers = [DnDUtilities extractJavaExtKeyModifiersFromJavaExtModifiers:fModifiers];
fDragMouseModifiers = [DnDUtilities extractJavaExtMouseModifiersFromJavaExtModifiers:fModifiers];
sNeedsEnter = YES;
@try {
// Data dragging:
if (isFileDrag == FALSE) {
[view dragImage:dragImage at:dragOrigin offset:dragOffset event:dragEvent pasteboard:pb source:view slideBack:YES];
} else if (fileDragPromises == FALSE) {
// File dragging:
NSLog(@"[CDragSource drag]: file dragging is unsupported.");
NSString* fileName = nil; // This should be set based on the transferable.
NSRect fileLocationRect = NSMakeRect(0, 0, 0, 0); // This should be set based on the filename.
BOOL success = [view dragFile:fileName fromRect:fileLocationRect slideBack:YES event:dragEvent];
if (success == TRUE) { // One would erase dragged file if this was a move operation.
}
} else {
// Promised file dragging:
NSLog(@"[CDragSource drag]: file dragging promises are unsupported.");
NSArray* fileTypesArray = nil; // This should be set based on the transferable.
NSRect fileLocationRect = NSMakeRect(0, 0, 0, 0); // This should be set based on all filenames.
BOOL success = [view dragPromisedFilesOfTypes:fileTypesArray fromRect:fileLocationRect source:view slideBack:YES event:dragEvent];
if (success == TRUE) { // One would write out the promised files here.
}
}
NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];
// Convert drag operation to Java:
jint dragOp = [DnDUtilities mapNSDragOperationToJava:sDragOperation];
// Drag success must acount for DragOperationNone:
jboolean success = (dragOp != java_awt_dnd_DnDConstants_ACTION_NONE);
// We have a problem here... we don't send DragSource dragEnter/Exit messages outside of our own process
// because we don't get anything from AppKit/CoreDrag
// This means that if you drag outside of the app and drop, even if it's valid, a dragDropFinished is posted without dragEnter
// I'm worried that this might confuse Java, so we're going to send a "bogus" dragEnter if necessary (only if the drag succeeded)
if (success && sNeedsEnter) {
[self postDragEnter];
}
// DragSourceContextPeer.dragDropFinished() should be called even if there was an error:
JNF_MEMBER_CACHE(dragDropFinishedMethod, CDragSourceContextPeerClass, "dragDropFinished", "(ZIII)V");
DLog3(@" -> posting dragDropFinished, point %f, %f", point.x, point.y);
JNFCallVoidMethod(env, fDragSourceContextPeer, dragDropFinishedMethod, success, dragOp, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event)
JNF_MEMBER_CACHE(resetHoveringMethod, CDragSourceContextPeerClass, "resetHovering", "()V");
JNFCallVoidMethod(env, fDragSourceContextPeer, resetHoveringMethod); // Hust reset static variable
} @finally {
sNeedsEnter = NO;
}
// We have to do this, otherwise AppKit doesn't know we're finished dragging. Yup, it's that bad.
if ([[[NSRunLoop currentRunLoop] currentMode] isEqualTo:NSEventTrackingRunLoopMode]) {
[NSApp postEvent:[self nsDragEvent:NO] atStart:YES];
}
DLog2(@"[CDragSource doDrag] end: %@\n", self);
}
- (void)drag
{
AWT_ASSERT_NOT_APPKIT_THREAD;
[self performSelectorOnMainThread:@selector(doDrag) withObject:nil waitUntilDone:YES]; // AWT_THREADING Safe (called from unique asynchronous thread)
}
/******************************** BEGIN NSDraggingSource Interface ********************************/
- (void)draggingOperationChanged:(NSDragOperation)dragOp {
//DLog2(@"[CDragSource draggingOperationChanged]: %@\n", self);
JNIEnv* env = [ThreadUtilities getJNIEnv];
jint targetActions = fSourceActions;
if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions];
NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];
DLog3(@" -> posting operationChanged, point %f, %f", point.x, point.y);
jint modifiedModifiers = fDragKeyModifiers | fDragMouseModifiers | [DnDUtilities javaKeyModifiersForNSDragOperation:dragOp];
JNF_MEMBER_CACHE(operationChangedMethod, CDragSourceContextPeerClass, "operationChanged", "(IIII)V");
JNFCallVoidMethod(env, fDragSourceContextPeer, operationChangedMethod, targetActions, modifiedModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event)
}
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)localDrag {
//DLog2(@"[CDragSource draggingSourceOperationMaskForLocal]: %@\n", self);
return [DnDUtilities mapJavaDragOperationToNS:fSourceActions];
}
/* 9-16-02 Note: we don't support promises yet.
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination {
}*/
- (void)draggedImage:(NSImage *)image beganAt:(NSPoint)screenPoint {
DLog4(@"[CDragSource draggedImage beganAt]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self);
// Initialize static variables:
sDragOperation = NSDragOperationNone;
sDraggingLocation = screenPoint;
}
- (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation {
DLog4(@"[CDragSource draggedImage endedAt:]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self);
sDraggingLocation = screenPoint;
sDragOperation = operation;
}
- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint {
//DLog4(@"[CDragSource draggedImage moved]: (%d, %d) %@\n", (int) screenPoint.x, (int) screenPoint.y, self);
JNIEnv* env = [ThreadUtilities getJNIEnv];
JNF_COCOA_ENTER(env);
// There are two things we would be interested in:
// a) mouse pointer has moved
// b) drag actions (key modifiers) have changed
BOOL notifyJava = FALSE;
// a) mouse pointer has moved:
if (NSEqualPoints(screenPoint, sDraggingLocation) == FALSE) {
//DLog2(@"[CDragSource draggedImage:movedTo]: mouse moved, %@\n", self);
notifyJava = TRUE;
}
// b) drag actions (key modifiers) have changed:
jint modifiers = NsKeyModifiersToJavaModifiers([NSEvent modifierFlags], YES);
if (fDragKeyModifiers != modifiers) {
NSDragOperation currentOp = [DnDUtilities nsDragOperationForModifiers:[NSEvent modifierFlags]];
NSDragOperation allowedOp = [DnDUtilities mapJavaDragOperationToNS:fSourceActions] & currentOp;
fDragKeyModifiers = modifiers;
if (sDragOperation != allowedOp) {
sDragOperation = allowedOp;
[self draggingOperationChanged:allowedOp];
}
}
// Should we notify Java things have changed?
if (notifyJava) {
sDraggingLocation = screenPoint;
NSPoint point = [self mapNSScreenPointToJavaWithOffset:screenPoint];
jint targetActions = fSourceActions;
if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions];
// Motion: dragMotion, dragMouseMoved
DLog4(@"[CDragSource draggedImage moved]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self);
DLog3(@" -> posting dragMotion, point %f, %f", point.x, point.y);
JNF_MEMBER_CACHE(dragMotionMethod, CDragSourceContextPeerClass, "dragMotion", "(IIII)V");
JNFCallVoidMethod(env, fDragSourceContextPeer, dragMotionMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event)
DLog3(@" -> posting dragMouseMoved, point %f, %f", point.x, point.y);
JNF_MEMBER_CACHE(dragMouseMovedMethod, CDragSourceContextPeerClass, "dragMouseMoved", "(IIII)V");
JNFCallVoidMethod(env, fDragSourceContextPeer, dragMouseMovedMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event)
}
JNF_COCOA_EXIT(env);
}
- (BOOL)ignoreModifierKeysWhileDragging {
//DLog2(@"[CDragSource ignoreModifierKeysWhileDragging]: %@\n", self);
return NO;
}
/******************************** END NSDraggingSource Interface ********************************/
// postDragEnter and postDragExit are called from CDropTarget when possible and appropriate
// Currently only possible if source and target are in the same process
- (void) postDragEnter {
JNIEnv *env = [ThreadUtilities getJNIEnv];
sNeedsEnter = NO;
jint targetActions = fSourceActions;
if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions];
NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];
DLog3(@" -> posting dragEnter, point %f, %f", point.x, point.y);
JNF_MEMBER_CACHE(dragEnterMethod, CDragSourceContextPeerClass, "dragEnter", "(IIII)V");
JNFCallVoidMethod(env, fDragSourceContextPeer, dragEnterMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event)
}
- (void) postDragExit {
JNIEnv *env = [ThreadUtilities getJNIEnv];
sNeedsEnter = YES;
NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];
DLog3(@" -> posting dragExit, point %f, %f", point.x, point.y);
JNF_MEMBER_CACHE(dragExitMethod, CDragSourceContextPeerClass, "dragExit", "(II)V");
JNFCallVoidMethod(env, fDragSourceContextPeer, dragExitMethod, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event)
}
// Java assumes that the origin is the top-left corner of the screen.
// Cocoa assumes that the origin is the bottom-left corner of the screen.
// Adjust the y coordinate to account for this.
// NOTE: Also need to take into account the 0 screen relative screen coords.
// This is because all screen coords in Cocoa are relative to the 0 screen.
// Also see +[CWindow convertAWTToCocoaScreenRect]
// NSScreen-to-JavaScreen mapping:
- (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint)screenPoint {
NSRect mainR = [[[NSScreen screens] objectAtIndex:0] frame];
NSPoint point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y));
// Adjust the point with the drag image offset to get the real mouse hotspot:
// The point should remain in screen coordinates (as per DragSourceEvent.getLocation() documentation)
point.x -= fDragImageOffset.x;
point.y -= ([fDragImage size].height + fDragImageOffset.y);
return point;
}
@end