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