JDK14/Java14源码在线阅读

/*
 * Copyright (c) 2011, 2019, 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 "jni_util.h"
#import "CGLGraphicsConfig.h"
#import "AWTView.h"
#import "AWTWindow.h"
#import "JavaComponentAccessibility.h"
#import "JavaTextAccessibility.h"
#import "JavaAccessibilityUtilities.h"
#import "GeomUtilities.h"
#import "OSVersion.h"
#import "ThreadUtilities.h"

#import <Carbon/Carbon.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>

@interface AWTView()
@property (retain) CDropTarget *_dropTarget;
@property (retain) CDragSource *_dragSource;

-(void) deliverResize: (NSRect) rect;
-(void) resetTrackingArea;
-(void) deliverJavaKeyEventHelper: (NSEvent*) event;
-(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint;
-(NSMutableString *) parseString : (id) complexString;
@end

// Uncomment this line to see fprintfs of each InputMethod API being called on this View
//#define IM_DEBUG TRUE
//#define EXTRA_DEBUG

static BOOL shouldUsePressAndHold() {
    static int shouldUsePressAndHold = -1;
    if (shouldUsePressAndHold != -1) return shouldUsePressAndHold;
    shouldUsePressAndHold = !isSnowLeopardOrLower();
    return shouldUsePressAndHold;
}

@implementation AWTView

@synthesize _dropTarget;
@synthesize _dragSource;
@synthesize cglLayer;
@synthesize mouseIsOver;

// Note: Must be called on main (AppKit) thread only
- (id) initWithRect: (NSRect) rect
       platformView: (jobject) cPlatformView
        windowLayer: (CALayer*) windowLayer
{
    AWT_ASSERT_APPKIT_THREAD;
    // Initialize ourselves
    self = [super initWithFrame: rect];
    if (self == nil) return self;

    m_cPlatformView = cPlatformView;
    fInputMethodLOCKABLE = NULL;
    fKeyEventsNeeded = NO;
    fProcessingKeystroke = NO;

    fEnablePressAndHold = shouldUsePressAndHold();
    fInPressAndHold = NO;
    fPAHNeedsToSelect = NO;

    mouseIsOver = NO;
    [self resetTrackingArea];
    [self setAutoresizesSubviews:NO];

    if (windowLayer != nil) {
        self.cglLayer = windowLayer;
        //Layer hosting view
        [self setLayer: cglLayer];
        [self setWantsLayer: YES];
        //Layer backed view
        //[self.layer addSublayer: (CALayer *)cglLayer];
        //[self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize];
        //[self setLayerContentsPlacement: NSViewLayerContentsPlacementTopLeft];
        //[self setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable];
    }

    return self;
}

- (void) dealloc {
    AWT_ASSERT_APPKIT_THREAD;

    self.cglLayer = nil;

    JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
    (*env)->DeleteWeakGlobalRef(env, m_cPlatformView);
    m_cPlatformView = NULL;

    if (fInputMethodLOCKABLE != NULL)
    {
        JNIEnv *env = [ThreadUtilities getJNIEnvUncached];

        JNFDeleteGlobalRef(env, fInputMethodLOCKABLE);
        fInputMethodLOCKABLE = NULL;
    }

    if (rolloverTrackingArea != nil) {
        [self removeTrackingArea:rolloverTrackingArea];
        [rolloverTrackingArea release];
        rolloverTrackingArea = nil;
    }

    [super dealloc];
}

- (void) viewDidMoveToWindow {
    AWT_ASSERT_APPKIT_THREAD;

    [AWTToolkit eventCountPlusPlus];

    [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^() {
        [[self window] makeFirstResponder: self];
    }];
    if ([self window] != NULL) {
        [self resetTrackingArea];
    }
}

- (BOOL) acceptsFirstMouse: (NSEvent *)event {
    return YES;
}

- (BOOL) acceptsFirstResponder {
    return YES;
}

- (BOOL) becomeFirstResponder {
    return YES;
}

- (BOOL) preservesContentDuringLiveResize {
    return YES;
}

/*
 * Automatically triggered functions.
 */

- (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize {
    [super resizeWithOldSuperviewSize: oldBoundsSize];
    [self deliverResize: [self frame]];
}

/*
 * MouseEvents support
 */

- (void) mouseDown: (NSEvent *)event {
    NSInputManager *inputManager = [NSInputManager currentInputManager];
    if ([inputManager wantsToHandleMouseEvents]) {
#if IM_DEBUG
        NSLog(@"-> IM wants to handle event");
#endif
        if (![inputManager handleMouseEvent:event]) {
            [self deliverJavaMouseEvent: event];
        } else {
#if IM_DEBUG
            NSLog(@"-> Event was handled.");
#endif
        }
    } else {
#if IM_DEBUG
        NSLog(@"-> IM does not want to handle event");
#endif
        [self deliverJavaMouseEvent: event];
    }
}

- (void) mouseUp: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) rightMouseDown: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) rightMouseUp: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) otherMouseDown: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) otherMouseUp: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) mouseMoved: (NSEvent *)event {
    // TODO: better way to redirect move events to the "under" view

    NSPoint eventLocation = [event locationInWindow];
    NSPoint localPoint = [self convertPoint: eventLocation fromView: nil];

    if  ([self mouse: localPoint inRect: [self bounds]]) {
        [self deliverJavaMouseEvent: event];
    } else {
        [[self nextResponder] mouseDown:event];
    }
}

- (void) mouseDragged: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) rightMouseDragged: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) otherMouseDragged: (NSEvent *)event {
    [self deliverJavaMouseEvent: event];
}

- (void) mouseEntered: (NSEvent *)event {
    [[self window] setAcceptsMouseMovedEvents:YES];
    //[[self window] makeFirstResponder:self];
    [self deliverJavaMouseEvent: event];
}

- (void) mouseExited: (NSEvent *)event {
    [[self window] setAcceptsMouseMovedEvents:NO];
    [self deliverJavaMouseEvent: event];
    //Restore the cursor back.
    //[CCursorManager _setCursor: [NSCursor arrowCursor]];
}

- (void) scrollWheel: (NSEvent*) event {
    [self deliverJavaMouseEvent: event];
}

/*
 * KeyEvents support
 */

- (void) keyDown: (NSEvent *)event {
    fProcessingKeystroke = YES;
    fKeyEventsNeeded = YES;

    // Allow TSM to look at the event and potentially send back NSTextInputClient messages.
    [self interpretKeyEvents:[NSArray arrayWithObject:event]];

    if (fEnablePressAndHold && [event willBeHandledByComplexInputMethod] &&
        fInputMethodLOCKABLE)
    {
        fProcessingKeystroke = NO;
        if (!fInPressAndHold) {
            fInPressAndHold = YES;
            fPAHNeedsToSelect = YES;
        } else {
            // Abandon input to reset IM and unblock input after canceling
            // input accented symbols

            switch([event keyCode]) {
                case kVK_Escape:
                case kVK_Delete:
                case kVK_Return:
                case kVK_ForwardDelete:
                case kVK_PageUp:
                case kVK_PageDown:
                case kVK_DownArrow:
                case kVK_UpArrow:
                case kVK_Home:
                case kVK_End:
                   [self abandonInput];
                   break;
            }
        }
        return;
    }

    NSString *eventCharacters = [event characters];
    BOOL isDeadKey = (eventCharacters != nil && [eventCharacters length] == 0);

    if ((![self hasMarkedText] && fKeyEventsNeeded) || isDeadKey) {
        [self deliverJavaKeyEventHelper: event];
    }

    fProcessingKeystroke = NO;
}

- (void) keyUp: (NSEvent *)event {
    [self deliverJavaKeyEventHelper: event];
}

- (void) flagsChanged: (NSEvent *)event {
    [self deliverJavaKeyEventHelper: event];
}

- (BOOL) performKeyEquivalent: (NSEvent *) event {
    // if IM is active key events should be ignored
    if (![self hasMarkedText] && !fInPressAndHold) {
        [self deliverJavaKeyEventHelper: event];
    }

    // Workaround for 8020209: special case for "Cmd =" and "Cmd ."
    // because Cocoa calls performKeyEquivalent twice for these keystrokes
    NSUInteger modFlags = [event modifierFlags] &
    (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
    if (modFlags == NSCommandKeyMask) {
        NSString *eventChars = [event charactersIgnoringModifiers];
        if ([eventChars length] == 1) {
            unichar ch = [eventChars characterAtIndex:0];
            if (ch == '=' || ch == '.') {
                [[NSApp mainMenu] performKeyEquivalent: event];
                return YES;
            }
        }

    }

    return NO;
}

/**
 * Utility methods and accessors
 */

-(void) deliverJavaMouseEvent: (NSEvent *) event {
    BOOL isEnabled = YES;
    NSWindow* window = [self window];
    if ([window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]]) {
        isEnabled = [(AWTWindow*)[window delegate] isEnabled];
    }

    if (!isEnabled) {
        return;
    }

    NSEventType type = [event type];

    // check synthesized mouse entered/exited events
    if ((type == NSMouseEntered && mouseIsOver) || (type == NSMouseExited && !mouseIsOver)) {
        return;
    }else if ((type == NSMouseEntered && !mouseIsOver) || (type == NSMouseExited && mouseIsOver)) {
        mouseIsOver = !mouseIsOver;
    }

    [AWTToolkit eventCountPlusPlus];

    JNIEnv *env = [ThreadUtilities getJNIEnv];

    NSPoint eventLocation = [event locationInWindow];
    NSPoint localPoint = [self convertPoint: eventLocation fromView: nil];
    NSPoint absP = [NSEvent mouseLocation];

    // Convert global numbers between Cocoa's coordinate system and Java.
    // TODO: need consitent way for doing that both with global as well as with local coordinates.
    // The reason to do it here is one more native method for getting screen dimension otherwise.

    NSRect screenRect = [[[NSScreen screens] objectAtIndex:0] frame];
    absP.y = screenRect.size.height - absP.y;
    jint clickCount;

    if (type == NSMouseEntered ||
        type == NSMouseExited ||
        type == NSScrollWheel ||
        type == NSMouseMoved) {
        clickCount = 0;
    } else {
        clickCount = [event clickCount];
    }

    jdouble deltaX = [event deltaX];
    jdouble deltaY = [event deltaY];
    if ([AWTToolkit hasPreciseScrollingDeltas: event]) {
        deltaX = [event scrollingDeltaX] * 0.1;
        deltaY = [event scrollingDeltaY] * 0.1;
    }

    static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
    static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDDI)V");
    jobject jEvent = JNFNewObject(env, jctor_NSEvent,
                                  [event type],
                                  [event modifierFlags],
                                  clickCount,
                                  [event buttonNumber],
                                  (jint)localPoint.x, (jint)localPoint.y,
                                  (jint)absP.x, (jint)absP.y,
                                  deltaY,
                                  deltaX,
                                  [AWTToolkit scrollStateWithEvent: event]);
    CHECK_NULL(jEvent);

    static JNF_CLASS_CACHE(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
    static JNF_MEMBER_CACHE(jm_deliverMouseEvent, jc_PlatformView, "deliverMouseEvent", "(Lsun/lwawt/macosx/NSEvent;)V");
    jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
    if (!(*env)->IsSameObject(env, jlocal, NULL)) {
        JNFCallVoidMethod(env, jlocal, jm_deliverMouseEvent, jEvent);
        (*env)->DeleteLocalRef(env, jlocal);
    }
    (*env)->DeleteLocalRef(env, jEvent);
}

- (void) resetTrackingArea {
    if (rolloverTrackingArea != nil) {
        [self removeTrackingArea:rolloverTrackingArea];
        [rolloverTrackingArea release];
    }

    int options = (NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited |
                   NSTrackingMouseMoved | NSTrackingEnabledDuringMouseDrag);

    rolloverTrackingArea = [[NSTrackingArea alloc] initWithRect:[self visibleRect]
                                                        options: options
                                                          owner:self
                                                       userInfo:nil
                            ];
    [self addTrackingArea:rolloverTrackingArea];
}

- (void)updateTrackingAreas {
    [super updateTrackingAreas];
    [self resetTrackingArea];
}

- (void) resetCursorRects {
    [super resetCursorRects];
    [self resetTrackingArea];
}

-(void) deliverJavaKeyEventHelper: (NSEvent *) event {
    static NSEvent* sLastKeyEvent = nil;
    if (event == sLastKeyEvent) {
        // The event is repeatedly delivered by keyDown: after performKeyEquivalent:
        return;
    }
    [sLastKeyEvent release];
    sLastKeyEvent = [event retain];

    [AWTToolkit eventCountPlusPlus];
    JNIEnv *env = [ThreadUtilities getJNIEnv];

    jstring characters = NULL;
    jstring charactersIgnoringModifiers = NULL;
    if ([event type] != NSFlagsChanged) {
        characters = JNFNSToJavaString(env, [event characters]);
        charactersIgnoringModifiers = JNFNSToJavaString(env, [event charactersIgnoringModifiers]);
    }

    static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
    static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IISLjava/lang/String;Ljava/lang/String;)V");
    jobject jEvent = JNFNewObject(env, jctor_NSEvent,
                                  [event type],
                                  [event modifierFlags],
                                  [event keyCode],
                                  characters,
                                  charactersIgnoringModifiers);
    CHECK_NULL(jEvent);

    static JNF_CLASS_CACHE(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
    static JNF_MEMBER_CACHE(jm_deliverKeyEvent, jc_PlatformView,
                            "deliverKeyEvent", "(Lsun/lwawt/macosx/NSEvent;)V");
    jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
    if (!(*env)->IsSameObject(env, jlocal, NULL)) {
        JNFCallVoidMethod(env, jlocal, jm_deliverKeyEvent, jEvent);
        (*env)->DeleteLocalRef(env, jlocal);
    }
    if (characters != NULL) {
        (*env)->DeleteLocalRef(env, characters);
    }
    (*env)->DeleteLocalRef(env, jEvent);
}

-(void) deliverResize: (NSRect) rect {
    jint x = (jint) rect.origin.x;
    jint y = (jint) rect.origin.y;
    jint w = (jint) rect.size.width;
    jint h = (jint) rect.size.height;
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    static JNF_CLASS_CACHE(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
    static JNF_MEMBER_CACHE(jm_deliverResize, jc_PlatformView, "deliverResize", "(IIII)V");

    jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
    if (!(*env)->IsSameObject(env, jlocal, NULL)) {
        JNFCallVoidMethod(env, jlocal, jm_deliverResize, x,y,w,h);
        (*env)->DeleteLocalRef(env, jlocal);
    }
}


- (void) drawRect:(NSRect)dirtyRect {
    AWT_ASSERT_APPKIT_THREAD;

    [super drawRect:dirtyRect];
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    if (env != NULL) {
        /*
         if ([self inLiveResize]) {
         NSRect rs[4];
         NSInteger count;
         [self getRectsExposedDuringLiveResize:rs count:&count];
         for (int i = 0; i < count; i++) {
         JNU_CallMethodByName(env, NULL, [m_awtWindow cPlatformView],
         "deliverWindowDidExposeEvent", "(FFFF)V",
         (jfloat)rs[i].origin.x, (jfloat)rs[i].origin.y,
         (jfloat)rs[i].size.width, (jfloat)rs[i].size.height);
         if ((*env)->ExceptionOccurred(env)) {
         (*env)->ExceptionDescribe(env);
         (*env)->ExceptionClear(env);
         }
         }
         } else {
         */
        static JNF_CLASS_CACHE(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView");
        static JNF_MEMBER_CACHE(jm_deliverWindowDidExposeEvent, jc_CPlatformView, "deliverWindowDidExposeEvent", "()V");
        jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
        if (!(*env)->IsSameObject(env, jlocal, NULL)) {
            JNFCallVoidMethod(env, jlocal, jm_deliverWindowDidExposeEvent);
            (*env)->DeleteLocalRef(env, jlocal);
        }
        /*
         }
         */
    }
}

-(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint {
    if ((codePoint == 0x0024) || (codePoint == 0x00A3) ||
        (codePoint == 0x00A5) ||
        ((codePoint >= 0x20A3) && (codePoint <= 0x20BF)) ||
	((codePoint >= 0x3000) && (codePoint <= 0x303F)) ||
        ((codePoint >= 0xFF00) && (codePoint <= 0xFFEF))) {
        // Code point is in 'CJK Symbols and Punctuation' or
        // 'Halfwidth and Fullwidth Forms' Unicode block or
	// currency symbols unicode
        return YES;
    }
    return NO;
}

-(NSMutableString *) parseString : (id) complexString {
    if ([complexString isKindOfClass:[NSString class]]) {
        return [complexString mutableCopy];
    }
    else {
        return [complexString mutableString];
    }
}

// NSAccessibility support
- (jobject)awtComponent:(JNIEnv*)env
{
    static JNF_CLASS_CACHE(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView");
    static JNF_MEMBER_CACHE(jf_Peer, jc_CPlatformView, "peer", "Lsun/lwawt/LWWindowPeer;");
    if ((env == NULL) || (m_cPlatformView == NULL)) {
        NSLog(@"Apple AWT : Error AWTView:awtComponent given bad parameters.");
        if (env != NULL)
        {
            JNFDumpJavaStack(env);
        }
        return NULL;
    }

    jobject peer = NULL;
    jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
    if (!(*env)->IsSameObject(env, jlocal, NULL)) {
        peer = JNFGetObjectField(env, jlocal, jf_Peer);
        (*env)->DeleteLocalRef(env, jlocal);
    }
    static JNF_CLASS_CACHE(jc_LWWindowPeer, "sun/lwawt/LWWindowPeer");
    static JNF_MEMBER_CACHE(jf_Target, jc_LWWindowPeer, "target", "Ljava/awt/Component;");
    if (peer == NULL) {
        NSLog(@"Apple AWT : Error AWTView:awtComponent got null peer from CPlatformView");
        JNFDumpJavaStack(env);
        return NULL;
    }
    jobject comp = JNFGetObjectField(env, peer, jf_Target);
    (*env)->DeleteLocalRef(env, peer);
    return comp;
}

+ (AWTView *) awtView:(JNIEnv*)env ofAccessible:(jobject)jaccessible
{
    static JNF_STATIC_MEMBER_CACHE(jm_getAWTView, sjc_CAccessibility, "getAWTView", "(Ljavax/accessibility/Accessible;)J");

    jlong jptr = JNFCallStaticLongMethod(env, jm_getAWTView, jaccessible);
    if (jptr == 0) return nil;

    return (AWTView *)jlong_to_ptr(jptr);
}

- (id)getAxData:(JNIEnv*)env
{
    jobject jcomponent = [self awtComponent:env];
    id ax = [[[JavaComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease];
    (*env)->DeleteLocalRef(env, jcomponent);
    return ax;
}

- (NSArray *)accessibilityAttributeNames
{
    return [[super accessibilityAttributeNames] arrayByAddingObject:NSAccessibilityChildrenAttribute];
}

// NSAccessibility messages
// attribute methods
- (id)accessibilityAttributeValue:(NSString *)attribute
{
    AWT_ASSERT_APPKIT_THREAD;

    if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
    {
        JNIEnv *env = [ThreadUtilities getJNIEnv];

        (*env)->PushLocalFrame(env, 4);

        id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]);

        (*env)->PopLocalFrame(env, NULL);

        return result;
    }
    else
    {
        return [super accessibilityAttributeValue:attribute];
    }
}
- (BOOL)accessibilityIsIgnored
{
    return YES;
}

- (id)accessibilityHitTest:(NSPoint)point
{
    AWT_ASSERT_APPKIT_THREAD;
    JNIEnv *env = [ThreadUtilities getJNIEnv];

    (*env)->PushLocalFrame(env, 4);

    id result = [[self getAxData:env] accessibilityHitTest:point withEnv:env];

    (*env)->PopLocalFrame(env, NULL);

    return result;
}

- (id)accessibilityFocusedUIElement
{
    AWT_ASSERT_APPKIT_THREAD;

    JNIEnv *env = [ThreadUtilities getJNIEnv];

    (*env)->PushLocalFrame(env, 4);

    id result = [[self getAxData:env] accessibilityFocusedUIElement];

    (*env)->PopLocalFrame(env, NULL);

    return result;
}

// --- Services menu support for lightweights ---

// finds the focused accessible element, and if it is a text element, obtains the text from it
- (NSString *)accessibleSelectedText
{
    id focused = [self accessibilityFocusedUIElement];
    if (![focused isKindOfClass:[JavaTextAccessibility class]]) return nil;
    return [(JavaTextAccessibility *)focused accessibilitySelectedTextAttribute];
}

// same as above, but converts to RTFD
- (NSData *)accessibleSelectedTextAsRTFD
{
    NSString *selectedText = [self accessibleSelectedText];
    NSAttributedString *styledText = [[NSAttributedString alloc] initWithString:selectedText];
    NSData *rtfdData = [styledText RTFDFromRange:NSMakeRange(0, [styledText length])
                              documentAttributes:
                                @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}];
    [styledText release];
    return rtfdData;
}

// finds the focused accessible element, and if it is a text element, sets the text in it
- (BOOL)replaceAccessibleTextSelection:(NSString *)text
{
    id focused = [self accessibilityFocusedUIElement];
    if (![focused isKindOfClass:[JavaTextAccessibility class]]) return NO;
    [(JavaTextAccessibility *)focused accessibilitySetSelectedTextAttribute:text];
    return YES;
}

// called for each service in the Services menu - only handle text for now
- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
{
    if ([[self window] firstResponder] != self) return nil; // let AWT components handle themselves

    if ([sendType isEqual:NSStringPboardType] || [returnType isEqual:NSStringPboardType]) {
        NSString *selectedText = [self accessibleSelectedText];
        if (selectedText) return self;
    }

    return nil;
}

// fetch text from Java and hand off to the service
- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types
{
    if ([types containsObject:NSStringPboardType])
    {
        [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
        return [pboard setString:[self accessibleSelectedText] forType:NSStringPboardType];
    }

    if ([types containsObject:NSRTFDPboardType])
    {
        [pboard declareTypes:[NSArray arrayWithObject:NSRTFDPboardType] owner:nil];
        return [pboard setData:[self accessibleSelectedTextAsRTFD] forType:NSRTFDPboardType];
    }

    return NO;
}

// write text back to Java from the service
- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard
{
    if ([[pboard types] containsObject:NSStringPboardType])
    {
        NSString *text = [pboard stringForType:NSStringPboardType];
        return [self replaceAccessibleTextSelection:text];
    }

    if ([[pboard types] containsObject:NSRTFDPboardType])
    {
        NSData *rtfdData = [pboard dataForType:NSRTFDPboardType];
        NSAttributedString *styledText = [[NSAttributedString alloc] initWithRTFD:rtfdData documentAttributes:NULL];
        NSString *text = [styledText string];
        [styledText release];

        return [self replaceAccessibleTextSelection:text];
    }

    return NO;
}


-(void) setDragSource:(CDragSource *)source {
    self._dragSource = source;
}


- (void) setDropTarget:(CDropTarget *)target {
    self._dropTarget = target;
    [ThreadUtilities performOnMainThread:@selector(controlModelControlValid) on:self._dropTarget withObject:nil waitUntilDone:YES];
}

/********************************  BEGIN NSDraggingSource Interface  ********************************/

- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
{
    // If draggingSource is nil route the message to the superclass (if responding to the selector):
    CDragSource *dragSource = self._dragSource;
    NSDragOperation dragOp = NSDragOperationNone;

    if (dragSource != nil) {
        dragOp = [dragSource draggingSourceOperationMaskForLocal:flag];
    }
    return dragOp;
}

- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
{
    // If draggingSource is nil route the message to the superclass (if responding to the selector):
    CDragSource *dragSource = self._dragSource;
    NSArray* array = nil;

    if (dragSource != nil) {
        array = [dragSource namesOfPromisedFilesDroppedAtDestination:dropDestination];
    }
    return array;
}

- (void)draggedImage:(NSImage *)image beganAt:(NSPoint)screenPoint
{
    // If draggingSource is nil route the message to the superclass (if responding to the selector):
    CDragSource *dragSource = self._dragSource;

    if (dragSource != nil) {
        [dragSource draggedImage:image beganAt:screenPoint];
    }
}

- (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
    // If draggingSource is nil route the message to the superclass (if responding to the selector):
    CDragSource *dragSource = self._dragSource;

    if (dragSource != nil) {
        [dragSource draggedImage:image endedAt:screenPoint operation:operation];
    }
}

- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint
{
    // If draggingSource is nil route the message to the superclass (if responding to the selector):
    CDragSource *dragSource = self._dragSource;

    if (dragSource != nil) {
        [dragSource draggedImage:image movedTo:screenPoint];
    }
}

- (BOOL)ignoreModifierKeysWhileDragging
{
    // If draggingSource is nil route the message to the superclass (if responding to the selector):
    CDragSource *dragSource = self._dragSource;
    BOOL result = FALSE;

    if (dragSource != nil) {
        result = [dragSource ignoreModifierKeysWhileDragging];
    }
    return result;
}

/********************************  END NSDraggingSource Interface  ********************************/

/********************************  BEGIN NSDraggingDestination Interface  ********************************/

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;
    NSDragOperation dragOp = NSDragOperationNone;

    if (dropTarget != nil) {
        dragOp = [dropTarget draggingEntered:sender];
    }
    return dragOp;
}

- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;
    NSDragOperation dragOp = NSDragOperationNone;

    if (dropTarget != nil) {
        dragOp = [dropTarget draggingUpdated:sender];
    }
    return dragOp;
}

- (void)draggingExited:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;

    if (dropTarget != nil) {
        [dropTarget draggingExited:sender];
    }
}

- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;
    BOOL result = FALSE;

    if (dropTarget != nil) {
        result = [dropTarget prepareForDragOperation:sender];
    }
    return result;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;
    BOOL result = FALSE;

    if (dropTarget != nil) {
        result = [dropTarget performDragOperation:sender];
    }
    return result;
}

- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;

    if (dropTarget != nil) {
        [dropTarget concludeDragOperation:sender];
    }
}

- (void)draggingEnded:(id <NSDraggingInfo>)sender
{
    // If draggingDestination is nil route the message to the superclass:
    CDropTarget *dropTarget = self._dropTarget;

    if (dropTarget != nil) {
        [dropTarget draggingEnded:sender];
    }
}

/********************************  END NSDraggingDestination Interface  ********************************/

/********************************  BEGIN NSTextInputClient Protocol  ********************************/


JNF_CLASS_CACHE(jc_CInputMethod, "sun/lwawt/macosx/CInputMethod");

- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [insertText]: %s\n", [aString UTF8String]);
#endif // IM_DEBUG

    if (fInputMethodLOCKABLE == NULL) {
        return;
    }

    // Insert happens at the end of PAH
    fInPressAndHold = NO;

    // insertText gets called when the user commits text generated from an input method.  It also gets
    // called during ordinary input as well.  We only need to send an input method event when we have marked
    // text, or 'text in progress'.  We also need to send the event if we get an insert text out of the blue!
    // (i.e., when the user uses the Character palette or Inkwell), or when the string to insert is a complex
    // Unicode value.

    NSMutableString * useString = [self parseString:aString];
    NSUInteger utf16Length = [useString lengthOfBytesUsingEncoding:NSUTF16StringEncoding];
    NSUInteger utf8Length = [useString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    BOOL aStringIsComplex = NO;

    unichar codePoint = [useString characterAtIndex:0];

#ifdef IM_DEBUG
    NSLog(@"insertText kbdlayout %@ ",(NSString *)kbdLayout);
#endif // IM_DEBUG

    if ((utf16Length > 2) ||
        ((utf8Length > 1) && [self isCodePointInUnicodeBlockNeedingIMEvent:codePoint]) ||
        ((codePoint == 0x5c) && ([(NSString *)kbdLayout containsString:@"Kotoeri"]))) {
        aStringIsComplex = YES;
    }

    if ([self hasMarkedText] || !fProcessingKeystroke || aStringIsComplex) {
        JNIEnv *env = [ThreadUtilities getJNIEnv];

        static JNF_MEMBER_CACHE(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
        // We need to select the previous glyph so that it is overwritten.
        if (fPAHNeedsToSelect) {
            JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
            fPAHNeedsToSelect = NO;
        }

        static JNF_MEMBER_CACHE(jm_insertText, jc_CInputMethod, "insertText", "(Ljava/lang/String;)V");
        jstring insertedText =  JNFNSToJavaString(env, useString);
        JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_insertText, insertedText); // AWT_THREADING Safe (AWTRunLoopMode)
        (*env)->DeleteLocalRef(env, insertedText);

        // The input method event will create psuedo-key events for each character in the committed string.
        // We also don't want to send the character that triggered the insertText, usually a return. [3337563]
        fKeyEventsNeeded = NO;
    }
    else {
        // Need to set back the fKeyEventsNeeded flag so that the string following the
        // marked text is not ignored by keyDown
        if ([useString length] > 0) {
            fKeyEventsNeeded = YES;
        }
    }
    fPAHNeedsToSelect = NO;

    // Abandon input to reset IM and unblock input after entering accented
    // symbols

    [self abandonInput];
}

- (void)keyboardInputSourceChanged:(NSNotification *)notification
{
#ifdef IM_DEBUG
    NSLog(@"keyboardInputSourceChangeNotification received");
#endif
    NSTextInputContext *curContxt = [NSTextInputContext currentInputContext];
    kbdLayout = curContxt.selectedKeyboardInputSource;
}

- (void) doCommandBySelector:(SEL)aSelector
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [doCommandBySelector]\n");
    NSLog(@"%@", NSStringFromSelector(aSelector));
#endif // IM_DEBUG
    if (@selector(insertNewline:) == aSelector || @selector(insertTab:) == aSelector || @selector(deleteBackward:) == aSelector)
    {
        fKeyEventsNeeded = YES;
    }
}

// setMarkedText: cannot take a nil first argument. aString can be NSString or NSAttributedString
- (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replacementRange:(NSRange)replacementRange
{
    if (!fInputMethodLOCKABLE)
        return;

    BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
    NSAttributedString *attrString = (isAttributedString ? (NSAttributedString *)aString : nil);
    NSString *incomingString = (isAttributedString ? [aString string] : aString);
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [setMarkedText] \"%s\", loc=%lu, length=%lu\n", [incomingString UTF8String], (unsigned long)selectionRange.location, (unsigned long)selectionRange.length);
#endif // IM_DEBUG
    static JNF_MEMBER_CACHE(jm_startIMUpdate, jc_CInputMethod, "startIMUpdate", "(Ljava/lang/String;)V");
    static JNF_MEMBER_CACHE(jm_addAttribute, jc_CInputMethod, "addAttribute", "(ZZII)V");
    static JNF_MEMBER_CACHE(jm_dispatchText, jc_CInputMethod, "dispatchText", "(IIZ)V");
    JNIEnv *env = [ThreadUtilities getJNIEnv];

    // NSInputContext already did the analysis of the TSM event and created attributes indicating
    // the underlining and color that should be done to the string.  We need to look at the underline
    // style and color to determine what kind of Java hilighting needs to be done.
    jstring inProcessText = JNFNSToJavaString(env, incomingString);
    JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_startIMUpdate, inProcessText); // AWT_THREADING Safe (AWTRunLoopMode)
    (*env)->DeleteLocalRef(env, inProcessText);

    if (isAttributedString) {
        NSUInteger length;
        NSRange effectiveRange;
        NSDictionary *attributes;
        length = [attrString length];
        effectiveRange = NSMakeRange(0, 0);
        while (NSMaxRange(effectiveRange) < length) {
            attributes = [attrString attributesAtIndex:NSMaxRange(effectiveRange)
                                        effectiveRange:&effectiveRange];
            if (attributes) {
                BOOL isThickUnderline, isGray;
                NSNumber *underlineSizeObj =
                (NSNumber *)[attributes objectForKey:NSUnderlineStyleAttributeName];
                NSInteger underlineSize = [underlineSizeObj integerValue];
                isThickUnderline = (underlineSize > 1);

                NSColor *underlineColorObj =
                (NSColor *)[attributes objectForKey:NSUnderlineColorAttributeName];
                isGray = !([underlineColorObj isEqual:[NSColor blackColor]]);

                JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_addAttribute, isThickUnderline, isGray, effectiveRange.location, effectiveRange.length); // AWT_THREADING Safe (AWTRunLoopMode)
            }
        }
    }

    static JNF_MEMBER_CACHE(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
    // We need to select the previous glyph so that it is overwritten.
    if (fPAHNeedsToSelect) {
        JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
        fPAHNeedsToSelect = NO;
    }

    JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_dispatchText, selectionRange.location, selectionRange.length, JNI_FALSE); // AWT_THREADING Safe (AWTRunLoopMode)

    // If the marked text is being cleared (zero-length string) don't handle the key event.
    if ([incomingString length] == 0) {
        fKeyEventsNeeded = NO;
    }
}

- (void) unmarkText
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [unmarkText]\n");
#endif // IM_DEBUG

    if (!fInputMethodLOCKABLE) {
        return;
    }

    // unmarkText cancels any input in progress and commits it to the text field.
    static JNF_MEMBER_CACHE(jm_unmarkText, jc_CInputMethod, "unmarkText", "()V");
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    JNFCallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText); // AWT_THREADING Safe (AWTRunLoopMode)

}

- (BOOL) hasMarkedText
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [hasMarkedText]\n");
#endif // IM_DEBUG

    if (!fInputMethodLOCKABLE) {
        return NO;
    }

    static JNF_MEMBER_CACHE(jf_fCurrentText, jc_CInputMethod, "fCurrentText", "Ljava/text/AttributedString;");
    static JNF_MEMBER_CACHE(jf_fCurrentTextLength, jc_CInputMethod, "fCurrentTextLength", "I");
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    jobject currentText = JNFGetObjectField(env, fInputMethodLOCKABLE, jf_fCurrentText);

    jint currentTextLength = JNFGetIntField(env, fInputMethodLOCKABLE, jf_fCurrentTextLength);

    BOOL hasMarkedText = (currentText != NULL && currentTextLength > 0);

    if (currentText != NULL) {
        (*env)->DeleteLocalRef(env, currentText);
    }

    return hasMarkedText;
}

- (NSInteger) conversationIdentifier
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [conversationIdentifier]\n");
#endif // IM_DEBUG

    return (NSInteger) self;
}

/* Returns attributed string at the range.  This allows input mangers to
 query any range in backing-store (Andy's request)
 */
- (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [attributedSubstringFromRange] location=%lu, length=%lu\n", (unsigned long)theRange.location, (unsigned long)theRange.length);
#endif // IM_DEBUG

    static JNF_MEMBER_CACHE(jm_substringFromRange, jc_CInputMethod, "attributedSubstringFromRange", "(II)Ljava/lang/String;");
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    jobject theString = JNFCallObjectMethod(env, fInputMethodLOCKABLE, jm_substringFromRange, theRange.location, theRange.length); // AWT_THREADING Safe (AWTRunLoopMode)

    id result = [[[NSAttributedString alloc] initWithString:JNFJavaToNSString(env, theString)] autorelease];
#ifdef IM_DEBUG
    NSLog(@"attributedSubstringFromRange returning \"%@\"", result);
#endif // IM_DEBUG

    (*env)->DeleteLocalRef(env, theString);
    return result;
}

/* This method returns the range for marked region.  If hasMarkedText == false,
 it'll return NSNotFound location & 0 length range.
 */
- (NSRange) markedRange
{

#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [markedRange]\n");
#endif // IM_DEBUG

    if (!fInputMethodLOCKABLE) {
        return NSMakeRange(NSNotFound, 0);
    }

    static JNF_MEMBER_CACHE(jm_markedRange, jc_CInputMethod, "markedRange", "()[I");
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    jarray array;
    jboolean isCopy;
    jint *_array;
    NSRange range = NSMakeRange(NSNotFound, 0);

    array = JNFCallObjectMethod(env, fInputMethodLOCKABLE, jm_markedRange); // AWT_THREADING Safe (AWTRunLoopMode)

    if (array) {
        _array = (*env)->GetIntArrayElements(env, array, &isCopy);
        if (_array != NULL) {
            range.location = _array[0];
            range.length = _array[1];
#ifdef IM_DEBUG
            fprintf(stderr, "markedRange returning (%lu, %lu)\n",
                    (unsigned long)range.location, (unsigned long)range.length);
#endif // IM_DEBUG
            (*env)->ReleaseIntArrayElements(env, array, _array, 0);
        }
        (*env)->DeleteLocalRef(env, array);
    }

    return range;
}

/* This method returns the range for selected region.  Just like markedRange method,
 its location field contains char index from the text beginning.
 */
- (NSRange) selectedRange
{
    if (!fInputMethodLOCKABLE) {
        return NSMakeRange(NSNotFound, 0);
    }

    static JNF_MEMBER_CACHE(jm_selectedRange, jc_CInputMethod, "selectedRange", "()[I");
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    jarray array;
    jboolean isCopy;
    jint *_array;
    NSRange range = NSMakeRange(NSNotFound, 0);

#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [selectedRange]\n");
#endif // IM_DEBUG

    array = JNFCallObjectMethod(env, fInputMethodLOCKABLE, jm_selectedRange); // AWT_THREADING Safe (AWTRunLoopMode)
    if (array) {
        _array = (*env)->GetIntArrayElements(env, array, &isCopy);
        if (_array != NULL) {
            range.location = _array[0];
            range.length = _array[1];
            (*env)->ReleaseIntArrayElements(env, array, _array, 0);
        }
        (*env)->DeleteLocalRef(env, array);
    }

    return range;
}

/* This method returns the first frame of rects for theRange in screen coordindate system.
 */
- (NSRect) firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
{
    if (!fInputMethodLOCKABLE) {
        return NSZeroRect;
    }

    static JNF_MEMBER_CACHE(jm_firstRectForCharacterRange, jc_CInputMethod,
                            "firstRectForCharacterRange", "(I)[I");
    JNIEnv *env = [ThreadUtilities getJNIEnv];
    jarray array;
    jboolean isCopy;
    jint *_array;
    NSRect rect;

#ifdef IM_DEBUG
    fprintf(stderr,
            "AWTView InputMethod Selector Called : [firstRectForCharacterRange:] location=%lu, length=%lu\n",
            (unsigned long)theRange.location, (unsigned long)theRange.length);
#endif // IM_DEBUG

    array = JNFCallObjectMethod(env, fInputMethodLOCKABLE, jm_firstRectForCharacterRange,
                                theRange.location); // AWT_THREADING Safe (AWTRunLoopMode)

    _array = (*env)->GetIntArrayElements(env, array, &isCopy);
    if (_array) {
        rect = ConvertNSScreenRect(env, NSMakeRect(_array[0], _array[1], _array[2], _array[3]));
        (*env)->ReleaseIntArrayElements(env, array, _array, 0);
    } else {
        rect = NSZeroRect;
    }
    (*env)->DeleteLocalRef(env, array);

#ifdef IM_DEBUG
    fprintf(stderr,
            "firstRectForCharacterRange returning x=%f, y=%f, width=%f, height=%f\n",
            rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
#endif // IM_DEBUG
    return rect;
}

/* This method returns the index for character that is nearest to thePoint.  thPoint is in
 screen coordinate system.
 */
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
    if (!fInputMethodLOCKABLE) {
        return NSNotFound;
    }

    static JNF_MEMBER_CACHE(jm_characterIndexForPoint, jc_CInputMethod,
                            "characterIndexForPoint", "(II)I");
    JNIEnv *env = [ThreadUtilities getJNIEnv];

    NSPoint flippedLocation = ConvertNSScreenPoint(env, thePoint);

#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [characterIndexForPoint:(NSPoint)thePoint] x=%f, y=%f\n", flippedLocation.x, flippedLocation.y);
#endif // IM_DEBUG

    jint index = JNFCallIntMethod(env, fInputMethodLOCKABLE, jm_characterIndexForPoint, (jint)flippedLocation.x, (jint)flippedLocation.y); // AWT_THREADING Safe (AWTRunLoopMode)

#ifdef IM_DEBUG
    fprintf(stderr, "characterIndexForPoint returning %ld\n", index);
#endif // IM_DEBUG

    if (index == -1) {
        return NSNotFound;
    } else {
        return (NSUInteger)index;
    }
}

- (NSArray*) validAttributesForMarkedText
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [validAttributesForMarkedText]\n");
#endif // IM_DEBUG

    return [NSArray array];
}

- (void)setInputMethod:(jobject)inputMethod
{
#ifdef IM_DEBUG
    fprintf(stderr, "AWTView InputMethod Selector Called : [setInputMethod]\n");
#endif // IM_DEBUG

    JNIEnv *env = [ThreadUtilities getJNIEnv];

    // Get rid of the old one

/**代码未完, 请加载全部代码(NowJava.com).**/
展开阅读全文

关注时代Java

关注时代Java