1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 //#define DND_DEBUG TRUE 27 28 #import "java_awt_dnd_DnDConstants.h" 29 30 #import <Cocoa/Cocoa.h> 31 #import <JavaNativeFoundation/JavaNativeFoundation.h> 32 33 #import "AWTEvent.h" 34 #import "AWTView.h" 35 #import "CDataTransferer.h" 36 #import "CDropTarget.h" 37 #import "CDragSource.h" 38 #import "DnDUtilities.h" 39 #import "ThreadUtilities.h" 40 41 42 // 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 break 44 // loose in shared code - tracking state going haywire. 45 static BOOL sIsJavaDragging; 46 47 48 @interface NSEvent(AWTAdditions) 49 50 + (void)javaDraggingBegin; 51 + (void)javaDraggingEnd; 52 53 @end 54 55 56 @implementation NSEvent(AWTAdditions) 57 58 59 + (void)javaDraggingBegin 60 { 61 sIsJavaDragging = YES; 62 } 63 64 + (void)javaDraggingEnd 65 { 66 // sIsJavaDragging is reset on mouseDown as well. 67 sIsJavaDragging = NO; 68 } 69 @end 70 71 JNF_CLASS_CACHE(DataTransfererClass, "sun/awt/datatransfer/DataTransferer"); 72 JNF_CLASS_CACHE(CDragSourceContextPeerClass, "sun/lwawt/macosx/CDragSourceContextPeer"); 73 JNF_CLASS_CACHE(CImageClass, "sun/lwawt/macosx/CImage"); 74 75 static NSDragOperation sDragOperation; 76 static NSPoint sDraggingLocation; 77 78 static CDragSource* sCurrentDragSource; 79 static BOOL sNeedsEnter; 80 81 @implementation CDragSource 82 83 + (CDragSource *) currentDragSource { 84 return sCurrentDragSource; 85 } 86 87 - (id)init:(jobject)jdragsourcecontextpeer component:(jobject)jcomponent peer:(jobject)jpeer control:(id)control 88 transferable:(jobject)jtransferable triggerEvent:(jobject)jtrigger 89 dragPosX:(jint)dragPosX dragPosY:(jint)dragPosY modifiers:(jint)extModifiers clickCount:(jint)clickCount 90 timeStamp:(jlong)timeStamp cursor:(jobject)jcursor 91 dragImage:(jobject)jnsdragimage dragImageOffsetX:(jint)jdragimageoffsetx dragImageOffsetY:(jint)jdragimageoffsety 92 sourceActions:(jint)jsourceactions formats:(jlongArray)jformats formatMap:(jobject)jformatmap 93 { 94 self = [super init]; 95 DLog2(@"[CDragSource init]: %@\n", self); 96 97 fView = nil; 98 fComponent = nil; 99 100 // Construct the object if we have a valid model for it: 101 if (control != nil) { 102 JNIEnv *env = [ThreadUtilities getJNIEnv]; 103 fComponent = JNFNewGlobalRef(env, jcomponent); 104 fComponentPeer = JNFNewGlobalRef(env, jpeer); 105 fDragSourceContextPeer = JNFNewGlobalRef(env, jdragsourcecontextpeer); 106 107 fTransferable = JNFNewGlobalRef(env, jtransferable); 108 fTriggerEvent = JNFNewGlobalRef(env, jtrigger); 109 fCursor = JNFNewGlobalRef(env, jcursor); 110 111 if (jnsdragimage) { 112 JNF_MEMBER_CACHE(nsImagePtr, CImageClass, "ptr", "J"); 113 jlong imgPtr = JNFGetLongField(env, jnsdragimage, nsImagePtr); 114 fDragImage = (NSImage*) jlong_to_ptr(imgPtr); // Double-casting prevents compiler 'd$|// 115 116 [fDragImage retain]; 117 } 118 119 fDragImageOffset = NSMakePoint(jdragimageoffsetx, jdragimageoffsety); 120 121 fSourceActions = jsourceactions; 122 fFormats = JNFNewGlobalRef(env, jformats); 123 fFormatMap = JNFNewGlobalRef(env, jformatmap); 124 125 fTriggerEventTimeStamp = timeStamp; 126 fDragPos = NSMakePoint(dragPosX, dragPosY); 127 fClickCount = clickCount; 128 fModifiers = extModifiers; 129 130 // Set this object as a dragging source: 131 132 AWTView *awtView = [((NSWindow *) control) contentView]; 133 fView = [awtView retain]; 134 [awtView setDragSource:self]; 135 136 // Let AWTEvent know Java drag is getting underway: 137 [NSEvent javaDraggingBegin]; 138 } 139 140 else { 141 [self release]; 142 self = nil; 143 } 144 145 return self; 146 } 147 148 - (void)removeFromView:(JNIEnv *)env 149 { 150 DLog2(@"[CDragSource removeFromView]: %@\n", self); 151 152 // Remove this dragging source from the view: 153 [((AWTView *) fView) setDragSource:nil]; 154 155 // Clean up JNI refs 156 if (fComponent != NULL) { 157 JNFDeleteGlobalRef(env, fComponent); 158 fComponent = NULL; 159 } 160 161 if (fComponentPeer != NULL) { 162 JNFDeleteGlobalRef(env, fComponentPeer); 163 fComponentPeer = NULL; 164 } 165 166 if (fDragSourceContextPeer != NULL) { 167 JNFDeleteGlobalRef(env, fDragSourceContextPeer); 168 fDragSourceContextPeer = NULL; 169 } 170 171 if (fTransferable != NULL) { 172 JNFDeleteGlobalRef(env, fTransferable); 173 fTransferable = NULL; 174 } 175 176 if (fTriggerEvent != NULL) { 177 JNFDeleteGlobalRef(env, fTriggerEvent); 178 fTriggerEvent = NULL; 179 } 180 181 if (fCursor != NULL) { 182 JNFDeleteGlobalRef(env, fCursor); 183 fCursor = NULL; 184 } 185 186 if (fFormats != NULL) { 187 JNFDeleteGlobalRef(env, fFormats); 188 fFormats = NULL; 189 } 190 191 if (fFormatMap != NULL) { 192 JNFDeleteGlobalRef(env, fFormatMap); 193 fFormatMap = NULL; 194 } 195 196 CFRelease(self); // GC 197 } 198 199 - (void)dealloc 200 { 201 DLog2(@"[CDragSource dealloc]: %@\n", self); 202 203 // Delete or release local data: 204 [fView release]; 205 fView = nil; 206 207 [fDragImage release]; 208 fDragImage = nil; 209 210 [super dealloc]; 211 } 212 //- (void)finalize { [super finalize]; } 213 214 215 // Appropriated from Windows' awt_DataTransferer.cpp: 216 // 217 // * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value. 218 // 219 - (jobject)dataTransferer:(JNIEnv*)env 220 { 221 JNF_STATIC_MEMBER_CACHE(getInstanceMethod, DataTransfererClass, "getInstance", "()Lsun/awt/datatransfer/DataTransferer;"); 222 return JNFCallStaticObjectMethod(env, getInstanceMethod); 223 } 224 225 // Appropriated from Windows' awt_DataTransferer.cpp: 226 // 227 // * NOTE: This returns a JNI Local Ref. Any code that calls must call DeleteLocalRef with the return value. 228 // 229 - (jbyteArray)convertData:(jlong)format 230 { 231 JNIEnv* env = [ThreadUtilities getJNIEnv]; 232 jobject transferer = [self dataTransferer:env]; 233 jbyteArray data = nil; 234 235 if (transferer != NULL) { 236 JNF_MEMBER_CACHE(convertDataMethod, DataTransfererClass, "convertData", "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B"); 237 data = JNFCallObjectMethod(env, transferer, convertDataMethod, fComponent, fTransferable, format, fFormatMap, (jboolean) TRUE); 238 } 239 240 return data; 241 } 242 243 244 // Encodes a byte array of zero-terminated filenames into an NSArray of NSStrings representing them. 245 // Borrowed and adapted from awt_DataTransferer.c, convertFileType(). 246 - (id)getFileList:(jbyte *)jbytes dataLength:(jsize)jbytesLength 247 { 248 jsize strings = 0; 249 jsize i; 250 251 // Get number of filenames while making sure to skip over empty strings. 252 for (i = 1; i < jbytesLength; i++) { 253 if (jbytes[i] == '\0' && jbytes[i - 1] != '\0') 254 strings++; 255 } 256 257 // Create the file list to return: 258 NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:strings]; 259 260 for (i = 0; i < jbytesLength; i++) { 261 char* start = (char *) &jbytes[i]; 262 263 // Skip over empty strings: 264 if (start[0] == '\0') { 265 continue; 266 } 267 268 // Update the position marker: 269 i += strlen(start); 270 271 // Add this filename to the file list: 272 NSMutableString* fileName = [NSMutableString stringWithUTF8String:start]; 273 // Decompose the filename 274 CFStringNormalize((CFMutableStringRef)fileName, kCFStringNormalizationFormD); 275 [fileList addObject:fileName]; 276 } 277 278 // 03-01-09 Note: keep this around for debugging. 279 // return [NSArray arrayWithObjects:@"/tmp/foo1", @"/tmp/foo2", nil]; 280 281 return ([fileList count] > 0 ? fileList : nil); 282 } 283 284 285 // Set up the pasteboard for dragging: 286 - (BOOL)declareTypesToPasteboard:(NSPasteboard *)pb withEnv:(JNIEnv *) env { 287 // 9-20-02 Note: leave this here for debugging: 288 //[pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: self]; 289 //return TRUE; 290 291 // Get byte array elements: 292 jboolean isCopy; 293 jlong* jformats = (*env)->GetLongArrayElements(env, fFormats, &isCopy); 294 if (jformats == nil) 295 return FALSE; 296 297 // Allocate storage arrays for dragging types to register with the pasteboard: 298 jsize formatsLength = (*env)->GetArrayLength(env, fFormats); 299 NSMutableArray* pbTypes = [[NSMutableArray alloc] initWithCapacity:formatsLength]; 300 301 // And assume there are no NS-type data: [Radar 3065621] 302 // This is to be able to drop transferables containing only a serialized object flavor, e.g.: 303 // "JAVA_DATAFLAVOR:application/x-java-serialized-object; class=java.awt.Label". 304 BOOL hasNSTypeData = false; 305 306 // Collect all supported types in a pasteboard format into the storage arrays: 307 jsize i; 308 for (i = 0; i < formatsLength; i++) { 309 jlong jformat = jformats[i]; 310 311 if (jformat >= 0) { 312 NSString* type = formatForIndex(jformat); 313 314 // Add element type to the storage array. 315 if (type != nil) { 316 if ([type hasPrefix:@"JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref;"] == false) { 317 [pbTypes addObject:type]; 318 319 // This is a good approximation if not perfect. A conclusive search would 320 // have to be done matching all defined strings in AppKit's commonStrings.h. 321 if ([type hasPrefix:@"NS"] || [type hasPrefix:@"NeXT"]) 322 hasNSTypeData = true; 323 } 324 } 325 } 326 } 327 328 // 1-16-03 Note: [Radar 3065621] 329 // When TransferHandler is used with Swing components it puts only a type like this on the pasteboard: 330 // "JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref; class=java.lang.String" 331 // And there's similar type for serialized object only transferables. 332 // Since our drop targets aren't trained for arbitrary data types yet we need to fake an empty string 333 // which will cause drop target handlers to fire. 334 // KCH - 3550405 If the drag is between Swing components, formatsLength == 0, so expand the check. 335 // Also, use a custom format rather than NSString, since that will prevent random views from accepting the drag 336 if (hasNSTypeData == false && formatsLength >= 0) { 337 [pbTypes addObject:[DnDUtilities javaPboardType]]; 338 } 339 340 (*env)->ReleaseLongArrayElements(env, fFormats, jformats, JNI_ABORT); 341 342 // Declare pasteboard types. If the types array is empty we still want to declare them 343 // as otherwise an old set of types/data would remain on the pasteboard. 344 NSUInteger typesCount = [pbTypes count]; 345 [pb declareTypes:pbTypes owner: self]; 346 347 // KCH - Lame conversion bug between Cocoa and Carbon drag types 348 // If I provide the filenames _right now_, NSFilenamesPboardType is properly converted to CoreDrag flavors 349 // If I try to wait until pasteboard:provideDataForType:, the conversion won't happen 350 // and pasteboard:provideDataForType: won't even get called! (unless I go over a Cocoa app) 351 if ([pbTypes containsObject:NSFilenamesPboardType]) { 352 [self pasteboard:pb provideDataForType:NSFilenamesPboardType]; 353 } 354 355 [pbTypes release]; 356 357 return typesCount > 0 ? TRUE : FALSE; 358 } 359 360 // This is an NSPasteboard callback. In declareTypesToPasteboard:withEnv:, we only declared the types 361 // When the AppKit DnD system actually needs the data, this method will be invoked. 362 // Note that if the transfer is handled entirely from Swing (as in a local-vm drag), this method may never be called. 363 - (void)pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type { 364 AWT_ASSERT_APPKIT_THREAD; 365 366 // 9-20-02 Note: leave this here for debugging: 367 //[pb setString: @"Hello, World!" forType: NSStringPboardType]; 368 // return; 369 370 // Set up Java environment: 371 JNIEnv* env = [ThreadUtilities getJNIEnv]; 372 373 id pbData = nil; 374 375 // Collect data in a pasteboard format: 376 jlong jformat = indexForFormat(type); 377 if (jformat >= 0) { 378 // Convert DataTransfer data to a Java byte array: 379 // Note that this will eventually call getTransferData() 380 jbyteArray jdata = [self convertData:jformat]; 381 382 if (jdata != nil) { 383 jboolean isCopy; 384 jsize jdataLength = (*env)->GetArrayLength(env, jdata); 385 jbyte* jbytedata = (*env)->GetByteArrayElements(env, jdata, &isCopy); 386 387 if (jdataLength > 0 && jbytedata != nil) { 388 // Get element data to the storage array. For NSFilenamesPboardType type we use 389 // an NSArray-type data - NSData-type data would cause a crash. 390 if (type != nil) { 391 pbData = ([type isEqualTo:NSFilenamesPboardType]) ? 392 [self getFileList:jbytedata dataLength:jdataLength] : 393 [NSData dataWithBytes:jbytedata length:jdataLength]; 394 } 395 } 396 397 (*env)->ReleaseByteArrayElements(env, jdata, jbytedata, JNI_ABORT); 398 399 (*env)->DeleteLocalRef(env, jdata); 400 } 401 } 402 403 // If we are the custom type that matches local-vm drags, set an empty NSData 404 if ( (pbData == nil) && ([type isEqualTo:[DnDUtilities javaPboardType]]) ) { 405 pbData = [NSData dataWithBytes:"" length:1]; 406 } 407 408 // Add pasteboard data for the type: 409 // Remember, NSFilenamesPboardType's data is NSArray (property list), not NSData! 410 // We must use proper pb accessor depending on the data type. 411 if ([pbData isKindOfClass:[NSArray class]]) 412 [pb setPropertyList:pbData forType:type]; 413 else 414 [pb setData:pbData forType:type]; 415 } 416 417 418 - (void)validateDragImage 419 { 420 // Make a small blank image if we don't have a drag image: 421 if (fDragImage == nil) { 422 // 9-30-02 Note: keep this around for debugging: 423 fDragImage = [[NSImage alloc] initWithSize:NSMakeSize(21, 21)]; 424 NSSize imageSize = [fDragImage size]; 425 426 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 427 pixelsWide:imageSize.width pixelsHigh:imageSize.height bitsPerSample:8 samplesPerPixel:4 428 hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:32]; 429 430 [fDragImage addRepresentation:imageRep]; 431 fDragImageOffset = NSMakePoint(0, 0); 432 433 [imageRep release]; 434 } 435 } 436 437 - (NSEvent*)nsDragEvent:(BOOL)isDrag 438 { 439 // Get NSView for the drag source: 440 NSWindow* window = [fView window]; 441 442 NSInteger windowNumber = [window windowNumber]; 443 NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithWindow:window]; 444 445 // Convert mouse coordinates to NS: 446 NSPoint location = NSMakePoint(fDragPos.x, fDragPos.y); 447 NSPoint eventLocation = [fView convertPoint:location toView:nil]; 448 449 // Convert fTriggerEventTimeStamp to NS - AWTEvent.h defines UTC(nsEvent) as ((jlong)[event timestamp] * 1000): 450 NSTimeInterval timeStamp = fTriggerEventTimeStamp / 1000; 451 452 // Convert fModifiers (extModifiers) to NS: 453 NSEventType mouseButtons = 0; 454 float pressure = 0.0; 455 if (isDrag) { 456 mouseButtons = (NSEventType) [DnDUtilities mapJavaExtModifiersToNSMouseDownButtons:fModifiers]; 457 pressure = 1.0; 458 } else { 459 mouseButtons = (NSEventType) [DnDUtilities mapJavaExtModifiersToNSMouseUpButtons:fModifiers]; 460 } 461 462 // Convert fModifiers (extModifiers) to NS: 463 NSUInteger modifiers = [DnDUtilities mapJavaExtModifiersToNSKeyModifiers:fModifiers]; 464 465 // Just a dummy value ... 466 NSInteger eventNumber = 0; 467 468 // Make a native autoreleased dragging event: 469 NSEvent* dragEvent = [NSEvent mouseEventWithType:mouseButtons location:eventLocation 470 modifierFlags:modifiers timestamp:timeStamp windowNumber:windowNumber context:graphicsContext 471 eventNumber:eventNumber clickCount:fClickCount pressure:pressure]; 472 473 return dragEvent; 474 } 475 476 - (void)doDrag 477 { 478 AWT_ASSERT_APPKIT_THREAD; 479 480 DLog2(@"[CDragSource doDrag]: %@\n", self); 481 482 // Set up Java environment: 483 JNIEnv *env = [ThreadUtilities getJNIEnv]; 484 485 // Set up the pasteboard: 486 NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard]; 487 [self declareTypesToPasteboard:pb withEnv:env]; 488 489 // Make a native autoreleased NS dragging event: 490 NSEvent *dragEvent = [self nsDragEvent:YES]; 491 492 // Get NSView for the drag source: 493 NSView *view = fView; 494 495 // Make sure we have a valid drag image: 496 [self validateDragImage]; 497 NSImage* dragImage = fDragImage; 498 499 // Get drag origin and offset: 500 NSPoint dragOrigin; 501 dragOrigin.x = fDragPos.x; 502 dragOrigin.y = fDragPos.y; 503 dragOrigin = [view convertPoint:[dragEvent locationInWindow] fromView:nil]; 504 dragOrigin.x += fDragImageOffset.x; 505 dragOrigin.y += [dragImage size].height + fDragImageOffset.y; 506 507 // Drag offset values don't seem to matter: 508 NSSize dragOffset = NSMakeSize(0, 0); 509 510 // These variables should be set based on the transferable: 511 BOOL isFileDrag = FALSE; 512 BOOL fileDragPromises = FALSE; 513 514 DLog(@"[CDragSource drag]: calling dragImage/File:"); 515 DLog3(@" - drag origin: %f, %f", fDragPos.x, fDragPos.y); 516 DLog5(@" - drag image: %f, %f (%f x %f)", fDragImageOffset.x, fDragImageOffset.y, [dragImage size].width, [dragImage size].height); 517 DLog3(@" - event point (window) %f, %f", [dragEvent locationInWindow].x, [dragEvent locationInWindow].y); 518 DLog3(@" - drag point (view) %f, %f", dragOrigin.x, dragOrigin.y); 519 520 // Set up the fDragKeyModifier, so we know if the operation has changed 521 // Set up the fDragMouseModifier, so we can |= it later (since CoreDrag doesn't tell us mouse states during a drag) 522 fDragKeyModifiers = [DnDUtilities extractJavaExtKeyModifiersFromJavaExtModifiers:fModifiers]; 523 fDragMouseModifiers = [DnDUtilities extractJavaExtMouseModifiersFromJavaExtModifiers:fModifiers]; 524 525 // Set the current DragSource 526 sCurrentDragSource = self; 527 sNeedsEnter = YES; 528 529 @try { 530 // Data dragging: 531 if (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: 535 NSLog(@"[CDragSource drag]: file dragging is unsupported."); 536 NSString* fileName = nil; // This should be set based on the transferable. 537 NSRect fileLocationRect = NSMakeRect(0, 0, 0, 0); // This should be set based on the filename. 538 539 BOOL success = [view dragFile:fileName fromRect:fileLocationRect slideBack:YES event:dragEvent]; 540 if (success == TRUE) { // One would erase dragged file if this was a move operation. 541 } 542 } else { 543 // Promised file dragging: 544 NSLog(@"[CDragSource drag]: file dragging promises are unsupported."); 545 NSArray* fileTypesArray = nil; // This should be set based on the transferable. 546 NSRect fileLocationRect = NSMakeRect(0, 0, 0, 0); // This should be set based on all filenames. 547 548 BOOL success = [view dragPromisedFilesOfTypes:fileTypesArray fromRect:fileLocationRect source:view slideBack:YES event:dragEvent]; 549 if (success == TRUE) { // One would write out the promised files here. 550 } 551 } 552 553 NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation]; 554 555 // Convert drag operation to Java: 556 jint dragOp = [DnDUtilities mapNSDragOperationToJava:sDragOperation]; 557 558 // Drag success must acount for DragOperationNone: 559 jboolean success = (dragOp != java_awt_dnd_DnDConstants_ACTION_NONE); 560 561 // We have a problem here... we don't send DragSource dragEnter/Exit messages outside of our own process 562 // because we don't get anything from AppKit/CoreDrag 563 // This means that if you drag outside of the app and drop, even if it's valid, a dragDropFinished is posted without dragEnter 564 // I'm worried that this might confuse Java, so we're going to send a "bogus" dragEnter if necessary (only if the drag succeeded) 565 if (success && sNeedsEnter) { 566 [self postDragEnter]; 567 } 568 569 // DragSourceContextPeer.dragDropFinished() should be called even if there was an error: 570 JNF_MEMBER_CACHE(dragDropFinishedMethod, CDragSourceContextPeerClass, "dragDropFinished", "(ZIII)V"); 571 DLog3(@" -> posting dragDropFinished, point %f, %f", point.x, point.y); 572 JNFCallVoidMethod(env, fDragSourceContextPeer, dragDropFinishedMethod, success, dragOp, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event) 573 JNF_MEMBER_CACHE(resetHoveringMethod, CDragSourceContextPeerClass, "resetHovering", "()V"); 574 JNFCallVoidMethod(env, fDragSourceContextPeer, resetHoveringMethod); // Hust reset static variable 575 } @finally { 576 // Clear the current DragSource 577 sCurrentDragSource = nil; 578 sNeedsEnter = NO; 579 } 580 581 // We have to do this, otherwise AppKit doesn't know we're finished dragging. Yup, it's that bad. 582 if ([[[NSRunLoop currentRunLoop] currentMode] isEqualTo:NSEventTrackingRunLoopMode]) { 583 [NSApp postEvent:[self nsDragEvent:NO] atStart:YES]; 584 } 585 586 DLog2(@"[CDragSource doDrag] end: %@\n", self); 587 } 588 589 - (void)drag 590 { 591 AWT_ASSERT_NOT_APPKIT_THREAD; 592 593 // Set the drag cursor (or not 3839999) 594 //JNIEnv *env = [ThreadUtilities getJNIEnv]; 595 //jobject gCursor = JNFNewGlobalRef(env, fCursor); 596 //[EventFactory setJavaCursor:gCursor withEnv:env]; 597 598 [self performSelectorOnMainThread:@selector(doDrag) withObject:nil waitUntilDone:YES]; // AWT_THREADING Safe (called from unique asynchronous thread) 599 } 600 601 /******************************** BEGIN NSDraggingSource Interface ********************************/ 602 603 - (void)draggingOperationChanged:(NSDragOperation)dragOp { 604 //DLog2(@"[CDragSource draggingOperationChanged]: %@\n", self); 605 606 JNIEnv* env = [ThreadUtilities getJNIEnv]; 607 608 jint targetActions = fSourceActions; 609 if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions]; 610 611 NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation]; 612 DLog3(@" -> posting operationChanged, point %f, %f", point.x, point.y); 613 jint modifiedModifiers = fDragKeyModifiers | fDragMouseModifiers | [DnDUtilities javaKeyModifiersForNSDragOperation:dragOp]; 614 615 JNF_MEMBER_CACHE(operationChangedMethod, CDragSourceContextPeerClass, "operationChanged", "(IIII)V"); 616 JNFCallVoidMethod(env, fDragSourceContextPeer, operationChangedMethod, targetActions, modifiedModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event) 617 } 618 619 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)localDrag { 620 //DLog2(@"[CDragSource draggingSourceOperationMaskForLocal]: %@\n", self); 621 return [DnDUtilities mapJavaDragOperationToNS:fSourceActions]; 622 } 623 624 /* 9-16-02 Note: we don't support promises yet. 625 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination { 626 }*/ 627 628 - (void)draggedImage:(NSImage *)image beganAt:(NSPoint)screenPoint { 629 DLog4(@"[CDragSource draggedImage beganAt]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self); 630 631 // Initialize static variables: 632 sDragOperation = NSDragOperationNone; 633 sDraggingLocation = screenPoint; 634 } 635 636 - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation { 637 DLog4(@"[CDragSource draggedImage endedAt:]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self); 638 639 sDraggingLocation = screenPoint; 640 sDragOperation = operation; 641 } 642 643 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint { 644 //DLog4(@"[CDragSource draggedImage moved]: (%d, %d) %@\n", (int) screenPoint.x, (int) screenPoint.y, self); 645 JNIEnv* env = [ThreadUtilities getJNIEnv]; 646 647 JNF_COCOA_ENTER(env); 648 // There are two things we would be interested in: 649 // a) mouse pointer has moved 650 // b) drag actions (key modifiers) have changed 651 652 BOOL notifyJava = FALSE; 653 654 // a) mouse pointer has moved: 655 if (NSEqualPoints(screenPoint, sDraggingLocation) == FALSE) { 656 //DLog2(@"[CDragSource draggedImage:movedTo]: mouse moved, %@\n", self); 657 notifyJava = TRUE; 658 } 659 660 // b) drag actions (key modifiers) have changed: 661 jint modifiers = [DnDUtilities currentJavaExtKeyModifiers]; 662 if (fDragKeyModifiers != modifiers) { 663 NSDragOperation currentOp = [DnDUtilities nsDragOperationForModifiers:[NSEvent modifierFlags]]; 664 NSDragOperation allowedOp = [DnDUtilities mapJavaDragOperationToNS:fSourceActions] & currentOp; 665 666 fDragKeyModifiers = modifiers; 667 668 if (sDragOperation != allowedOp) { 669 sDragOperation = allowedOp; 670 [self draggingOperationChanged:allowedOp]; 671 } 672 } 673 674 // Should we notify Java things have changed? 675 if (notifyJava) { 676 sDraggingLocation = screenPoint; 677 678 NSPoint point = [self mapNSScreenPointToJavaWithOffset:screenPoint]; 679 680 jint targetActions = fSourceActions; 681 if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions]; 682 683 // Motion: dragMotion, dragMouseMoved 684 DLog4(@"[CDragSource draggedImage moved]: (%f, %f) %@\n", screenPoint.x, screenPoint.y, self); 685 686 DLog3(@" -> posting dragMotion, point %f, %f", point.x, point.y); 687 JNF_MEMBER_CACHE(dragMotionMethod, CDragSourceContextPeerClass, "dragMotion", "(IIII)V"); 688 JNFCallVoidMethod(env, fDragSourceContextPeer, dragMotionMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event) 689 690 DLog3(@" -> posting dragMouseMoved, point %f, %f", point.x, point.y); 691 JNF_MEMBER_CACHE(dragMouseMovedMethod, CDragSourceContextPeerClass, "dragMouseMoved", "(IIII)V"); 692 JNFCallVoidMethod(env, fDragSourceContextPeer, dragMouseMovedMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event) 693 } 694 JNF_COCOA_EXIT(env); 695 } 696 697 - (BOOL)ignoreModifierKeysWhileDragging { 698 //DLog2(@"[CDragSource ignoreModifierKeysWhileDragging]: %@\n", self); 699 return NO; 700 } 701 702 /******************************** END NSDraggingSource Interface ********************************/ 703 704 705 // postDragEnter and postDragExit are called from CDropTarget when possible and appropriate 706 // Currently only possible if source and target are in the same process 707 - (void) postDragEnter { 708 JNIEnv *env = [ThreadUtilities getJNIEnv]; 709 sNeedsEnter = NO; 710 711 jint targetActions = fSourceActions; 712 if ([CDropTarget currentDropTarget]) targetActions = [[CDropTarget currentDropTarget] currentJavaActions]; 713 714 NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation]; 715 DLog3(@" -> posting dragEnter, point %f, %f", point.x, point.y); 716 JNF_MEMBER_CACHE(dragEnterMethod, CDragSourceContextPeerClass, "dragEnter", "(IIII)V"); 717 JNFCallVoidMethod(env, fDragSourceContextPeer, dragEnterMethod, targetActions, (jint) fModifiers, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event) 718 } 719 720 - (void) postDragExit { 721 JNIEnv *env = [ThreadUtilities getJNIEnv]; 722 sNeedsEnter = YES; 723 724 NSPoint point = [self mapNSScreenPointToJavaWithOffset:sDraggingLocation]; 725 DLog3(@" -> posting dragExit, point %f, %f", point.x, point.y); 726 JNF_MEMBER_CACHE(dragExitMethod, CDragSourceContextPeerClass, "dragExit", "(II)V"); 727 JNFCallVoidMethod(env, fDragSourceContextPeer, dragExitMethod, (jint) point.x, (jint) point.y); // AWT_THREADING Safe (event) 728 } 729 730 731 // Java assumes that the origin is the top-left corner of the screen. 732 // Cocoa assumes that the origin is the bottom-left corner of the screen. 733 // Adjust the y coordinate to account for this. 734 // NOTE: Also need to take into account the 0 screen relative screen coords. 735 // This is because all screen coords in Cocoa are relative to the 0 screen. 736 // Also see +[CWindow convertAWTToCocoaScreenRect] 737 // NSScreen-to-JavaScreen mapping: 738 - (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint)screenPoint { 739 NSRect mainR = [[[NSScreen screens] objectAtIndex:0] frame]; 740 NSPoint point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y)); 741 742 // Adjust the point with the drag image offset to get the real mouse hotspot: 743 // The point should remain in screen coordinates (as per DragSourceEvent.getLocation() documentation) 744 point.x -= fDragImageOffset.x; 745 point.y -= ([fDragImage size].height + fDragImageOffset.y); 746 747 return point; 748 } 749 750 @end