1 /* 2 * Copyright (c) 2011, 2014, 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 #import "CDataTransferer.h" 27 #import "ThreadUtilities.h" 28 #import "jni_util.h" 29 #import <Cocoa/Cocoa.h> 30 #import <JavaNativeFoundation/JavaNativeFoundation.h> 31 32 @interface CClipboard : NSObject { } 33 @property NSInteger changeCount; 34 @property jobject clipboardOwner; 35 36 + (CClipboard*)sharedClipboard; 37 - (void)declareTypes:(NSArray *)types withOwner:(jobject)owner jniEnv:(JNIEnv*)env; 38 - (void)checkPasteboard:(id)sender; 39 @end 40 41 @implementation CClipboard 42 @synthesize changeCount = _changeCount; 43 @synthesize clipboardOwner = _clipboardOwner; 44 45 // Clipboard creation is synchronized at the Java level 46 + (CClipboard*)sharedClipboard { 47 static CClipboard* sClipboard = nil; 48 if (sClipboard == nil) { 49 sClipboard = [[CClipboard alloc] init]; 50 [[NSNotificationCenter defaultCenter] addObserver:sClipboard selector: @selector(checkPasteboard:) 51 name: NSApplicationDidBecomeActiveNotification 52 object: nil]; 53 } 54 55 return sClipboard; 56 } 57 58 - (id)init { 59 if (self = [super init]) { 60 self.changeCount = [[NSPasteboard generalPasteboard] changeCount]; 61 } 62 return self; 63 } 64 65 - (void)declareTypes:(NSArray*)types withOwner:(jobject)owner jniEnv:(JNIEnv*)env { 66 @synchronized(self) { 67 if (owner != NULL) { 68 if (self.clipboardOwner != NULL) { 69 JNFDeleteGlobalRef(env, self.clipboardOwner); 70 } 71 self.clipboardOwner = JNFNewGlobalRef(env, owner); 72 } 73 } 74 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 75 self.changeCount = [[NSPasteboard generalPasteboard] declareTypes:types owner:self]; 76 }]; 77 } 78 79 - (void)checkPasteboard:(id)sender { 80 81 // This is called via NSApplicationDidBecomeActiveNotification. 82 83 // If the change count on the general pasteboard is different than when we set it 84 // someone else put data on the clipboard. That means the current owner lost ownership. 85 86 NSInteger newChangeCount = [[NSPasteboard generalPasteboard] changeCount]; 87 88 if (self.changeCount != newChangeCount) { 89 self.changeCount = newChangeCount; 90 91 // Notify that the content might be changed 92 static JNF_CLASS_CACHE(jc_CClipboard, "sun/lwawt/macosx/CClipboard"); 93 static JNF_STATIC_MEMBER_CACHE(jm_contentChanged, jc_CClipboard, "notifyChanged", "()V"); 94 JNIEnv *env = [ThreadUtilities getJNIEnv]; 95 JNFCallStaticVoidMethod(env, jm_contentChanged); 96 97 // If we have a Java pasteboard owner, tell it that it doesn't own the pasteboard anymore. 98 static JNF_MEMBER_CACHE(jm_lostOwnership, jc_CClipboard, "notifyLostOwnership", "()V"); 99 @synchronized(self) { 100 if (self.clipboardOwner) { 101 JNIEnv *env = [ThreadUtilities getJNIEnv]; 102 JNFCallVoidMethod(env, self.clipboardOwner, jm_lostOwnership); // AWT_THREADING Safe (event) 103 JNFDeleteGlobalRef(env, self.clipboardOwner); 104 self.clipboardOwner = NULL; 105 } 106 } 107 } 108 } 109 110 @end 111 112 /* 113 * Class: sun_lwawt_macosx_CClipboard 114 * Method: declareTypes 115 * Signature: ([JLsun/awt/datatransfer/SunClipboard;)V 116 */ 117 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_declareTypes 118 (JNIEnv *env, jobject inObject, jlongArray inTypes, jobject inJavaClip) 119 { 120 JNF_COCOA_ENTER(env); 121 122 jint i; 123 jint nElements = (*env)->GetArrayLength(env, inTypes); 124 NSMutableArray *formatArray = [NSMutableArray arrayWithCapacity:nElements]; 125 jlong *elements = (*env)->GetPrimitiveArrayCritical(env, inTypes, NULL); 126 127 for (i = 0; i < nElements; i++) { 128 NSString *pbFormat = formatForIndex(elements[i]); 129 if (pbFormat) 130 [formatArray addObject:pbFormat]; 131 } 132 133 (*env)->ReleasePrimitiveArrayCritical(env, inTypes, elements, JNI_ABORT); 134 [[CClipboard sharedClipboard] declareTypes:formatArray withOwner:inJavaClip jniEnv:env]; 135 JNF_COCOA_EXIT(env); 136 } 137 138 /* 139 * Class: sun_lwawt_macosx_CClipboard 140 * Method: setData 141 * Signature: ([BJ)V 142 */ 143 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_setData 144 (JNIEnv *env, jobject inObject, jbyteArray inBytes, jlong inFormat) 145 { 146 if (inBytes == NULL) { 147 return; 148 } 149 150 JNF_COCOA_ENTER(env); 151 jint nBytes = (*env)->GetArrayLength(env, inBytes); 152 jbyte *rawBytes = (*env)->GetPrimitiveArrayCritical(env, inBytes, NULL); 153 CHECK_NULL(rawBytes); 154 NSData *bytesAsData = [NSData dataWithBytes:rawBytes length:nBytes]; 155 (*env)->ReleasePrimitiveArrayCritical(env, inBytes, rawBytes, JNI_ABORT); 156 NSString *format = formatForIndex(inFormat); 157 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 158 [[NSPasteboard generalPasteboard] setData:bytesAsData forType:format]; 159 }]; 160 JNF_COCOA_EXIT(env); 161 } 162 163 /* 164 * Class: sun_lwawt_macosx_CClipboard 165 * Method: getClipboardFormats 166 * Signature: (J)[J 167 */ 168 JNIEXPORT jlongArray JNICALL Java_sun_lwawt_macosx_CClipboard_getClipboardFormats 169 (JNIEnv *env, jobject inObject) 170 { 171 jlongArray returnValue = NULL; 172 JNF_COCOA_ENTER(env); 173 174 __block NSArray* dataTypes; 175 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 176 dataTypes = [[[NSPasteboard generalPasteboard] types] retain]; 177 }]; 178 [dataTypes autorelease]; 179 180 NSUInteger nFormats = [dataTypes count]; 181 NSUInteger knownFormats = 0; 182 NSUInteger i; 183 184 // There can be any number of formats on the general pasteboard. Find out which ones 185 // we know about (i.e., live in the flavormap.properties). 186 for (i = 0; i < nFormats; i++) { 187 NSString *format = (NSString *)[dataTypes objectAtIndex:i]; 188 if (indexForFormat(format) != -1) 189 knownFormats++; 190 } 191 192 returnValue = (*env)->NewLongArray(env, knownFormats); 193 if (returnValue == NULL) { 194 return NULL; 195 } 196 197 if (knownFormats == 0) { 198 return returnValue; 199 } 200 201 // Now go back and map the formats we found back to Java indexes. 202 jboolean isCopy; 203 jlong *lFormats = (*env)->GetLongArrayElements(env, returnValue, &isCopy); 204 jlong *saveFormats = lFormats; 205 206 for (i = 0; i < nFormats; i++) { 207 NSString *format = (NSString *)[dataTypes objectAtIndex:i]; 208 jlong index = indexForFormat(format); 209 210 if (index != -1) { 211 *lFormats = index; 212 lFormats++; 213 } 214 } 215 216 (*env)->ReleaseLongArrayElements(env, returnValue, saveFormats, JNI_COMMIT); 217 JNF_COCOA_EXIT(env); 218 return returnValue; 219 } 220 221 /* 222 * Class: sun_lwawt_macosx_CClipboard 223 * Method: getClipboardData 224 * Signature: (JJ)[B 225 */ 226 JNIEXPORT jbyteArray JNICALL Java_sun_lwawt_macosx_CClipboard_getClipboardData 227 (JNIEnv *env, jobject inObject, jlong format) 228 { 229 jbyteArray returnValue = NULL; 230 231 // Note that this routine makes no attempt to interpret the data, since we're returning 232 // a byte array back to Java. CDataTransferer will do that if necessary. 233 JNF_COCOA_ENTER(env); 234 235 NSString *formatAsString = formatForIndex(format); 236 __block NSData* clipData; 237 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 238 clipData = [[[NSPasteboard generalPasteboard] dataForType:formatAsString] retain]; 239 }]; 240 241 if (clipData == NULL) { 242 [JNFException raise:env as:"java/io/IOException" reason:"Font transform has NaN position"]; 243 return NULL; 244 } else { 245 [clipData autorelease]; 246 } 247 248 NSUInteger dataSize = [clipData length]; 249 returnValue = (*env)->NewByteArray(env, dataSize); 250 if (returnValue == NULL) { 251 return NULL; 252 } 253 254 if (dataSize != 0) { 255 const void *dataBuffer = [clipData bytes]; 256 (*env)->SetByteArrayRegion(env, returnValue, 0, dataSize, (jbyte *)dataBuffer); 257 } 258 259 JNF_COCOA_EXIT(env); 260 return returnValue; 261 } 262 263 /* 264 * Class: sun_lwawt_macosx_CClipboard 265 * Method: checkPasteboard 266 * Signature: ()V 267 */ 268 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_checkPasteboard 269 (JNIEnv *env, jobject inObject ) 270 { 271 JNF_COCOA_ENTER(env); 272 273 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 274 [[CClipboard sharedClipboard] checkPasteboard:nil]; 275 }]; 276 277 JNF_COCOA_EXIT(env); 278 } 279 280