1/*
2 * Copyright (c) 2011, 2013, 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 "CDropTarget.h"
29#import "AWTView.h"
30
31#import "sun_lwawt_macosx_CDropTarget.h"
32#import "java_awt_dnd_DnDConstants.h"
33
34#import <JavaRuntimeSupport/JavaRuntimeSupport.h>
35#include <objc/objc-runtime.h>
36
37
38#import "CDragSource.h"
39#import "CDataTransferer.h"
40#import "DnDUtilities.h"
41#import "ThreadUtilities.h"
42#import "JNIUtilities.h"
43
44
45static NSInteger        sDraggingSequenceNumber = -1;
46static NSDragOperation    sDragOperation;
47static NSDragOperation    sUpdateOperation;
48static jint                sJavaDropOperation;
49static NSPoint            sDraggingLocation;
50static BOOL                sDraggingExited;
51static BOOL                sDraggingError;
52
53static NSUInteger        sPasteboardItemsCount = 0;
54static NSArray*            sPasteboardTypes = nil;
55static NSArray*            sPasteboardData = nil;
56static jlongArray        sDraggingFormats = nil;
57
58static CDropTarget*        sCurrentDropTarget;
59
60extern jclass jc_CDropTargetContextPeer;
61#define GET_DTCP_CLASS() \
62    GET_CLASS(jc_CDropTargetContextPeer, "sun/lwawt/macosx/CDropTargetContextPeer");
63
64#define GET_DTCP_CLASS_RETURN(ret) \
65    GET_CLASS_RETURN(jc_CDropTargetContextPeer, "sun/lwawt/macosx/CDropTargetContextPeer", ret);
66
67@implementation CDropTarget
68
69+ (CDropTarget *) currentDropTarget {
70    return sCurrentDropTarget;
71}
72
73- (id)init:(jobject)jdropTarget component:(jobject)jcomponent control:(id)control
74{
75    self = [super init];
76    DLog2(@"[CDropTarget init]: %@\n", self);
77
78    fView = nil;
79    fComponent = nil;
80    fDropTarget = nil;
81    fDropTargetContextPeer = nil;
82
83
84    if (control != nil) {
85        JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
86        fComponent = (*env)->NewGlobalRef(env, jcomponent);
87        fDropTarget = (*env)->NewGlobalRef(env, jdropTarget);
88
89        fView = [((AWTView *) control) retain];
90        [fView setDropTarget:self];
91
92
93    } else {
94        // This would be an error.
95        [self release];
96        self = nil;
97    }
98    return self;
99}
100
101// When [CDropTarget init] is called the ControlModel's fView may not have been set up yet. ControlModel
102// (soon after) calls [CDropTarget controlModelControlValid] on the native event thread, once per CDropTarget,
103// to let it know it's been set up now.
104- (void)controlModelControlValid
105{
106    // 9-30-02 Note: [Radar 3065621]
107    // List all known pasteboard types here (see AppKit's NSPasteboard.h)
108    // How to register for non-standard data types remains to be determined.
109    NSArray* dataTypes = [[NSArray alloc] initWithObjects:
110        NSStringPboardType,
111        NSFilenamesPboardType,
112        NSPostScriptPboardType,
113        NSTIFFPboardType,
114        NSPasteboardTypePNG,
115        NSRTFPboardType,
116        NSTabularTextPboardType,
117        NSFontPboardType,
118        NSRulerPboardType,
119        NSFileContentsPboardType,
120        NSColorPboardType,
121        NSRTFDPboardType,
122        NSHTMLPboardType,
123        NSURLPboardType,
124        NSPDFPboardType,
125        NSVCardPboardType,
126        NSFilesPromisePboardType,
127        [DnDUtilities javaPboardType],
128        (NSString*)kUTTypeJPEG,
129        nil];
130
131    // Enable dragging events over this object:
132    [fView registerForDraggedTypes:dataTypes];
133
134    [dataTypes release];
135}
136
137- (void)releaseDraggingData
138{
139    DLog2(@"[CDropTarget releaseDraggingData]: %@\n", self);
140
141    // Release any old pasteboard types, data and properties:
142    [sPasteboardTypes release];
143    sPasteboardTypes = nil;
144
145    [sPasteboardData release];
146    sPasteboardData = nil;
147
148    if (sDraggingFormats != NULL) {
149        JNIEnv *env = [ThreadUtilities getJNIEnv];
150        (*env)->DeleteGlobalRef(env, sDraggingFormats);
151        sDraggingFormats = NULL;
152    }
153
154    sPasteboardItemsCount = 0;
155    sDraggingSequenceNumber = -1;
156}
157
158- (void)removeFromView:(JNIEnv *)env
159{
160    DLog2(@"[CDropTarget removeFromView]: %@\n", self);
161
162    // Remove this dragging destination from the view:
163    [((AWTView *) fView) setDropTarget:nil];
164
165    // Clean up JNI refs
166    if (fComponent != NULL) {
167        (*env)->DeleteGlobalRef(env, fComponent);
168        fComponent = NULL;
169    }
170    if (fDropTarget != NULL) {
171        (*env)->DeleteGlobalRef(env, fDropTarget);
172        fDropTarget = NULL;
173    }
174    if (fDropTargetContextPeer != NULL) {
175        (*env)->DeleteGlobalRef(env, fDropTargetContextPeer);
176        fDropTargetContextPeer = NULL;
177    }
178
179    [self release];
180}
181
182- (void)dealloc
183{
184    DLog2(@"[CDropTarget dealloc]: %@\n", self);
185
186    if(sCurrentDropTarget == self) {
187        sCurrentDropTarget = nil;
188    }
189
190    [fView release];
191    fView = nil;
192
193    [super dealloc];
194}
195
196- (NSInteger) getDraggingSequenceNumber
197{
198    return sDraggingSequenceNumber;
199}
200
201// Debugging help:
202- (void)dumpPasteboard:(NSPasteboard*)pasteboard
203{
204    NSArray* pasteboardTypes = [pasteboard types];
205    NSUInteger pasteboardItemsCount = [pasteboardTypes count];
206    NSUInteger i;
207
208    // For each flavor on the pasteboard show the type, its data, and its property if there is one:
209    for (i = 0; i < pasteboardItemsCount; i++) {
210        NSString* pbType = [pasteboardTypes objectAtIndex:i];
211        CFShow(pbType);
212
213        NSData*    pbData = [pasteboard dataForType:pbType];
214        CFShow(pbData);
215
216        if ([pbType hasPrefix:@"CorePasteboardFlavorType"] == NO) {
217            id pbDataProperty = [pasteboard propertyListForType:pbType];
218            CFShow(pbDataProperty);
219        }
220    }
221}
222
223- (BOOL)copyDraggingTypes:(id<NSDraggingInfo>)sender
224{
225    DLog2(@"[CDropTarget copyDraggingTypes]: %@\n", self);
226    JNIEnv*    env = [ThreadUtilities getJNIEnv];
227
228    // Release any old pasteboard data:
229    [self releaseDraggingData];
230
231    NSPasteboard* pb = [sender draggingPasteboard];
232    sPasteboardTypes = [[pb types] retain];
233    sPasteboardItemsCount = [sPasteboardTypes count];
234    if (sPasteboardItemsCount == 0)
235        return FALSE;
236
237    jlongArray formats = (*env)->NewLongArray(env, sPasteboardItemsCount);
238    if (formats == nil)
239        return FALSE;
240
241    sDraggingFormats = (jlongArray) (*env)->NewGlobalRef(env, formats);
242    (*env)->DeleteLocalRef(env, formats);
243    if (sDraggingFormats == nil)
244        return FALSE;
245
246    jboolean isCopy;
247    jlong* jformats = (*env)->GetLongArrayElements(env, sDraggingFormats, &isCopy);
248    if (jformats == nil) {
249        return FALSE;
250    }
251
252    // Copy all data formats and properties. In case of properties, if they are nil, we need to use
253    // a special NilProperty since [NSArray addObject] would crash on adding a nil object.
254    DLog2(@"[CDropTarget copyDraggingTypes]: typesCount = %lu\n", (unsigned long) sPasteboardItemsCount);
255    NSUInteger i;
256    for (i = 0; i < sPasteboardItemsCount; i++) {
257        NSString* pbType = [sPasteboardTypes objectAtIndex:i];
258        DLog3(@"[CDropTarget copyDraggingTypes]: type[%lu] = %@\n", (unsigned long) i, pbType);
259
260        // 01-10-03 Note: until we need data properties for doing something useful don't copy them.
261        // They're often copies of their flavor's data and copying them for all available pasteboard flavors
262        // (which are often auto-translation of one another) can be a significant time/space hit.
263
264        // If this is a remote object type (not a pre-defined format) register it with the pasteboard:
265        jformats[i] = indexForFormat(pbType);
266        if (jformats[i] == -1 && [pbType hasPrefix:@"JAVA_DATAFLAVOR:application/x-java-remote-object;"])
267            jformats[i] = registerFormatWithPasteboard(pbType);
268    }
269
270    (*env)->ReleaseLongArrayElements(env, sDraggingFormats, jformats, JNI_COMMIT);
271
272    return TRUE;
273}
274
275- (BOOL)copyDraggingData:(id<NSDraggingInfo>)sender
276{
277    DLog2(@"[CDropTarget copyDraggingData]: %@\n", self);
278
279    sPasteboardData = [[NSMutableArray alloc] init];
280    if (sPasteboardData == nil)
281        return FALSE;
282
283    // Copy all data items to a safe place since the pasteboard may go away before we'll need them:
284    NSPasteboard* pb = [sender draggingPasteboard];
285    NSUInteger i;
286    for (i = 0; i < sPasteboardItemsCount; i++) {
287        // Get a type and its data and save the data:
288        NSString* pbType = [sPasteboardTypes objectAtIndex:i];
289        // 01-10-03 Note: copying only NS-type data (until Java-specified types can make it through the AppKit)
290        // would be a good idea since we can't do anything with those CoreFoundation unknown types anyway.
291        // But I'm worried that it would break something in Fuller so I'm leaving this here as a reminder,
292        // to be evaluated later.
293        //id pbData = [pbType hasPrefix:@"NS"] ? [pb dataForType:pbType] : nil; // Copy only NS-type data!
294        id pbData = [pb dataForType:pbType];
295
296        // If the data is null we can't store it in the array - an exception would be thrown.
297        // We use the special object NSNull instead which is kosher.
298        if (pbData == nil)
299            pbData = [NSNull null];
300
301        [((NSMutableArray*) sPasteboardData) addObject:pbData];
302    }
303
304    return TRUE;
305}
306
307- (NSData*) getDraggingDataForURL:(NSData*)data
308{
309    NSData* result = nil;
310
311    // Convert data into a property list if possible:
312    NSPropertyListFormat propertyListFormat;
313    NSString* errorString = nil;
314    id propertyList = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable
315        format:&propertyListFormat errorDescription:&errorString];
316
317    // URL types have only a single URL string in an array:
318    if (propertyList != nil && errorString == nil && [propertyList isKindOfClass:[NSArray class]]) {
319        NSArray*  array = (NSArray*) propertyList;
320        if ([array count] > 0) {
321            NSString* url = (NSString*) [array objectAtIndex:0];
322            if (url != nil && [url length] > 0)
323                result = [url dataUsingEncoding:[url fastestEncoding]];
324        }
325    }
326
327    return result;
328}
329
330- (jobject) copyDraggingDataForFormat:(jlong)format
331{
332    JNIEnv*      env = [ThreadUtilities getJNIEnvUncached]; // Join the main thread by requesting uncached environment
333
334    NSData*      data = nil;
335
336    // Convert the Java format (datatransferer int index) to a pasteboard format (NSString):
337    NSString* pbType = formatForIndex(format);
338    if ([sPasteboardTypes containsObject:pbType]) {
339        NSUInteger dataIndex = [sPasteboardTypes indexOfObject:pbType];
340        data = [sPasteboardData objectAtIndex:dataIndex];
341
342        if ((id) data == [NSNull null])
343            data = nil;
344
345        // format == 8 (CF_URL in CDataTransferer): we need a URL-to-String conversion:
346        else if ([pbType isEqualToString:@"Apple URL pasteboard type"])
347            data = [self getDraggingDataForURL:data];
348    }
349
350    // Get NS data:
351    char* dataBytes = (data != nil) ? (char*) [data bytes] : "Unsupported type";
352    NSUInteger dataLength = (data != nil) ? [data length] : sizeof("Unsupported type");
353
354    // Create a global byte array:
355    jbyteArray lbyteArray = (*env)->NewByteArray(env, dataLength);
356    if (lbyteArray == nil)
357        return nil;
358    jbyteArray gbyteArray = (jbyteArray) (*env)->NewGlobalRef(env, lbyteArray);
359    (*env)->DeleteLocalRef(env, lbyteArray);
360    if (gbyteArray == nil)
361        return nil;
362
363    // Get byte array elements:
364    jboolean isCopy;
365    jbyte* jbytes = (*env)->GetByteArrayElements(env, gbyteArray, &isCopy);
366    if (jbytes == nil)
367        return nil;
368
369    // Copy data to byte array and release elements:
370    memcpy(jbytes, dataBytes, dataLength);
371    (*env)->ReleaseByteArrayElements(env, gbyteArray, jbytes, JNI_COMMIT);
372
373    // In case of an error make sure to return nil:
374    if ((*env)->ExceptionOccurred(env)) {
375                (*env)->ExceptionDescribe(env);
376        gbyteArray = nil;
377        }
378
379    return gbyteArray;
380}
381
382- (void)safeReleaseDraggingData:(NSNumber *)arg
383{
384    jlong draggingSequenceNumber = [arg longLongValue];
385
386    // Make sure dragging data is released only if no new drag is under way. If a new drag
387    // has been initiated it has released the old dragging data already. This has to be called
388    // on the native event thread - otherwise we'd need to start synchronizing.
389    if (draggingSequenceNumber == sDraggingSequenceNumber)
390        [self releaseDraggingData];
391}
392
393- (void)javaDraggingEnded:(jlong)draggingSequenceNumber success:(BOOL)jsuccess action:(jint)jdropaction
394{
395    NSNumber *draggingSequenceNumberID = [NSNumber numberWithLongLong:draggingSequenceNumber];
396        // Report back actual Swing success, not what AppKit thinks
397        sDraggingError = !jsuccess;
398        sDragOperation = [DnDUtilities mapJavaDragOperationToNS:jdropaction];
399
400    // Release dragging data if any when Java's AWT event thread is all finished.
401    // Make sure dragging data is released on the native event thread.
402    [ThreadUtilities performOnMainThread:@selector(safeReleaseDraggingData:) on:self withObject:draggingSequenceNumberID waitUntilDone:NO];
403}
404
405- (jint)currentJavaActions {
406    return [DnDUtilities mapNSDragOperationToJava:sUpdateOperation];
407}
408
409/********************************  BEGIN NSDraggingDestination Interface  ********************************/
410
411
412// Private API to calculate the current Java actions
413- (void) calculateCurrentSourceActions:(jint *)actions dropAction:(jint *)dropAction
414{
415    // Get the raw (unmodified by keys) source actions
416    id jrsDrag = objc_lookUpClass("JRSDrag");
417    if (jrsDrag != nil) {
418        NSDragOperation rawDragActions = (NSDragOperation) [jrsDrag performSelector:@selector(currentAllowableActions)];
419        if (rawDragActions != NSDragOperationNone) {
420            // Both actions and dropAction default to the rawActions
421            *actions = [DnDUtilities mapNSDragOperationMaskToJava:rawDragActions];
422            *dropAction = *actions;
423
424            // Get the current key modifiers.
425            NSUInteger dragModifiers = (NSUInteger) [jrsDrag performSelector:@selector(currentModifiers)];
426            // Either the drop action is narrowed as per Java rules (MOVE, COPY, LINK, NONE) or by the drag modifiers
427            if (dragModifiers) {
428                // Get the user selected operation based on the drag modifiers, then return the intersection
429                NSDragOperation currentOp = [DnDUtilities nsDragOperationForModifiers:dragModifiers];
430                NSDragOperation allowedOp = rawDragActions & currentOp;
431
432                *dropAction = [DnDUtilities mapNSDragOperationToJava:allowedOp];
433            }
434        }
435    }
436    *dropAction = [DnDUtilities narrowJavaDropActions:*dropAction];
437}
438
439- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
440{
441    DLog2(@"[CDropTarget draggingEntered]: %@\n", self);
442
443    sCurrentDropTarget = self;
444
445    JNIEnv* env = [ThreadUtilities getJNIEnv];
446    NSInteger draggingSequenceNumber = [sender draggingSequenceNumber];
447
448    // Set the initial drag operation return value:
449    NSDragOperation dragOp = NSDragOperationNone;
450        sJavaDropOperation = java_awt_dnd_DnDConstants_ACTION_NONE;
451
452    // We could probably special-case some stuff if drag and drop objects match:
453    //if ([sender dragSource] == fView)
454
455    if (draggingSequenceNumber != sDraggingSequenceNumber) {
456        sDraggingSequenceNumber = draggingSequenceNumber;
457        sDraggingError = FALSE;
458
459        // Delete any drop target context peer left over from a previous drag:
460        if (fDropTargetContextPeer != NULL) {
461            (*env)->DeleteGlobalRef(env, fDropTargetContextPeer);
462            fDropTargetContextPeer = NULL;
463        }
464
465        // Look up the CDropTargetContextPeer class:
466        GET_DTCP_CLASS_RETURN(dragOp);
467        DECLARE_STATIC_METHOD_RETURN(getDropTargetContextPeerMethod, jc_CDropTargetContextPeer,
468                                     "getDropTargetContextPeer", "()Lsun/lwawt/macosx/CDropTargetContextPeer;", dragOp)
469        if (sDraggingError == FALSE) {
470            // Create a new drop target context peer:
471            jobject dropTargetContextPeer = (*env)->CallStaticObjectMethod(env, jc_CDropTargetContextPeer, getDropTargetContextPeerMethod);
472            CHECK_EXCEPTION();
473
474            if (dropTargetContextPeer != nil) {
475                fDropTargetContextPeer = (*env)->NewGlobalRef(env, dropTargetContextPeer);
476                (*env)->DeleteLocalRef(env, dropTargetContextPeer);
477            }
478        }
479
480        // Get dragging types (dragging data is only copied if dropped):
481        if (sDraggingError == FALSE && [self copyDraggingTypes:sender] == FALSE)
482            sDraggingError = TRUE;
483    }
484
485    if (sDraggingError == FALSE) {
486        sDraggingExited = FALSE;
487        sDraggingLocation = [sender draggingLocation];
488        NSPoint javaLocation = [fView convertPoint:sDraggingLocation fromView:nil];
489        javaLocation.y = fView.window.frame.size.height - javaLocation.y;
490
491        DLog5(@"+ dragEnter: loc native %f, %f, java %f, %f\n", sDraggingLocation.x, sDraggingLocation.y, javaLocation.x, javaLocation.y);
492
493                ////////// BEGIN Calculate the current drag actions //////////
494                jint actions = java_awt_dnd_DnDConstants_ACTION_NONE;
495        jint dropAction = actions;
496
497                [self calculateCurrentSourceActions:&actions dropAction:&dropAction];
498
499                sJavaDropOperation = dropAction;
500                ////////// END Calculate the current drag actions //////////
501
502        jlongArray formats = sDraggingFormats;
503
504        GET_DTCP_CLASS_RETURN(dragOp);
505        DECLARE_METHOD_RETURN(handleEnterMessageMethod, jc_CDropTargetContextPeer,
506                              "handleEnterMessage", "(Ljava/awt/Component;IIII[JJ)I", dragOp);
507        if (sDraggingError == FALSE) {
508            // Double-casting self gets rid of 'different size' compiler warning:
509            // AWT_THREADING Safe (CToolkitThreadBlockedHandler)
510            actions = (*env)->CallIntMethod(env, fDropTargetContextPeer, handleEnterMessageMethod,
511                                       fComponent, (jint) javaLocation.x, (jint) javaLocation.y,
512                                       dropAction, actions, formats, ptr_to_jlong(self));
513            CHECK_EXCEPTION();
514        }
515
516        if (sDraggingError == FALSE) {
517            // Initialize drag operation:
518            sDragOperation = NSDragOperationNone;
519
520            // Map Java actions back to NSDragOperation.
521            // 1-6-03 Note: if the entry point of this CDropTarget isn't covered by a droppable component
522            // (as can be the case with lightweight children) we must not return NSDragOperationNone
523            // since that would prevent dropping into any of the contained drop targets.
524            // Unfortunately there is no easy way to test this so we just test actions and override them
525            // with GENERIC if necessary. Proper drag operations will be returned by draggingUpdated: which is
526            // called right away, taking care of setting the right cursor and snap-back action.
527            dragOp = ((actions != java_awt_dnd_DnDConstants_ACTION_NONE) ?
528                [DnDUtilities mapJavaDragOperationToNS:dropAction] : NSDragOperationGeneric);
529
530            // Remember the dragOp for no-op'd update messages:
531            sUpdateOperation = dragOp;
532        }
533    }
534
535    // 9-11-02 Note: the native event thread would not handle an exception gracefully:
536    //if (sDraggingError == TRUE)
537    //    [NSException raise:NSGenericException format:@"[CDropTarget draggingEntered] failed."];
538
539    DLog2(@"[CDropTarget draggingEntered]: returning %lu\n", (unsigned long) dragOp);
540
541    return dragOp;
542}
543
544- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
545{
546    //DLog2(@"[CDropTarget draggingUpdated]: %@\n", self);
547
548    sCurrentDropTarget = self;
549
550    // Set the initial drag operation return value:
551    NSDragOperation dragOp = (sDraggingError == FALSE ? sUpdateOperation : NSDragOperationNone);
552
553    // There are two things we would be interested in:
554    // a) mouse pointer has moved
555    // b) drag actions (key modifiers) have changed
556
557    NSPoint draggingLocation = [sender draggingLocation];
558    JNIEnv* env = [ThreadUtilities getJNIEnv];
559
560    BOOL notifyJava = FALSE;
561
562    // a) mouse pointer has moved:
563    if (NSEqualPoints(draggingLocation, sDraggingLocation) == FALSE) {
564        //DLog2(@"[CDropTarget draggingUpdated]: mouse moved, %@\n", self);
565        sDraggingLocation = draggingLocation;
566        notifyJava = TRUE;
567    }
568
569    // b) drag actions (key modifiers) have changed (handleMotionMessage() will do proper notifications):
570        ////////// BEGIN Calculate the current drag actions //////////
571        jint actions = java_awt_dnd_DnDConstants_ACTION_NONE;
572        jint dropAction = actions;
573
574        [self calculateCurrentSourceActions:&actions dropAction:&dropAction];
575
576        if (sJavaDropOperation != dropAction) {
577            sJavaDropOperation = dropAction;
578            notifyJava = TRUE;
579        }
580        ////////// END Calculate the current drag actions //////////
581
582    jint userAction = dropAction;
583
584    // Should we notify Java things have changed?
585    if (sDraggingError == FALSE && notifyJava) {
586        NSPoint javaLocation = [fView convertPoint:sDraggingLocation fromView:nil];
587        javaLocation.y = fView.window.frame.size.height - javaLocation.y;
588        //DLog5(@"  : dragMoved: loc native %f, %f, java %f, %f\n", sDraggingLocation.x, sDraggingLocation.y, javaLocation.x, javaLocation.y);
589
590        jlongArray formats = sDraggingFormats;
591
592        GET_DTCP_CLASS_RETURN(dragOp);
593        DECLARE_METHOD_RETURN(handleMotionMessageMethod, jc_CDropTargetContextPeer, "handleMotionMessage", "(Ljava/awt/Component;IIII[JJ)I", dragOp);
594        if (sDraggingError == FALSE) {
595            DLog3(@"  >> posting handleMotionMessage, point %f, %f", javaLocation.x, javaLocation.y);
596            userAction = (*env)->CallIntMethod(env, fDropTargetContextPeer, handleMotionMessageMethod, fComponent,
597                         (jint) javaLocation.x, (jint) javaLocation.y, dropAction, actions, formats, ptr_to_jlong(self)); // AWT_THREADING Safe (CToolkitThreadBlockedHandler)
598        CHECK_EXCEPTION();
599        }
600
601        if (sDraggingError == FALSE) {
602            dragOp = [DnDUtilities mapJavaDragOperationToNS:userAction];
603
604            // Remember the dragOp for no-op'd update messages:
605            sUpdateOperation = dragOp;
606        } else {
607            dragOp = NSDragOperationNone;
608        }
609    }
610
611    DLog2(@"[CDropTarget draggingUpdated]: returning %lu\n", (unsigned long) dragOp);
612
613    return dragOp;
614}
615
616- (void)draggingExited:(id<NSDraggingInfo>)sender
617{
618    DLog2(@"[CDropTarget draggingExited]: %@\n", self);
619
620    sCurrentDropTarget = nil;
621
622    JNIEnv* env = [ThreadUtilities getJNIEnv];
623
624    if (sDraggingExited == FALSE && sDraggingError == FALSE) {
625        GET_DTCP_CLASS();
626        DECLARE_METHOD(handleExitMessageMethod, jc_CDropTargetContextPeer, "handleExitMessage", "(Ljava/awt/Component;J)V");
627        if (sDraggingError == FALSE) {
628            DLog3(@"  - dragExit: loc native %f, %f\n", sDraggingLocation.x, sDraggingLocation.y);
629             // AWT_THREADING Safe (CToolkitThreadBlockedHandler)
630            (*env)->CallVoidMethod(env, fDropTargetContextPeer,
631                              handleExitMessageMethod, fComponent, ptr_to_jlong(self));
632            CHECK_EXCEPTION();
633        }
634
635        // 5-27-03 Note: [Radar 3270455]
636        // -draggingExited: can be called both by the AppKit and by -performDragOperation: but shouldn't execute
637        // twice per drop since cleanup code like that in swing/plaf/basic/BasicDropTargetListener would throw NPEs.
638        sDraggingExited = TRUE;
639    }
640
641    DLog(@"[CDropTarget draggingExited]: returning.\n");
642}
643
644- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
645{
646    DLog2(@"[CDropTarget prepareForDragOperation]: %@\n", self);
647    DLog2(@"[CDropTarget prepareForDragOperation]: returning %@\n", (sDraggingError ? @"NO" : @"YES"));
648
649    return sDraggingError ? NO : YES;
650}
651
652- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
653{
654    DLog2(@"[CDropTarget performDragOperation]: %@\n", self);
655
656    sCurrentDropTarget = nil;
657
658    JNIEnv* env = [ThreadUtilities getJNIEnv];
659
660    // Now copy dragging data:
661    if (sDraggingError == FALSE && [self copyDraggingData:sender] == FALSE)
662        sDraggingError = TRUE;
663
664    if (sDraggingError == FALSE) {
665        sDraggingLocation = [sender draggingLocation];
666        NSPoint javaLocation = [fView convertPoint:sDraggingLocation fromView:nil];
667        // The y coordinate that comes in the NSDraggingInfo seems to be reversed - probably
668        // has to do something with the type of view it comes to.
669        // This is the earliest place where we can correct it.
670        javaLocation.y = fView.window.frame.size.height - javaLocation.y;
671
672        jint actions = [DnDUtilities mapNSDragOperationMaskToJava:[sender draggingSourceOperationMask]];
673        jint dropAction = sJavaDropOperation;
674
675        jlongArray formats = sDraggingFormats;
676
677        GET_DTCP_CLASS_RETURN(NO);
678        DECLARE_METHOD_RETURN(handleDropMessageMethod, jc_CDropTargetContextPeer, "handleDropMessage", "(Ljava/awt/Component;IIII[JJ)V", NO);
679
680        if (sDraggingError == FALSE) {
681            (*env)->CallVoidMethod(env, fDropTargetContextPeer, handleDropMessageMethod, fComponent,
682                     (jint) javaLocation.x, (jint) javaLocation.y, dropAction, actions, formats, ptr_to_jlong(self)); // AWT_THREADING Safe (event)
683            CHECK_EXCEPTION();
684        }
685    } else {
686        // 8-19-03 Note: [Radar 3368754]
687        // draggingExited: is not called after a drop - we must do that here ... but only in case
688        // of an error, instead of drop(). Otherwise we get twice the cleanup in shared code.
689        [self draggingExited:sender];
690    }
691
692// TODO:BG
693//   [(id)sender _setLastDragDestinationOperation:sDragOperation];
694
695
696    DLog2(@"[CDropTarget performDragOperation]: returning %@\n", (sDraggingError ? @"NO" : @"YES"));
697
698    return !sDraggingError;
699}
700
701- (void)concludeDragOperation:(id<NSDraggingInfo>)sender
702{
703    sCurrentDropTarget = nil;
704
705    DLog2(@"[CDropTarget concludeDragOperation]: %@\n", self);
706    DLog(@"[CDropTarget concludeDragOperation]: returning.\n");
707}
708
709// 9-11-02 Note: draggingEnded is not yet implemented by the AppKit.
710- (void)draggingEnded:(id<NSDraggingInfo>)sender
711{
712    sCurrentDropTarget = nil;
713
714    DLog2(@"[CDropTarget draggingEnded]: %@\n", self);
715    DLog(@"[CDropTarget draggingEnded]: returning.\n");
716}
717
718/********************************  END NSDraggingDestination Interface  ********************************/
719
720@end
721
722
723/*
724 * Class:     sun_lwawt_macosx_CDropTarget
725 * Method:    createNativeDropTarget
726 * Signature: (Ljava/awt/dnd/DropTarget;Ljava/awt/Component;Ljava/awt/peer/ComponentPeer;J)J
727 */
728JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CDropTarget_createNativeDropTarget
729  (JNIEnv *env, jobject jthis, jobject jdroptarget, jobject jcomponent, jlong jnativepeer)
730{
731    CDropTarget* dropTarget = nil;
732
733JNI_COCOA_ENTER(env);
734    id controlObj = (id) jlong_to_ptr(jnativepeer);
735    dropTarget = [[CDropTarget alloc] init:jdroptarget component:jcomponent control:controlObj];
736JNI_COCOA_EXIT(env);
737
738    return ptr_to_jlong(dropTarget);
739}
740
741/*
742 * Class:     sun_lwawt_macosx_CDropTarget
743 * Method:    releaseNativeDropTarget
744 * Signature: (J)V
745 */
746JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CDropTarget_releaseNativeDropTarget
747  (JNIEnv *env, jobject jthis, jlong nativeDropTargetVal)
748{
749    id dropTarget = (id)jlong_to_ptr(nativeDropTargetVal);
750
751JNI_COCOA_ENTER(env);
752    [dropTarget removeFromView:env];
753JNI_COCOA_EXIT(env);
754}
755