Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
41152 views
/*1* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425//#define DND_DEBUG TRUE2627#import "java_awt_dnd_DnDConstants.h"2829#import <Cocoa/Cocoa.h>3031#import "AWTEvent.h"32#import "AWTView.h"33#import "CDataTransferer.h"34#import "CDropTarget.h"35#import "CDragSource.h"36#import "DnDUtilities.h"37#import "ThreadUtilities.h"38#import "LWCToolkit.h"39#import "JNIUtilities.h"404142// When sIsJavaDragging is true Java drag gesture has been recognized and a drag is/has been initialized.43// We must stop posting MouseEvent.MOUSE_DRAGGED events for the duration of the drag or all hell will break44// loose in shared code - tracking state going haywire.45static BOOL sIsJavaDragging;464748@interface NSEvent(AWTAdditions)4950+ (void)javaDraggingBegin;51+ (void)javaDraggingEnd;5253@end545556@implementation NSEvent(AWTAdditions)575859+ (void)javaDraggingBegin60{61sIsJavaDragging = YES;62}6364+ (void)javaDraggingEnd65{66// sIsJavaDragging is reset on mouseDown as well.67sIsJavaDragging = NO;68}69@end7071static jclass DataTransfererClass = NULL;72static jclass CDragSourceContextPeerClass = NULL;7374#define GET_DT_CLASS() \75GET_CLASS(DataTransfererClass, "sun/awt/datatransfer/DataTransferer");7677#define GET_DT_CLASS_RETURN(ret) \78GET_CLASS_RETURN(DataTransfererClass, "sun/awt/datatransfer/DataTransferer", ret);7980#define GET_DSCP_CLASS() \81GET_CLASS(CDragSourceContextPeerClass, "sun/lwawt/macosx/CDragSourceContextPeer");8283static NSDragOperation sDragOperation;84static NSPoint sDraggingLocation;8586static BOOL sNeedsEnter;8788@interface CDragSource ()89// Updates from the destination to the source90- (void) postDragEnter;91- (void) postDragExit;92// Utility93- (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint) point;94@end9596@implementation CDragSource9798- (id) init:(jobject)jDragSourceContextPeer99component:(jobject)jComponent100control:(id)control101transferable:(jobject)jTransferable102triggerEvent:(jobject)jTrigger103dragPosX:(jint)dragPosX104dragPosY:(jint)dragPosY105modifiers:(jint)extModifiers106clickCount:(jint)clickCount107timeStamp:(jlong)timeStamp108dragImage:(jlong)nsDragImagePtr109dragImageOffsetX:(jint)jDragImageOffsetX110dragImageOffsetY:(jint)jDragImageOffsetY111sourceActions:(jint)jSourceActions112formats:(jlongArray)jFormats113formatMap:(jobject)jFormatMap114{115self = [super init];116DLog2(@"[CDragSource init]: %@\n", self);117118fView = nil;119fComponent = nil;120121// Construct the object if we have a valid model for it:122if (control != nil) {123fComponent = jComponent;124fDragSourceContextPeer = jDragSourceContextPeer;125fTransferable = jTransferable;126fTriggerEvent = jTrigger;127128if (nsDragImagePtr) {129fDragImage = (NSImage*) jlong_to_ptr(nsDragImagePtr);130[fDragImage retain];131}132133fDragImageOffset = NSMakePoint(jDragImageOffsetX, jDragImageOffsetY);134135fSourceActions = jSourceActions;136fFormats = jFormats;137fFormatMap = jFormatMap;138139fTriggerEventTimeStamp = timeStamp;140fDragPos = NSMakePoint(dragPosX, dragPosY);141fClickCount = clickCount;142fModifiers = extModifiers;143144// Set this object as a dragging source:145146fView = [(AWTView *) control retain];147[fView setDragSource:self];148149// Let AWTEvent know Java drag is getting underway:150[NSEvent javaDraggingBegin];151}152153else {154[self release];155self = nil;156}157158return self;159}160161- (void)removeFromView:(JNIEnv *)env162{163DLog2(@"[CDragSource removeFromView]: %@\n", self);164165// Remove this dragging source from the view:166[((AWTView *) fView) setDragSource:nil];167168// Clean up JNI refs169if (fComponent != NULL) {170(*env)->DeleteGlobalRef(env, fComponent);171fComponent = NULL;172}173174if (fDragSourceContextPeer != NULL) {175(*env)->DeleteGlobalRef(env, fDragSourceContextPeer);176fDragSourceContextPeer = NULL;177}178179if (fTransferable != NULL) {180(*env)->DeleteGlobalRef(env, fTransferable);181fTransferable = NULL;182}183184if (fTriggerEvent != NULL) {185(*env)->DeleteGlobalRef(env, fTriggerEvent);186fTriggerEvent = NULL;187}188189if (fFormats != NULL) {190(*env)->DeleteGlobalRef(env, fFormats);191fFormats = NULL;192}193194if (fFormatMap != NULL) {195(*env)->DeleteGlobalRef(env, fFormatMap);196fFormatMap = NULL;197}198199[self release];200}201202- (void)dealloc203{204DLog2(@"[CDragSource dealloc]: %@\n", self);205206// Delete or release local data:207[fView release];208fView = nil;209210[fDragImage release];211fDragImage = nil;212213[super dealloc];214}215216// Appropriated from Windows' awt_DataTransferer.cpp:217//218// * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value.219//220- (jobject)dataTransferer:(JNIEnv*)env221{222GET_DT_CLASS_RETURN(NULL);223DECLARE_STATIC_METHOD_RETURN(getInstanceMethod, DataTransfererClass, "getInstance", "()Lsun/awt/datatransfer/DataTransferer;", NULL);224jobject o = (*env)->CallStaticObjectMethod(env, DataTransfererClass, getInstanceMethod);225CHECK_EXCEPTION();226return o;227}228229// Appropriated from Windows' awt_DataTransferer.cpp:230//231// * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value.232//233- (jbyteArray)convertData:(jlong)format234{235JNIEnv* env = [ThreadUtilities getJNIEnv];236jobject transferer = [self dataTransferer:env];237jbyteArray data = nil;238239if (transferer != NULL) {240GET_DT_CLASS_RETURN(NULL);241DECLARE_METHOD_RETURN(convertDataMethod, DataTransfererClass, "convertData", "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B", NULL);242data = (*env)->CallObjectMethod(env, transferer, convertDataMethod, fComponent, fTransferable, format, fFormatMap, (jboolean) TRUE);243}244CHECK_EXCEPTION();245246return data;247}248249250// Encodes a byte array of zero-terminated filenames into an NSArray of NSStrings representing them.251// Borrowed and adapted from awt_DataTransferer.c, convertFileType().252- (id)getFileList:(jbyte *)jbytes dataLength:(jsize)jbytesLength253{254jsize strings = 0;255jsize i;256257// Get number of filenames while making sure to skip over empty strings.258for (i = 1; i < jbytesLength; i++) {259if (jbytes[i] == '\0' && jbytes[i - 1] != '\0')260strings++;261}262263// Create the file list to return:264NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:strings];265266for (i = 0; i < jbytesLength; i++) {267char* start = (char *) &jbytes[i];268269// Skip over empty strings:270if (start[0] == '\0') {271continue;272}273274// Update the position marker:275i += strlen(start);276277// Add this filename to the file list:278NSMutableString* fileName = [NSMutableString stringWithUTF8String:start];279// Decompose the filename280CFStringNormalize((CFMutableStringRef)fileName, kCFStringNormalizationFormD);281[fileList addObject:fileName];282}283284// 03-01-09 Note: keep this around for debugging.285// return [NSArray arrayWithObjects:@"/tmp/foo1", @"/tmp/foo2", nil];286287return ([fileList count] > 0 ? fileList : nil);288}289290291// Set up the pasteboard for dragging:292- (BOOL)declareTypesToPasteboard:(NSPasteboard *)pb withEnv:(JNIEnv *) env {293// 9-20-02 Note: leave this here for debugging:294//[pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: self];295//return TRUE;296297// Get byte array elements:298jboolean isCopy;299jlong* jformats = (*env)->GetLongArrayElements(env, fFormats, &isCopy);300if (jformats == nil)301return FALSE;302303// Allocate storage arrays for dragging types to register with the pasteboard:304jsize formatsLength = (*env)->GetArrayLength(env, fFormats);305NSMutableArray* pbTypes = [[NSMutableArray alloc] initWithCapacity:formatsLength];306307// And assume there are no NS-type data: [Radar 3065621]308// This is to be able to drop transferables containing only a serialized object flavor, e.g.:309// "JAVA_DATAFLAVOR:application/x-java-serialized-object; class=java.awt.Label".310BOOL hasNSTypeData = false;311312// Collect all supported types in a pasteboard format into the storage arrays:313jsize i;314for (i = 0; i < formatsLength; i++) {315jlong jformat = jformats[i];316317if (jformat >= 0) {318NSString* type = formatForIndex(jformat);319320// Add element type to the storage array.321if (type != nil) {322if ([type hasPrefix:@"JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref;"] == false) {323[pbTypes addObject:type];324325// This is a good approximation if not perfect. A conclusive search would326// have to be done matching all defined strings in AppKit's commonStrings.h.327hasNSTypeData = [type hasPrefix:@"NS"] || [type hasPrefix:@"NeXT"] || [type hasPrefix:@"public."];328}329}330}331}332333// 1-16-03 Note: [Radar 3065621]334// When TransferHandler is used with Swing components it puts only a type like this on the pasteboard:335// "JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref; class=java.lang.String"336// And there's similar type for serialized object only transferables.337// Since our drop targets aren't trained for arbitrary data types yet we need to fake an empty string338// which will cause drop target handlers to fire.339// KCH - 3550405 If the drag is between Swing components, formatsLength == 0, so expand the check.340// Also, use a custom format rather than NSString, since that will prevent random views from accepting the drag341if (hasNSTypeData == false && formatsLength >= 0) {342[pbTypes addObject:[DnDUtilities javaPboardType]];343}344345(*env)->ReleaseLongArrayElements(env, fFormats, jformats, JNI_ABORT);346347// Declare pasteboard types. If the types array is empty we still want to declare them348// as otherwise an old set of types/data would remain on the pasteboard.349NSUInteger typesCount = [pbTypes count];350[pb declareTypes:pbTypes owner: self];351352// KCH - Lame conversion bug between Cocoa and Carbon drag types353// If I provide the filenames _right now_, NSFilenamesPboardType is properly converted to CoreDrag flavors354// If I try to wait until pasteboard:provideDataForType:, the conversion won't happen355// and pasteboard:provideDataForType: won't even get called! (unless I go over a Cocoa app)356if ([pbTypes containsObject:NSFilenamesPboardType]) {357[self pasteboard:pb provideDataForType:NSFilenamesPboardType];358}359360[pbTypes release];361362return typesCount > 0 ? TRUE : FALSE;363}364365// This is an NSPasteboard callback. In declareTypesToPasteboard:withEnv:, we only declared the types366// When the AppKit DnD system actually needs the data, this method will be invoked.367// Note that if the transfer is handled entirely from Swing (as in a local-vm drag), this method may never be called.368- (void)pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type {369AWT_ASSERT_APPKIT_THREAD;370371// 9-20-02 Note: leave this here for debugging:372//[pb setString: @"Hello, World!" forType: NSStringPboardType];373// return;374375// Set up Java environment:376JNIEnv* env = [ThreadUtilities getJNIEnv];377378id pbData = nil;379380// Collect data in a pasteboard format:381jlong jformat = indexForFormat(type);382if (jformat >= 0) {383// Convert DataTransfer data to a Java byte array:384// Note that this will eventually call getTransferData()385jbyteArray jdata = [self convertData:jformat];386387if (jdata != nil) {388jboolean isCopy;389jsize jdataLength = (*env)->GetArrayLength(env, jdata);390jbyte* jbytedata = (*env)->GetByteArrayElements(env, jdata, &isCopy);391392if (jdataLength > 0 && jbytedata != nil) {393// Get element data to the storage array. For NSFilenamesPboardType type we use394// an NSArray-type data - NSData-type data would cause a crash.395if (type != nil) {396pbData = ([type isEqualTo:NSFilenamesPboardType]) ?397[self getFileList:jbytedata dataLength:jdataLength] :398[NSData dataWithBytes:jbytedata length:jdataLength];399}400}401402(*env)->ReleaseByteArrayElements(env, jdata, jbytedata, JNI_ABORT);403404(*env)->DeleteLocalRef(env, jdata);405}406}407408// If we are the custom type that matches local-vm drags, set an empty NSData409if ( (pbData == nil) && ([type isEqualTo:[DnDUtilities javaPboardType]]) ) {410pbData = [NSData dataWithBytes:"" length:1];411}412413// Add pasteboard data for the type:414// Remember, NSFilenamesPboardType's data is NSArray (property list), not NSData!415// We must use proper pb accessor depending on the data type.416if ([pbData isKindOfClass:[NSArray class]])417[pb setPropertyList:pbData forType:type];418else419[pb setData:pbData forType:type];420}421422423- (void)validateDragImage424{425// Make a small blank image if we don't have a drag image:426if (fDragImage == nil) {427// 9-30-02 Note: keep this around for debugging:428fDragImage = [[NSImage alloc] initWithSize:NSMakeSize(21, 21)];429NSSize imageSize = [fDragImage size];430431NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL432pixelsWide:imageSize.width pixelsHigh:imageSize.height bitsPerSample:8 samplesPerPixel:4433hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:32];434435[fDragImage addRepresentation:imageRep];436fDragImageOffset = NSMakePoint(0, 0);437438[imageRep release];439}440}441442- (NSEvent*)nsDragEvent:(BOOL)isDrag443{444// Get NSView for the drag source:445NSWindow* window = [fView window];446447NSInteger windowNumber = [window windowNumber];448449// Convert mouse coordinates to NS:450NSPoint eventLocation = [fView convertPoint:NSMakePoint(fDragPos.x, fDragPos.y) toView:nil];451eventLocation.y = [[fView window] frame].size.height - eventLocation.y;452453// Convert fTriggerEventTimeStamp to NS - AWTEvent.h defines UTC(nsEvent) as ((jlong)[event timestamp] * 1000):454NSTimeInterval timeStamp = fTriggerEventTimeStamp / 1000;455456// Convert fModifiers (extModifiers) to NS:457NSEventType mouseButtons = 0;458float pressure = 0.0;459if (isDrag) {460mouseButtons = (NSEventType) [DnDUtilities mapJavaExtModifiersToNSMouseDownButtons:fModifiers];461pressure = 1.0;462} else {463mouseButtons = (NSEventType) [DnDUtilities mapJavaExtModifiersToNSMouseUpButtons:fModifiers];464}465466// Convert fModifiers (extModifiers) to NS:467NSUInteger modifiers = JavaModifiersToNsKeyModifiers(fModifiers, TRUE);468469// Just a dummy value ...470NSInteger eventNumber = 0;471// NSEvent.context is deprecated and unused472NSGraphicsContext* unusedPassNil = nil;473474// Make a native autoreleased dragging event:475NSEvent* dragEvent = [NSEvent mouseEventWithType:mouseButtons location:eventLocation476modifierFlags:modifiers timestamp:timeStamp windowNumber:windowNumber context:unusedPassNil477eventNumber:eventNumber clickCount:fClickCount pressure:pressure];478479return dragEvent;480}481482- (void)doDrag483{484AWT_ASSERT_APPKIT_THREAD;485486DLog2(@"[CDragSource doDrag]: %@\n", self);487488// Set up Java environment:489JNIEnv *env = [ThreadUtilities getJNIEnv];490491// Set up the pasteboard:492NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard];493[self declareTypesToPasteboard:pb withEnv:env];494495// Make a native autoreleased NS dragging event:496NSEvent *dragEvent = [self nsDragEvent:YES];497498// Get NSView for the drag source:499NSView *view = fView;500501// Make sure we have a valid drag image:502[self validateDragImage];503NSImage* dragImage = fDragImage;504505// Get drag origin and offset:506NSPoint dragOrigin = [dragEvent locationInWindow];507dragOrigin.x += fDragImageOffset.x;508dragOrigin.y -= fDragImageOffset.y + [dragImage size].height;509510// Drag offset values don't seem to matter:511NSSize dragOffset = NSMakeSize(0, 0);512513// These variables should be set based on the transferable:514BOOL isFileDrag = FALSE;515BOOL fileDragPromises = FALSE;516517DLog(@"[CDragSource drag]: calling dragImage/File:");518DLog3(@" - drag origin: %f, %f", fDragPos.x, fDragPos.y);519DLog5(@" - drag image: %f, %f (%f x %f)", fDragImageOffset.x, fDragImageOffset.y, [dragImage size].width, [dragImage size].height);520DLog3(@" - event point (window) %f, %f", [dragEvent locationInWindow].x, [dragEvent locationInWindow].y);521DLog3(@" - drag point (view) %f, %f", dragOrigin.x, dragOrigin.y);522// Set up the fDragKeyModifier, so we know if the operation has changed523// Set up the fDragMouseModifier, so we can |= it later (since CoreDrag doesn't tell us mouse states during a drag)524fDragKeyModifiers = [DnDUtilities extractJavaExtKeyModifiersFromJavaExtModifiers:fModifiers];525fDragMouseModifiers = [DnDUtilities extractJavaExtMouseModifiersFromJavaExtModifiers:fModifiers];526527@try {528sNeedsEnter = YES;529AWTToolkit.inDoDragDropLoop = YES;530// Data dragging:531if (isFileDrag == FALSE) {532[view dragImage:dragImage at:dragOrigin offset:dragOffset event:dragEvent pasteboard:pb source:view slideBack:YES];533} else if (fileDragPromises == FALSE) {534// File dragging:535NSLog(@"[CDragSource drag]: file dragging is unsupported.");536NSString* fileName = nil; // This should be set based on the transferable.537NSRect fileLocationRect = NSMakeRect(0, 0, 0, 0); // This should be set based on the filename.538539BOOL success = [view dragFile:fileName fromRect:fileLocationRect slideBack:YES event:dragEvent];540if (success == TRUE) { // One would erase dragged file if this was a move operation.541}542} else {543// Promised file dragging:544NSLog(@"[CDragSource drag]: file dragging promises are unsupported.");545NSArray* fileTypesArray = nil; // This should be set based on the transferable.546NSRect fileLocationRect = NSMakeRect(0, 0, 0, 0); // This should be set based on all filenames.547548BOOL success = [view dragPromisedFilesOfTypes:fileTypesArray fromRect:fileLocationRect source:view slideBack:YES event:dragEvent];549if (success == TRUE) { // One would write out the promised files here.550}551}552553NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];554555// Convert drag operation to Java:556jint dragOp = [DnDUtilities mapNSDragOperationToJava:sDragOperation];557558// Drag success must acount for DragOperationNone:559jboolean success = (dragOp != java_awt_dnd_DnDConstants_ACTION_NONE);560561// We have a problem here... we don't send DragSource dragEnter/Exit messages outside of our own process562// because we don't get anything from AppKit/CoreDrag563// This means that if you drag outside of the app and drop, even if it's valid, a dragDropFinished is posted without dragEnter564// I'm worried that this might confuse Java, so we're going to send a "bogus" dragEnter if necessary (only if the drag succeeded)565if (success && sNeedsEnter) {566[self postDragEnter];567}568569// DragSourceContextPeer.dragDropFinished() should be called even if there was an error:570GET_DSCP_CLASS();571DECLARE_METHOD(dragDropFinishedMethod, CDragSourceContextPeerClass, "dragDropFinished", "(ZIII)V");572DLog3(@" -> posting dragDropFinished, point %f, %f", point.x, point.y);573(*env)->CallVoidMethod(env, fDragSourceContextPeer, dragDropFinishedMethod, success, dragOp, (jint) point.x, (jint) point.y);574CHECK_EXCEPTION();575DECLARE_METHOD(resetHoveringMethod, CDragSourceContextPeerClass, "resetHovering", "()V");576(*env)->CallVoidMethod(env, fDragSourceContextPeer, resetHoveringMethod); // Hust reset static variable577CHECK_EXCEPTION();578} @finally {579sNeedsEnter = NO;580AWTToolkit.inDoDragDropLoop = NO;581}582583// We have to do this, otherwise AppKit doesn't know we're finished dragging. Yup, it's that bad.584if ([[[NSRunLoop currentRunLoop] currentMode] isEqualTo:NSEventTrackingRunLoopMode]) {585[NSApp postEvent:[self nsDragEvent:NO] atStart:YES];586}587588DLog2(@"[CDragSource doDrag] end: %@\n", self);589}590591- (void)drag592{593AWT_ASSERT_NOT_APPKIT_THREAD;594595[self performSelectorOnMainThread:@selector(doDrag) withObject:nil waitUntilDone:YES];596}597598/******************************** BEGIN NSDraggingSource Interface ********************************/599600- (void)draggingOperationChanged:(NSDragOperation)dragOp {601//DLog2(@"[CDragSource draggingOperationChanged]: %@\n", self);602603JNIEnv* env = [ThreadUtilities getJNIEnv];604605jint targetActions = fSourceActions;606if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions];607608NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];609DLog3(@" -> posting operationChanged, point %f, %f", point.x, point.y);610jint modifiedModifiers = fDragKeyModifiers | fDragMouseModifiers | [DnDUtilities javaKeyModifiersForNSDragOperation:dragOp];611612GET_DSCP_CLASS();613DECLARE_METHOD(operationChangedMethod, CDragSourceContextPeerClass, "operationChanged", "(IIII)V");614(*env)->CallVoidMethod(env, fDragSourceContextPeer, operationChangedMethod, targetActions, modifiedModifiers, (jint) point.x, (jint) point.y);615CHECK_EXCEPTION();616}617618- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)localDrag {619//DLog2(@"[CDragSource draggingSourceOperationMaskForLocal]: %@\n", self);620return [DnDUtilities mapJavaDragOperationToNS:fSourceActions];621}622623/* 9-16-02 Note: we don't support promises yet.624- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination {625}*/626627- (void)draggedImage:(NSImage *)image beganAt:(NSPoint)screenPoint {628DLog4(@"[CDragSource draggedImage beganAt]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self);629[AWTToolkit eventCountPlusPlus];630// Initialize static variables:631sDragOperation = NSDragOperationNone;632sDraggingLocation = screenPoint;633}634635- (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation {636DLog4(@"[CDragSource draggedImage endedAt:]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self);637[AWTToolkit eventCountPlusPlus];638sDraggingLocation = screenPoint;639sDragOperation = operation;640}641642- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint {643//DLog4(@"[CDragSource draggedImage moved]: (%d, %d) %@\n", (int) screenPoint.x, (int) screenPoint.y, self);644JNIEnv* env = [ThreadUtilities getJNIEnv];645646JNI_COCOA_ENTER(env);647[AWTToolkit eventCountPlusPlus];648// There are two things we would be interested in:649// a) mouse pointer has moved650// b) drag actions (key modifiers) have changed651652BOOL notifyJava = FALSE;653654// a) mouse pointer has moved:655if (NSEqualPoints(screenPoint, sDraggingLocation) == FALSE) {656//DLog2(@"[CDragSource draggedImage:movedTo]: mouse moved, %@\n", self);657notifyJava = TRUE;658}659660// b) drag actions (key modifiers) have changed:661jint modifiers = NsKeyModifiersToJavaModifiers([NSEvent modifierFlags], YES);662if (fDragKeyModifiers != modifiers) {663NSDragOperation currentOp = [DnDUtilities nsDragOperationForModifiers:[NSEvent modifierFlags]];664NSDragOperation allowedOp = [DnDUtilities mapJavaDragOperationToNS:fSourceActions] & currentOp;665666fDragKeyModifiers = modifiers;667668if (sDragOperation != allowedOp) {669sDragOperation = allowedOp;670[self draggingOperationChanged:allowedOp];671}672}673674// Should we notify Java things have changed?675if (notifyJava) {676sDraggingLocation = screenPoint;677678NSPoint point = [self mapNSScreenPointToJavaWithOffset:screenPoint];679680jint targetActions = fSourceActions;681if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions];682683// Motion: dragMotion, dragMouseMoved684DLog4(@"[CDragSource draggedImage moved]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self);685686DLog3(@" -> posting dragMotion, point %f, %f", point.x, point.y);687GET_DSCP_CLASS();688DECLARE_METHOD(dragMotionMethod, CDragSourceContextPeerClass, "dragMotion", "(IIII)V");689(*env)->CallVoidMethod(env, fDragSourceContextPeer, dragMotionMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y);690CHECK_EXCEPTION();691DLog3(@" -> posting dragMouseMoved, point %f, %f", point.x, point.y);692DECLARE_METHOD(dragMouseMovedMethod, CDragSourceContextPeerClass, "dragMouseMoved", "(IIII)V");693(*env)->CallVoidMethod(env, fDragSourceContextPeer, dragMouseMovedMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y);694CHECK_EXCEPTION();695}696JNI_COCOA_EXIT(env);697}698699- (BOOL)ignoreModifierKeysWhileDragging {700//DLog2(@"[CDragSource ignoreModifierKeysWhileDragging]: %@\n", self);701return NO;702}703704/******************************** END NSDraggingSource Interface ********************************/705706707// postDragEnter and postDragExit are called from CDropTarget when possible and appropriate708// Currently only possible if source and target are in the same process709- (void) postDragEnter {710JNIEnv *env = [ThreadUtilities getJNIEnv];711sNeedsEnter = NO;712713jint targetActions = fSourceActions;714if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions];715716NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];717DLog3(@" -> posting dragEnter, point %f, %f", point.x, point.y);718GET_DSCP_CLASS();719DECLARE_METHOD(dragEnterMethod, CDragSourceContextPeerClass, "dragEnter", "(IIII)V");720(*env)->CallVoidMethod(env, fDragSourceContextPeer, dragEnterMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y);721CHECK_EXCEPTION();722}723724- (void) postDragExit {725JNIEnv *env = [ThreadUtilities getJNIEnv];726sNeedsEnter = YES;727728NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation];729DLog3(@" -> posting dragExit, point %f, %f", point.x, point.y);730GET_DSCP_CLASS();731DECLARE_METHOD(dragExitMethod, CDragSourceContextPeerClass, "dragExit", "(II)V");732(*env)->CallVoidMethod(env, fDragSourceContextPeer, dragExitMethod, (jint) point.x, (jint) point.y);733CHECK_EXCEPTION();734735}736737738// Java assumes that the origin is the top-left corner of the screen.739// Cocoa assumes that the origin is the bottom-left corner of the screen.740// Adjust the y coordinate to account for this.741// NOTE: Also need to take into account the 0 screen relative screen coords.742// This is because all screen coords in Cocoa are relative to the 0 screen.743// Also see +[CWindow convertAWTToCocoaScreenRect]744// NSScreen-to-JavaScreen mapping:745- (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint)screenPoint {746NSRect mainR = [[[NSScreen screens] objectAtIndex:0] frame];747NSPoint point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y));748749// Adjust the point with the drag image offset to get the real mouse hotspot:750// The point should remain in screen coordinates (as per DragSourceEvent.getLocation() documentation)751point.x -= fDragImageOffset.x;752point.y -= ([fDragImage size].height + fDragImageOffset.y);753754return point;755}756757@end758759760