view src/macosx/native/sun/awt/CClipboard.m @ 8215:9cbd15400b32

8071668: [macosx] Clipboard does not work with 3rd parties Clipboard Managers Reviewed-by: ant, serb
author anashaty
date Fri, 03 Jul 2015 19:25:05 +0100
parents c85e36decad6
children
line wrap: on
line source

/*
 * Copyright (c) 2011, 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.
 */

#include "CClipboard.h"
#include "CDataTransferer.h"
#import <Cocoa/Cocoa.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>

#include "ThreadUtilities.h"


static CClipboard *sClipboard = nil;

//
// CClipboardUpdate is used for mulitple calls to setData that happen before
// the model and AppKit can get back in sync.
//

@interface CClipboardUpdate : NSObject {
    NSData *fData;
    NSString *fFormat;
}

- (id)initWithData:(NSData *)inData withFormat:(NSString *)inFormat;
- (NSData *)data;
- (NSString *)format;

@end

@implementation CClipboardUpdate

- (id)initWithData:(NSData *)inData withFormat:(NSString *)inFormat
{
    self = [super init];

    if (self != nil) {
        fData = [inData retain];
        fFormat = [inFormat retain];
    }

    return self;
}

- (void)dealloc
{
    [fData release];
    fData = nil;

    [fFormat release];
    fFormat = nil;

    [super dealloc];
}
//- (void)finalize { [super finalize]; }

- (NSData *)data {
    return fData;
}

- (NSString *)format {
    return fFormat;
}
@end

@implementation CClipboard

// Clipboard creation is synchronized at the Java level.
+ (CClipboard *) sharedClipboard
{
    if (sClipboard == nil) {
        sClipboard = [[CClipboard alloc] init];
        [[NSNotificationCenter defaultCenter] addObserver:sClipboard selector: @selector(checkPasteboard:)
                                                     name: NSApplicationDidBecomeActiveNotification
                                                   object: nil];
    }

    return sClipboard;
}

- (id) init
{
    self = [super init];

    if (self != nil) {
        fChangeCount = [[NSPasteboard generalPasteboard] changeCount];
    }

    return self;
}

- (void) javaDeclareTypes:(NSArray *)inTypes withOwner:(jobject)inClipboard jniEnv:(JNIEnv *)inEnv {

    @synchronized(self) {
        if (inClipboard != NULL) {
            if (fClipboardOwner != NULL) {
                JNFDeleteGlobalRef(inEnv, fClipboardOwner);
            }
            fClipboardOwner = JNFNewGlobalRef(inEnv, inClipboard);
        }
    }
    [ThreadUtilities performOnMainThread:@selector(_nativeDeclareTypes:) on:self withObject:inTypes waitUntilDone:YES];
}

- (void) _nativeDeclareTypes:(NSArray *)inTypes {
    AWT_ASSERT_APPKIT_THREAD;

    fChangeCount = [[NSPasteboard generalPasteboard] declareTypes:inTypes owner:self];
}


- (NSArray *) javaGetTypes {

    NSMutableArray *args = [NSMutableArray arrayWithCapacity:1];
    [ThreadUtilities performOnMainThread:@selector(_nativeGetTypes:) on:self withObject:args waitUntilDone:YES];
    return [args lastObject];
}

- (void) _nativeGetTypes:(NSMutableArray *)args {
    AWT_ASSERT_APPKIT_THREAD;

    [args addObject:[[NSPasteboard generalPasteboard] types]];
}

- (void) javaSetData:(NSData *)inData forType:(NSString *) inFormat {

    CClipboardUpdate *newUpdate = [[CClipboardUpdate alloc] initWithData:inData withFormat:inFormat];
    [ThreadUtilities performOnMainThread:@selector(_nativeSetData:) on:self withObject:newUpdate waitUntilDone:YES];
    [newUpdate release];
}

- (void) _nativeSetData:(CClipboardUpdate *)newUpdate {
    AWT_ASSERT_APPKIT_THREAD;

    [[NSPasteboard generalPasteboard] setData:[newUpdate data] forType:[newUpdate format]];
}

- (NSData *) javaGetDataForType:(NSString *) inFormat {

    NSMutableArray *args = [NSMutableArray arrayWithObject:inFormat];
    [ThreadUtilities performOnMainThread:@selector(_nativeGetDataForType:) on:self withObject:args waitUntilDone:YES];
    return [args lastObject];
}

- (void) _nativeGetDataForType:(NSMutableArray *) args {
    AWT_ASSERT_APPKIT_THREAD;

    NSData *returnValue = [[NSPasteboard generalPasteboard] dataForType:[args objectAtIndex:0]];

    if (returnValue) [args replaceObjectAtIndex:0 withObject:returnValue];
    else [args removeLastObject];
}



- (void) checkPasteboard:(id)application {
    AWT_ASSERT_APPKIT_THREAD;
    
    // 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 (fChangeCount != newChangeCount) {
        fChangeCount = newChangeCount;    

        // Notify that the content might be changed
        static JNF_CLASS_CACHE(jc_CClipboard, "sun/lwawt/macosx/CClipboard");
        static JNF_STATIC_MEMBER_CACHE(jm_contentChanged, jc_CClipboard, "notifyChanged", "()V");
        JNIEnv *env = [ThreadUtilities getJNIEnv];
        JNFCallStaticVoidMethod(env, jm_contentChanged);

        // If we have a Java pasteboard owner, tell it that it doesn't own the pasteboard anymore.
        static JNF_MEMBER_CACHE(jm_lostOwnership, jc_CClipboard, "notifyLostOwnership", "()V");
        @synchronized(self) {
            if (fClipboardOwner) {
                JNIEnv *env = [ThreadUtilities getJNIEnv];
                JNFCallVoidMethod(env, fClipboardOwner, jm_lostOwnership); // AWT_THREADING Safe (event)
                JNFDeleteGlobalRef(env, fClipboardOwner);
                fClipboardOwner = NULL;
            }
        }
    }
}

- (BOOL) checkPasteboardWithoutNotification:(id)application {
    AWT_ASSERT_APPKIT_THREAD;
    
    NSInteger newChangeCount = [[NSPasteboard generalPasteboard] changeCount];
    
    if (fChangeCount != newChangeCount) {
        fChangeCount = 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)
{
JNF_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] javaDeclareTypes:formatArray withOwner:inJavaClip jniEnv:env];
JNF_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;
    }

JNF_COCOA_ENTER(env);
    jint nBytes = (*env)->GetArrayLength(env, inBytes);
    jbyte *rawBytes = (*env)->GetPrimitiveArrayCritical(env, inBytes, NULL);
    NSData *bytesAsData = [NSData dataWithBytes:rawBytes length:nBytes];
    (*env)->ReleasePrimitiveArrayCritical(env, inBytes, rawBytes, JNI_ABORT);
    NSString *format = formatForIndex(inFormat);
    [[CClipboard sharedClipboard] javaSetData:bytesAsData forType:format];
JNF_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;
JNF_COCOA_ENTER(env);

    NSArray *dataTypes = [[CClipboard sharedClipboard] javaGetTypes];
    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);
JNF_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.
JNF_COCOA_ENTER(env);

    NSString *formatAsString = formatForIndex(format);
    NSData *clipData = [[CClipboard sharedClipboard] javaGetDataForType:formatAsString];

    if (clipData == NULL) {
        [JNFException raise:env as:"java/io/IOException" reason:"Font transform has NaN position"];
        return NULL;
    }

    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);
    }

JNF_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;
    JNF_COCOA_ENTER(env);
    [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
        ret = [[CClipboard sharedClipboard] checkPasteboardWithoutNotification:nil];
    }];
     
    JNF_COCOA_EXIT(env);
    return ret;
}