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