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- (BOOL) checkPasteboardWithoutNotification:(id)application { 111 AWT_ASSERT_APPKIT_THREAD; 112 113 NSInteger newChangeCount = [[NSPasteboard generalPasteboard] changeCount]; 114 115 if (self.changeCount != newChangeCount) { 116 self.changeCount = newChangeCount; 117 return YES; 118 } else { 119 return NO; 120 } 121} 122 123@end 124 125/* 126 * Class: sun_lwawt_macosx_CClipboard 127 * Method: declareTypes 128 * Signature: ([JLsun/awt/datatransfer/SunClipboard;)V 129*/ 130JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_declareTypes 131(JNIEnv *env, jobject inObject, jlongArray inTypes, jobject inJavaClip) 132{ 133JNF_COCOA_ENTER(env); 134 135 jint i; 136 jint nElements = (*env)->GetArrayLength(env, inTypes); 137 NSMutableArray *formatArray = [NSMutableArray arrayWithCapacity:nElements]; 138 jlong *elements = (*env)->GetPrimitiveArrayCritical(env, inTypes, NULL); 139 140 for (i = 0; i < nElements; i++) { 141 NSString *pbFormat = formatForIndex(elements[i]); 142 if (pbFormat) 143 [formatArray addObject:pbFormat]; 144 } 145 146 (*env)->ReleasePrimitiveArrayCritical(env, inTypes, elements, JNI_ABORT); 147 [[CClipboard sharedClipboard] declareTypes:formatArray withOwner:inJavaClip jniEnv:env]; 148JNF_COCOA_EXIT(env); 149} 150 151/* 152 * Class: sun_lwawt_macosx_CClipboard 153 * Method: setData 154 * Signature: ([BJ)V 155*/ 156JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_setData 157(JNIEnv *env, jobject inObject, jbyteArray inBytes, jlong inFormat) 158{ 159 if (inBytes == NULL) { 160 return; 161 } 162 163JNF_COCOA_ENTER(env); 164 jint nBytes = (*env)->GetArrayLength(env, inBytes); 165 jbyte *rawBytes = (*env)->GetPrimitiveArrayCritical(env, inBytes, NULL); 166 CHECK_NULL(rawBytes); 167 NSData *bytesAsData = [NSData dataWithBytes:rawBytes length:nBytes]; 168 (*env)->ReleasePrimitiveArrayCritical(env, inBytes, rawBytes, JNI_ABORT); 169 NSString *format = formatForIndex(inFormat); 170 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 171 [[NSPasteboard generalPasteboard] setData:bytesAsData forType:format]; 172 }]; 173JNF_COCOA_EXIT(env); 174} 175 176/* 177 * Class: sun_lwawt_macosx_CClipboard 178 * Method: getClipboardFormats 179 * Signature: (J)[J 180 */ 181JNIEXPORT jlongArray JNICALL Java_sun_lwawt_macosx_CClipboard_getClipboardFormats 182(JNIEnv *env, jobject inObject) 183{ 184 jlongArray returnValue = NULL; 185JNF_COCOA_ENTER(env); 186 187 __block NSArray* dataTypes; 188 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 189 dataTypes = [[[NSPasteboard generalPasteboard] types] retain]; 190 }]; 191 [dataTypes autorelease]; 192 193 NSUInteger nFormats = [dataTypes count]; 194 NSUInteger knownFormats = 0; 195 NSUInteger i; 196 197 // There can be any number of formats on the general pasteboard. Find out which ones 198 // we know about (i.e., live in the flavormap.properties). 199 for (i = 0; i < nFormats; i++) { 200 NSString *format = (NSString *)[dataTypes objectAtIndex:i]; 201 if (indexForFormat(format) != -1) 202 knownFormats++; 203 } 204 205 returnValue = (*env)->NewLongArray(env, knownFormats); 206 if (returnValue == NULL) { 207 return NULL; 208 } 209 210 if (knownFormats == 0) { 211 return returnValue; 212 } 213 214 // Now go back and map the formats we found back to Java indexes. 215 jboolean isCopy; 216 jlong *lFormats = (*env)->GetLongArrayElements(env, returnValue, &isCopy); 217 jlong *saveFormats = lFormats; 218 219 for (i = 0; i < nFormats; i++) { 220 NSString *format = (NSString *)[dataTypes objectAtIndex:i]; 221 jlong index = indexForFormat(format); 222 223 if (index != -1) { 224 *lFormats = index; 225 lFormats++; 226 } 227 } 228 229 (*env)->ReleaseLongArrayElements(env, returnValue, saveFormats, JNI_COMMIT); 230JNF_COCOA_EXIT(env); 231 return returnValue; 232} 233 234/* 235 * Class: sun_lwawt_macosx_CClipboard 236 * Method: getClipboardData 237 * Signature: (JJ)[B 238 */ 239JNIEXPORT jbyteArray JNICALL Java_sun_lwawt_macosx_CClipboard_getClipboardData 240(JNIEnv *env, jobject inObject, jlong format) 241{ 242 jbyteArray returnValue = NULL; 243 244 // Note that this routine makes no attempt to interpret the data, since we're returning 245 // a byte array back to Java. CDataTransferer will do that if necessary. 246JNF_COCOA_ENTER(env); 247 248 NSString *formatAsString = formatForIndex(format); 249 __block NSData* clipData; 250 [ThreadUtilities performOnMainThreadWaiting:YES block:^() { 251 clipData = [[[NSPasteboard generalPasteboard] dataForType:formatAsString] retain]; 252 }]; 253 254 if (clipData == NULL) { 255 [JNFException raise:env as:"java/io/IOException" reason:"Font transform has NaN position"]; 256 return NULL; 257 } else { 258 [clipData autorelease]; 259 } 260 261 NSUInteger dataSize = [clipData length]; 262 returnValue = (*env)->NewByteArray(env, dataSize); 263 if (returnValue == NULL) { 264 return NULL; 265 } 266 267 if (dataSize != 0) { 268 const void *dataBuffer = [clipData bytes]; 269 (*env)->SetByteArrayRegion(env, returnValue, 0, dataSize, (jbyte *)dataBuffer); 270 } 271 272JNF_COCOA_EXIT(env); 273 return returnValue; 274} 275 276/* 277 * Class: sun_lwawt_macosx_CClipboard 278 * Method: checkPasteboard 279 * Signature: ()V 280 */ 281JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CClipboard_checkPasteboardWithoutNotification 282(JNIEnv *env, jobject inObject) 283{ 284 __block BOOL ret = NO; 285 JNF_COCOA_ENTER(env); 286 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ 287 ret = [[CClipboard sharedClipboard] checkPasteboardWithoutNotification:nil]; 288 }]; 289 290 JNF_COCOA_EXIT(env); 291 return ret; 292} 293