blob: 896e11cf568e2ed709c4683689838c10f663a5d8 [file] [log] [blame]
/*
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "CDataTransferer.h"
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
#import <Cocoa/Cocoa.h>
@interface CClipboard : NSObject { }
@property NSInteger changeCount;
@property jobject clipboardOwner;
+ (CClipboard*)sharedClipboard;
- (void)declareTypes:(NSArray *)types withOwner:(jobject)owner jniEnv:(JNIEnv*)env;
- (void)checkPasteboard:(id)sender;
@end
@implementation CClipboard
@synthesize changeCount = _changeCount;
@synthesize clipboardOwner = _clipboardOwner;
// Clipboard creation is synchronized at the Java level
+ (CClipboard*)sharedClipboard {
static CClipboard* sClipboard = nil;
if (sClipboard == nil) {
sClipboard = [[CClipboard alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:sClipboard selector: @selector(checkPasteboard:)
name: NSApplicationDidBecomeActiveNotification
object: nil];
}
return sClipboard;
}
- (id)init {
if (self = [super init]) {
self.changeCount = [[NSPasteboard generalPasteboard] changeCount];
}
return self;
}
- (void)declareTypes:(NSArray*)types withOwner:(jobject)owner jniEnv:(JNIEnv*)env {
@synchronized(self) {
if (owner != NULL) {
if (self.clipboardOwner != NULL) {
(*env)->DeleteGlobalRef(env, self.clipboardOwner);
}
self.clipboardOwner = (*env)->NewGlobalRef(env, owner);
}
}
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
self.changeCount = [[NSPasteboard generalPasteboard] declareTypes:types owner:self];
}];
}
- (void)checkPasteboard:(id)sender {
// This is called via NSApplicationDidBecomeActiveNotification.
// If the change count on the general pasteboard is different than when we set it
// someone else put data on the clipboard. That means the current owner lost ownership.
NSInteger newChangeCount = [[NSPasteboard generalPasteboard] changeCount];
if (self.changeCount != newChangeCount) {
self.changeCount = newChangeCount;
JNIEnv *env = [ThreadUtilities getJNIEnv];
// Notify that the content might be changed
DECLARE_CLASS(jc_CClipboard, "sun/lwawt/macosx/CClipboard");
DECLARE_STATIC_METHOD(jm_contentChanged, jc_CClipboard, "notifyChanged", "()V");
(*env)->CallStaticVoidMethod(env, jc_CClipboard, jm_contentChanged);
CHECK_EXCEPTION();
// If we have a Java pasteboard owner, tell it that it doesn't own the pasteboard anymore.
DECLARE_METHOD(jm_lostOwnership, jc_CClipboard, "notifyLostOwnership", "()V");
@synchronized(self) {
if (self.clipboardOwner) {
(*env)->CallVoidMethod(env, self.clipboardOwner, jm_lostOwnership);
CHECK_EXCEPTION();
(*env)->DeleteGlobalRef(env, self.clipboardOwner);
self.clipboardOwner = NULL;
}
}
}
}
- (BOOL) checkPasteboardWithoutNotification:(id)application {
AWT_ASSERT_APPKIT_THREAD;
NSInteger newChangeCount = [[NSPasteboard generalPasteboard] changeCount];
if (self.changeCount != newChangeCount) {
self.changeCount = newChangeCount;
return YES;
} else {
return NO;
}
}
@end
/*
* Class: sun_lwawt_macosx_CClipboard
* Method: declareTypes
* Signature: ([JLsun/awt/datatransfer/SunClipboard;)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_declareTypes
(JNIEnv *env, jobject inObject, jlongArray inTypes, jobject inJavaClip)
{
JNI_COCOA_ENTER(env);
jint i;
jint nElements = (*env)->GetArrayLength(env, inTypes);
NSMutableArray *formatArray = [NSMutableArray arrayWithCapacity:nElements];
jlong *elements = (*env)->GetPrimitiveArrayCritical(env, inTypes, NULL);
for (i = 0; i < nElements; i++) {
NSString *pbFormat = formatForIndex(elements[i]);
if (pbFormat)
[formatArray addObject:pbFormat];
}
(*env)->ReleasePrimitiveArrayCritical(env, inTypes, elements, JNI_ABORT);
[[CClipboard sharedClipboard] declareTypes:formatArray withOwner:inJavaClip jniEnv:env];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CClipboard
* Method: setData
* Signature: ([BJ)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_setData
(JNIEnv *env, jobject inObject, jbyteArray inBytes, jlong inFormat)
{
if (inBytes == NULL) {
return;
}
JNI_COCOA_ENTER(env);
jint nBytes = (*env)->GetArrayLength(env, inBytes);
jbyte *rawBytes = (*env)->GetPrimitiveArrayCritical(env, inBytes, NULL);
CHECK_NULL(rawBytes);
NSData *bytesAsData = [NSData dataWithBytes:rawBytes length:nBytes];
(*env)->ReleasePrimitiveArrayCritical(env, inBytes, rawBytes, JNI_ABORT);
NSString *format = formatForIndex(inFormat);
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
[[NSPasteboard generalPasteboard] setData:bytesAsData forType:format];
}];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CClipboard
* Method: getClipboardFormats
* Signature: (J)[J
*/
JNIEXPORT jlongArray JNICALL Java_sun_lwawt_macosx_CClipboard_getClipboardFormats
(JNIEnv *env, jobject inObject)
{
jlongArray returnValue = NULL;
JNI_COCOA_ENTER(env);
__block NSArray* dataTypes;
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
dataTypes = [[[NSPasteboard generalPasteboard] types] retain];
}];
[dataTypes autorelease];
NSUInteger nFormats = [dataTypes count];
NSUInteger knownFormats = 0;
NSUInteger i;
// There can be any number of formats on the general pasteboard. Find out which ones
// we know about (i.e., live in the flavormap.properties).
for (i = 0; i < nFormats; i++) {
NSString *format = (NSString *)[dataTypes objectAtIndex:i];
if (indexForFormat(format) != -1)
knownFormats++;
}
returnValue = (*env)->NewLongArray(env, knownFormats);
if (returnValue == NULL) {
return NULL;
}
if (knownFormats == 0) {
return returnValue;
}
// Now go back and map the formats we found back to Java indexes.
jboolean isCopy;
jlong *lFormats = (*env)->GetLongArrayElements(env, returnValue, &isCopy);
jlong *saveFormats = lFormats;
for (i = 0; i < nFormats; i++) {
NSString *format = (NSString *)[dataTypes objectAtIndex:i];
jlong index = indexForFormat(format);
if (index != -1) {
*lFormats = index;
lFormats++;
}
}
(*env)->ReleaseLongArrayElements(env, returnValue, saveFormats, JNI_COMMIT);
JNI_COCOA_EXIT(env);
return returnValue;
}
/*
* Class: sun_lwawt_macosx_CClipboard
* Method: getClipboardData
* Signature: (JJ)[B
*/
JNIEXPORT jbyteArray JNICALL Java_sun_lwawt_macosx_CClipboard_getClipboardData
(JNIEnv *env, jobject inObject, jlong format)
{
jbyteArray returnValue = NULL;
// Note that this routine makes no attempt to interpret the data, since we're returning
// a byte array back to Java. CDataTransferer will do that if necessary.
JNI_COCOA_ENTER(env);
NSString *formatAsString = formatForIndex(format);
__block NSData* clipData;
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
clipData = [[[NSPasteboard generalPasteboard] dataForType:formatAsString] retain];
}];
if (clipData == NULL) {
JNU_ThrowIOException(env, "Font transform has NaN position");
return NULL;
} else {
[clipData autorelease];
}
NSUInteger dataSize = [clipData length];
returnValue = (*env)->NewByteArray(env, dataSize);
if (returnValue == NULL) {
return NULL;
}
if (dataSize != 0) {
const void *dataBuffer = [clipData bytes];
(*env)->SetByteArrayRegion(env, returnValue, 0, dataSize, (jbyte *)dataBuffer);
}
JNI_COCOA_EXIT(env);
return returnValue;
}
/*
* Class: sun_lwawt_macosx_CClipboard
* Method: checkPasteboard
* Signature: ()V
*/
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CClipboard_checkPasteboardWithoutNotification
(JNIEnv *env, jobject inObject)
{
__block BOOL ret = NO;
JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
ret = [[CClipboard sharedClipboard] checkPasteboardWithoutNotification:nil];
}];
JNI_COCOA_EXIT(env);
return ret;
}