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 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 } 287 288 // Now go back and map the formats we found back to Java indexes. 289 jboolean isCopy; 290 jlong *lFormats = (*env)->GetLongArrayElements(env, returnValue, &isCopy); 291 jlong *saveFormats = lFormats; 292 293 for (i = 0; i < nFormats; i++) { 294 NSString *format = (NSString *)[dataTypes objectAtIndex:i]; 295 jlong index = indexForFormat(format); 296 297 if (index != -1) { 298 *lFormats = index; 299 lFormats++; 300 } 301 } 302 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