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