1/*
2 * Copyright (c) 2011, 2012, 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
27/*
28Documentation for Drag and Drop (Radar 3065640)
29There are several problems with Drag and Drop - notably, the mismatch between Java, Cocoa, and Carbon
30
31 Java reports both the original source actions, and the user-selected actions (selected using KB modifiers) to both the source and target during the drag. AppKit only reports to the destination during the drag. This was solved by directly asking CGS for the KB state during the source's image moved callback.
32
33 Java uses Shift/Move, Control/Copy and Shift+Control/Link. AppKit uses Command/Move, Alternate/Copy and Control/Link. Carbon uses Command/Move, Alternate/Copy and Command+Alternate/Link. This is bad, because Control overlaps between Java and AppKit. In this case, we choose compatibility between Carbon and Java (Java wins over AppKit wrt Control). This means that drags between Java applications will work correctly, regardless of whether you use the Carbon or the Java key modifiers. Drags to Java applications will work correctly regardless of whether you use the Carbon or the Java key modifiers. Drags from Java applications to non-Java applications will only work if you use the Carbon modifiers.
34
35 The reason we can't just set the CoreDrag(G/S)etAllowableActions directly (while ignoring the modifier keys) is because Carbon apps traditionally don't pay any attention - they only look at the modifier keys.
36 */
37
38#import <Cocoa/Cocoa.h>
39#import "DnDUtilities.h"
40#import "java_awt_dnd_DnDConstants.h"
41#import "java_awt_event_InputEvent.h"
42
43@implementation DnDUtilities
44
45// Make sure we don't let other apps see local drags by using a process unique pasteboard type.
46// This may not work in the Applet case, since they are all running in the same VM
47+ (NSString *) javaPboardType {
48    static NSString *customJavaPboardType = nil;
49    if (customJavaPboardType == nil)
50        customJavaPboardType = [[NSString stringWithFormat:@"NSJavaPboardType-%@", [[NSProcessInfo processInfo] globallyUniqueString]] retain];
51    return customJavaPboardType;
52}
53
54+ (jint)mapNSDragOperationToJava:(NSDragOperation)dragOperation
55{
56    jint result = java_awt_dnd_DnDConstants_ACTION_NONE;
57
58    if ((dragOperation & NSDragOperationCopy) != 0)                    // 1
59        result = ((dragOperation & NSDragOperationMove) == 0) ? java_awt_dnd_DnDConstants_ACTION_COPY : java_awt_dnd_DnDConstants_ACTION_COPY_OR_MOVE;
60
61    else if ((dragOperation & NSDragOperationMove) != 0)            // 16
62        result = java_awt_dnd_DnDConstants_ACTION_MOVE;
63
64    else if ((dragOperation & NSDragOperationLink) != 0)            // 2
65        result = java_awt_dnd_DnDConstants_ACTION_LINK;
66
67    else if ((dragOperation & NSDragOperationGeneric) != 0)            // 4
68        result = java_awt_dnd_DnDConstants_ACTION_MOVE;
69
70    // Pre-empted by the above cases:
71    //else if (dragOperation == NSDragOperationEvery)                    // UINT_MAX
72    //    result = java_awt_dnd_DnDConstants_ACTION_COPY_OR_MOVE;
73
74    // To be rejected:
75    //else if ((dragOperation & NSDragOperationPrivate) != 0)        // 8
76    //else if ((dragOperation & NSDragOperationAll_Obsolete) != 0)    // 15
77    //else if ((dragOperation & NSDragOperationDelete) != 0)        // 32
78
79    return result;
80}
81
82+ (jint)mapNSDragOperationMaskToJava:(NSDragOperation)dragOperation
83{
84    jint result = java_awt_dnd_DnDConstants_ACTION_NONE;
85
86    if (dragOperation & NSDragOperationMove)
87        result |= java_awt_dnd_DnDConstants_ACTION_MOVE;
88
89    if (dragOperation & NSDragOperationCopy)
90        result |= java_awt_dnd_DnDConstants_ACTION_COPY;
91
92    if (dragOperation & NSDragOperationLink)
93        result |= java_awt_dnd_DnDConstants_ACTION_LINK;
94
95    // Only look at Generic if none of the other options are specified
96    if ( (dragOperation & NSDragOperationGeneric) && !(dragOperation & (NSDragOperationMove|NSDragOperationCopy|NSDragOperationLink)) )
97        result |= java_awt_dnd_DnDConstants_ACTION_MOVE;
98
99    return result;
100}
101
102+ (jint)narrowJavaDropActions:(jint)actions
103{
104    if (YES) {
105        // Order is defined in the java.awt.dnd.DropTargetDropEvent JavaDoc
106        if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) {
107            return java_awt_dnd_DnDConstants_ACTION_MOVE;
108        }
109        if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) {
110            return java_awt_dnd_DnDConstants_ACTION_COPY;
111        }
112        if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) {
113            return java_awt_dnd_DnDConstants_ACTION_LINK;
114        }
115    } else {
116        // Order is what is most intuitive on Mac OS X
117        if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) {
118            return java_awt_dnd_DnDConstants_ACTION_COPY;
119        }
120        if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) {
121            return java_awt_dnd_DnDConstants_ACTION_LINK;
122        }
123        if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) {
124            return java_awt_dnd_DnDConstants_ACTION_MOVE;
125        }
126    }
127
128    return java_awt_dnd_DnDConstants_ACTION_NONE;
129}
130
131+ (NSDragOperation)mapJavaDragOperationToNS:(jint)dragOperation
132{
133    NSDragOperation result = NSDragOperationNone;
134
135    switch (dragOperation) {
136        case java_awt_dnd_DnDConstants_ACTION_NONE:            // 0
137            result = NSDragOperationNone;
138            break;
139        case java_awt_dnd_DnDConstants_ACTION_COPY:            // 1
140            result = NSDragOperationCopy;
141            break;
142        case java_awt_dnd_DnDConstants_ACTION_MOVE:            // 2
143            result = NSDragOperationMove;
144            break;
145        case java_awt_dnd_DnDConstants_ACTION_COPY_OR_MOVE:    // 3
146            result = NSDragOperationCopy | NSDragOperationMove;
147            break;
148        case java_awt_dnd_DnDConstants_ACTION_LINK:            // 1073741824L
149            result = NSDragOperationLink;
150            break;
151        case (java_awt_dnd_DnDConstants_ACTION_COPY_OR_MOVE | java_awt_dnd_DnDConstants_ACTION_LINK):
152            result = NSDragOperationCopy | NSDragOperationMove | NSDragOperationLink;
153            break;
154    }
155
156        if (result != NSDragOperationNone) {
157            result |= NSDragOperationGeneric;
158        }
159
160    return result;
161}
162
163// Mouse and key modifiers mapping:
164+ (NSUInteger)mapJavaExtModifiersToNSMouseDownButtons:(jint)modifiers
165{
166    NSUInteger result = NSLeftMouseDown;
167
168    if ((modifiers & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) != 0)
169        result = NSLeftMouseDown;
170
171    if ((modifiers & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) != 0)
172        result = NSOtherMouseDown;
173
174    if ((modifiers & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) != 0)
175        result = NSRightMouseDown;
176
177    return result;
178}
179
180+ (NSUInteger)mapJavaExtModifiersToNSMouseUpButtons:(jint)modifiers
181{
182    NSUInteger result = NSLeftMouseUp;
183
184    if ((modifiers & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) != 0)
185        result = NSLeftMouseUp;
186
187    if ((modifiers & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) != 0)
188        result = NSOtherMouseUp;
189
190    if ((modifiers & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) != 0)
191        result = NSRightMouseUp;
192
193    return result;
194}
195
196
197// Specialized key modifiers mappings (for DragSource.operationChanged)
198
199// Returns just the key modifiers from a java modifier flag
200+ (jint)extractJavaExtKeyModifiersFromJavaExtModifiers:(jint)modifiers
201{
202    // Build the mask
203    static jint mask = java_awt_event_InputEvent_SHIFT_DOWN_MASK | java_awt_event_InputEvent_CTRL_DOWN_MASK | java_awt_event_InputEvent_META_DOWN_MASK | java_awt_event_InputEvent_ALT_DOWN_MASK;
204    //static int mask = java_awt_event_InputEvent_SHIFT_DOWN_MASK | java_awt_event_InputEvent_CTRL_DOWN_MASK;
205
206    // Get results
207    jint result = modifiers & mask;
208
209    // Java appears to have 2 ALT buttons - combine them.
210    if (modifiers & java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK)
211        result |= java_awt_event_InputEvent_ALT_DOWN_MASK;
212
213    return result;
214}
215
216// Returns just the mouse modifiers from a java modifier flag
217+ (jint)extractJavaExtMouseModifiersFromJavaExtModifiers:(jint)modifiers
218{
219    // Build the mask
220    static jint mask = java_awt_event_InputEvent_BUTTON1_DOWN_MASK | java_awt_event_InputEvent_BUTTON2_DOWN_MASK | java_awt_event_InputEvent_BUTTON3_DOWN_MASK;
221
222    // Get results
223    return modifiers & mask;
224}
225
226+ (NSDragOperation) nsDragOperationForModifiers:(NSUInteger)modifiers {
227
228    // Java first
229    if ( (modifiers & NSShiftKeyMask) && (modifiers & NSControlKeyMask) ) {
230        return NSDragOperationLink;
231    }
232    if (modifiers & NSShiftKeyMask) {
233        return NSDragOperationMove;
234    }
235    if (modifiers & NSControlKeyMask) {
236        return NSDragOperationCopy;
237    }
238
239    // Then native
240    if ( (modifiers & NSCommandKeyMask) && (modifiers & NSAlternateKeyMask) ) {
241        return NSDragOperationLink;
242    }
243    if (modifiers & NSCommandKeyMask) {
244        return NSDragOperationMove;
245    }
246    if (modifiers & NSAlternateKeyMask) {
247        return NSDragOperationCopy;
248    }
249
250    // Otherwise, we allow anything
251    return NSDragOperationEvery;
252}
253
254+ (jint) javaKeyModifiersForNSDragOperation:(NSDragOperation)dragOperation {
255    if (dragOperation & NSDragOperationMove)
256        return java_awt_event_InputEvent_SHIFT_DOWN_MASK;
257
258    if (dragOperation & NSDragOperationCopy)
259        return java_awt_event_InputEvent_CTRL_DOWN_MASK;
260
261    if (dragOperation & NSDragOperationLink) {
262        return java_awt_event_InputEvent_SHIFT_DOWN_MASK | java_awt_event_InputEvent_CTRL_DOWN_MASK;
263    }
264    return 0;
265}
266
267@end
268