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