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