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