blob: bfe3c59b268c46d6371fd574f7b86f8519a31f05 [file] [log] [blame]
/*
* Copyright (c) 2011, 2021, 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 "AWTEvent.h"
#import "AWTWindow.h"
#import "a11y/CommonComponentAccessibility.h"
#import "JavaAccessibilityUtilities.h"
#import "GeomUtilities.h"
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
#import "jni_util.h"
#import "PropertiesUtilities.h"
#import "sun_lwawt_macosx_CPlatformWindow.h"
#import <Carbon/Carbon.h>
#define DEFAULT_FRAME_WIDTH 1504
#define DEFAULT_FRAME_HEIGHT 846
// keyboard layout
static NSString *kbdLayout;
// workaround for JBR-6704
static unichar lastCtrlCombo;
@interface AWTView()
@property (retain) CDropTarget *_dropTarget;
@property (retain) CDragSource *_dragSource;
-(void) deliverResize: (NSRect) rect;
-(void) resetTrackingArea;
-(void) deliverJavaKeyEventHelper: (NSEvent*) event;
-(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 LOG_KEY_EVENTS
static BOOL shouldUsePressAndHold() {
return YES;
}
extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString * chars);
@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;
fInputMethodInteractionEnabled = YES;
fKeyEventsNeeded = NO;
fProcessingKeystroke = NO;
lastCtrlCombo = 0;
fEnablePressAndHold = shouldUsePressAndHold();
fInPressAndHold = 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];
(*env)->DeleteGlobalRef(env, fInputMethodLOCKABLE);
fInputMethodLOCKABLE = NULL;
}
if (rolloverTrackingArea != nil) {
[self removeTrackingArea:rolloverTrackingArea];
[rolloverTrackingArea release];
rolloverTrackingArea = nil;
}
[super dealloc];
}
- (void) viewDidMoveToWindow {
AWT_ASSERT_APPKIT_THREAD;
[AWTToolkit eventCountPlusPlus];
[ThreadUtilities performOnMainThreadWaiting:NO block:^() {
[[self window] makeFirstResponder: self];
}];
if ([self window] != NULL) {
[self resetTrackingArea];
}
}
- (void)setFrame:(NSRect)newFrame {
if (isnan(newFrame.origin.x)) {
newFrame.origin.x = 0;
}
if (isnan(newFrame.origin.y)) {
newFrame.origin.y = 0;
}
if (isnan(newFrame.size.width)) {
newFrame.size.width = DEFAULT_FRAME_WIDTH;
}
if (isnan(newFrame.size.height)) {
newFrame.size.height = DEFAULT_FRAME_HEIGHT;
}
[super setFrame:newFrame];
}
- (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
*/
- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event {
return [self isWindowDisabled];
}
- (void) mouseDown: (NSEvent *)event {
if ([self isWindowDisabled]) {
[NSApp preventWindowOrdering];
[NSApp activateIgnoringOtherApps:YES];
}
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
NSString *checkForInputManagerInMouseDown =
[PropertiesUtilities
javaSystemPropertyForKey:@"apple.awt.checkForInputManagerInMouseDown"
withEnv:env];
if ([@"true" isCaseInsensitiveLike:checkForInputManagerInMouseDown]) {
NSInputManager *inputManager = [NSInputManager currentInputManager];
if ([inputManager wantsToHandleMouseEvents]) {
#if IM_DEBUG
NSLog(@"-> IM wants to handle event");
#endif
if ([inputManager handleMouseEvent:event]) {
#if IM_DEBUG
NSLog(@"-> Event was handled.");
return;
#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]]) {
NSWindow* eventWindow = [event window];
NSPoint screenPoint = (eventWindow == nil)
? eventLocation
// SDK we build against is too old - it doesn't have convertPointToScreen
: [eventWindow convertRectToScreen:NSMakeRect(eventLocation.x, eventLocation.y, 0, 0)].origin;
// macOS can report mouseMoved events to a window even if it's not showing currently (see JBR-2702)
// so we're performing an additional check here
if (self.window.windowNumber == [NSWindow windowNumberAtPoint:screenPoint belowWindowWithWindowNumber:0]) {
[self deliverJavaMouseEvent: event];
return;
}
}
[[self nextResponder] mouseMoved: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
*/
#ifdef LOG_KEY_EVENTS
static void debugPrintNSString(const char* name, NSString* s) {
if (s == nil) {
fprintf(stderr, "\t%s: nil\n", name);
return;
}
const char* utf8 = [s UTF8String];
int codeUnits = [s length];
int bytes = strlen(utf8);
fprintf(stderr, "\t%s: [utf8 = \"", name);
for (const unsigned char* c = (const unsigned char*)utf8; *c; ++c) {
if (*c >= 0x20 && *c <= 0x7e) {
fputc(*c, stderr);
} else {
fprintf(stderr, "\\x%02x", *c);
}
}
fprintf(stderr, "\", utf16 = \"");
for (int i = 0; i < codeUnits; ++i) {
int c = (int)[s characterAtIndex:i];
if (c >= 0x20 && c <= 0x7e) {
fputc(c, stderr);
} else {
fprintf(stderr, "\\u%04x", c);
}
}
fprintf(stderr, "\", bytes = %d, codeUnits = %d]\n", bytes, codeUnits);
}
static void debugPrintNSEvent(NSEvent* event, const char* comment) {
NSEventType type = [event type];
if (type == NSEventTypeKeyDown) {
fprintf(stderr, "[AWTView.m] keyDown in %s\n", comment);
} else if (type == NSEventTypeKeyUp) {
fprintf(stderr, "[AWTView.m] keyUp in %s\n", comment);
} else if (type == NSEventTypeFlagsChanged) {
fprintf(stderr, "[AWTView.m] flagsChanged in %s\n", comment);
} else {
fprintf(stderr, "[AWTView.m] unknown event %d in %s\n", (int)type, comment);
return;
}
if (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp) {
debugPrintNSString("characters", [event characters]);
debugPrintNSString("charactersIgnoringModifiers", [event charactersIgnoringModifiers]);
fprintf(stderr, "\tkeyCode: %d (0x%02x)\n", [event keyCode], [event keyCode]);
}
fprintf(stderr, "\tmodifierFlags: 0x%08x\n", (unsigned)[event modifierFlags]);
TISInputSourceRef is = TISCopyCurrentKeyboardLayoutInputSource();
fprintf(stderr, "\tTISCopyCurrentKeyboardLayoutInputSource: %s\n", is == nil ? "(nil)" : [(NSString*) TISGetInputSourceProperty(is, kTISPropertyInputSourceID) UTF8String]);
fprintf(stderr, "\twillBeHandledByComplexInputMethod: %s\n", [event willBeHandledByComplexInputMethod] ? "true" : "false");
CFRelease(is);
}
#endif
- (void) keyDown: (NSEvent *)event {
#ifdef LOG_KEY_EVENTS
debugPrintNSEvent(event, "keyDown");
#endif
fProcessingKeystroke = YES;
fKeyEventsNeeded = YES;
NSString *eventCharacters = [event characters];
unsigned mods = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
if (([event modifierFlags] & NSControlKeyMask) && [eventCharacters length] == 1) {
lastCtrlCombo = [eventCharacters characterAtIndex:0];
} else {
lastCtrlCombo = 0;
}
// Allow TSM to look at the event and potentially send back NSTextInputClient messages.
if (fInputMethodInteractionEnabled == YES) {
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
if (fEnablePressAndHold && [event willBeHandledByComplexInputMethod] &&
fInputMethodLOCKABLE)
{
BOOL skipProcessingCancelKeys = YES;
fProcessingKeystroke = NO;
if (!fInPressAndHold) {
fInPressAndHold = YES;
} else {
// Abandon input to reset IM and unblock input after canceling
// input accented symbols
switch([event keyCode]) {
case kVK_ForwardDelete:
case kVK_Delete:
skipProcessingCancelKeys = NO;
case kVK_Return:
case kVK_Escape:
case kVK_PageUp:
case kVK_PageDown:
case kVK_DownArrow:
case kVK_UpArrow:
case kVK_Home:
case kVK_End:
// Abandon input to reset IM and unblock input after
// canceling input accented symbols
[self abandonInput:nil];
break;
}
}
if (skipProcessingCancelKeys) {
return;
}
}
BOOL isDeadKey = (eventCharacters != nil && [eventCharacters length] == 0);
if ((![self hasMarkedText] && fKeyEventsNeeded) || isDeadKey) {
[self deliverJavaKeyEventHelper: event];
}
if (actualCharacters != nil) {
[actualCharacters release];
actualCharacters = nil;
}
fProcessingKeystroke = NO;
}
- (void) keyUp: (NSEvent *)event {
#ifdef LOG_KEY_EVENTS
debugPrintNSEvent(event, "keyUp");
#endif
[self deliverJavaKeyEventHelper: event];
}
- (void) flagsChanged: (NSEvent *)event {
#ifdef LOG_KEY_EVENTS
debugPrintNSEvent(event, "flagsChanged");
#endif
[self deliverJavaKeyEventHelper: event];
}
- (BOOL) performKeyEquivalent: (NSEvent *) event {
#ifdef LOG_KEY_EVENTS
debugPrintNSEvent(event, "performKeyEquivalent");
#endif
// if IM is active key events should be ignored
if (![self hasMarkedText] && !fInPressAndHold) {
[self deliverJavaKeyEventHelper: event];
}
const NSUInteger modFlags =
[event modifierFlags] & (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
// Workaround for JBR-3544
// When tabbing mode is on (jdk.allowMacOSTabbedWindows=true) and "Ctrl Opt N" / "Cmd Opt N" is pressed,
// macOS first sends it, and immediately then sends "Ctrl N" / "Cmd N".
// The workaround is to "eat" (by returning TRUE) the "Ctrl Opt N" / "Cmd Opt N",
// so macOS won't send its "fallback" version ("Ctrl N" / "Cmd N").
if ([event keyCode] == kVK_ANSI_N) {
const NSUInteger ctrlOpt = (NSControlKeyMask | NSAlternateKeyMask);
const NSUInteger cmdOpt = (NSCommandKeyMask | NSAlternateKeyMask);
if ((modFlags == ctrlOpt) || (modFlags == cmdOpt)) {
[[NSApp mainMenu] performKeyEquivalent: event]; // just in case (as in the workaround for 8020209 below)
return YES;
}
}
// Workaround for 8020209: special case for "Cmd =" and "Cmd ."
// because Cocoa calls performKeyEquivalent twice for these keystrokes
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;
}
}
}
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
NSString *captureNextAppWinKey = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.captureNextAppWinKey"
withEnv:env];
if ([@"true" isCaseInsensitiveLike:captureNextAppWinKey]) {
NSUInteger deviceIndependentModifierFlagsMask =
[event modifierFlags] & NSDeviceIndependentModifierFlagsMask;
// Why translate the key code here and not just use event.characters?
// The default macOS shortcut for NextWindowInApplication is Cmd+Backtick. Pressing Cmd+Dead Grave also works
// for layouts that have the backtick as a dead key. Unfortunately, some (but notably not all) of these layouts
// consider Cmd+Dead Grave to also be a dead key, which means that event.characters will be an empty string.
// Explicitly translating the key code with a proper underlying key layout fixes this.
struct KeyCodeTranslationResult translationResult = TranslateKeyCodeUsingLayout(GetCurrentUnderlyingLayout(YES), [event keyCode]);
if (translationResult.isSuccess && translationResult.character) {
return isSystemShortcut_NextWindowInApplication(deviceIndependentModifierFlagsMask, [event keyCode], [NSString stringWithCharacters:&translationResult.character length:1]) ? YES : NO;
}
}
return NO;
}
/**
* Utility methods and accessors
*/
-(BOOL) isWindowDisabled {
NSWindow* window = [self window];
if ([window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]]) {
return ![(AWTWindow*)[window delegate] isEnabled];
} else {
return NO;
}
}
-(void) deliverJavaMouseEvent: (NSEvent *) event {
if ([self isWindowDisabled]) {
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;
}
DECLARE_CLASS(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
DECLARE_METHOD(jctor_NSEvent, jc_NSEvent, "<init>", "(IIIIIIIIDDI)V");
jobject jEvent = (*env)->NewObject(env, jc_NSEvent, 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);
DECLARE_CLASS(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
DECLARE_METHOD(jm_deliverMouseEvent, jc_PlatformView, "deliverMouseEvent", "(Lsun/lwawt/macosx/NSEvent;)V");
jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
if (!(*env)->IsSameObject(env, jlocal, NULL)) {
(*env)->CallVoidMethod(env, jlocal, jm_deliverMouseEvent, jEvent);
CHECK_EXCEPTION();
(*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;
if ([event type] != NSEventTypeFlagsChanged) {
characters = NSStringToJavaString(env, [event characters]);
}
jstring jActualCharacters = NULL;
if (actualCharacters != nil) {
jActualCharacters = NSStringToJavaString(env, actualCharacters);
}
DECLARE_CLASS(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
DECLARE_METHOD(jctor_NSEvent, jc_NSEvent, "<init>", "(IISLjava/lang/String;Ljava/lang/String;)V");
jobject jEvent = (*env)->NewObject(env, jc_NSEvent, jctor_NSEvent,
[event type],
[event modifierFlags],
[event keyCode],
characters,
jActualCharacters);
CHECK_NULL(jEvent);
DECLARE_CLASS(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
DECLARE_METHOD(jm_deliverKeyEvent, jc_PlatformView,
"deliverKeyEvent", "(Lsun/lwawt/macosx/NSEvent;)V");
jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
if (!(*env)->IsSameObject(env, jlocal, NULL)) {
(*env)->CallVoidMethod(env, jlocal, jm_deliverKeyEvent, jEvent);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, jlocal);
}
if (characters != NULL) {
(*env)->DeleteLocalRef(env, characters);
}
if (jActualCharacters != NULL) {
(*env)->DeleteLocalRef(env, jActualCharacters);
}
(*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];
DECLARE_CLASS(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
DECLARE_METHOD(jm_deliverResize, jc_PlatformView, "deliverResize", "(IIII)V");
jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
if (!(*env)->IsSameObject(env, jlocal, NULL)) {
(*env)->CallVoidMethod(env, jlocal, jm_deliverResize, x,y,w,h);
CHECK_EXCEPTION();
(*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 {
*/
DECLARE_CLASS(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView");
DECLARE_METHOD(jm_deliverWindowDidExposeEvent, jc_CPlatformView, "deliverWindowDidExposeEvent", "()V");
jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
if (!(*env)->IsSameObject(env, jlocal, NULL)) {
(*env)->CallVoidMethod(env, jlocal, jm_deliverWindowDidExposeEvent);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, jlocal);
}
/*
}
*/
}
}
-(NSMutableString *) parseString : (id) complexString {
if ([complexString isKindOfClass:[NSString class]]) {
return [complexString mutableCopy];
}
else {
return [complexString mutableString];
}
}
// NSAccessibility support
- (jobject)awtComponent:(JNIEnv*)env
{
DECLARE_CLASS_RETURN(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView", NULL);
DECLARE_FIELD_RETURN(jf_Peer, jc_CPlatformView, "peer", "Lsun/lwawt/LWWindowPeer;", NULL);
if ((env == NULL) || (m_cPlatformView == NULL)) {
NSLog(@"Apple AWT : Error AWTView:awtComponent given bad parameters.");
NSLog(@"%@",[NSThread callStackSymbols]);
return NULL;
}
jobject peer = NULL;
jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
if (!(*env)->IsSameObject(env, jlocal, NULL)) {
peer = (*env)->GetObjectField(env, jlocal, jf_Peer);
(*env)->DeleteLocalRef(env, jlocal);
}
DECLARE_CLASS_RETURN(jc_LWWindowPeer, "sun/lwawt/LWWindowPeer", NULL);
DECLARE_FIELD_RETURN(jf_Target, jc_LWWindowPeer, "target", "Ljava/awt/Component;", NULL);
if (peer == NULL) {
NSLog(@"Apple AWT : Error AWTView:awtComponent got null peer from CPlatformView");
NSLog(@"%@",[NSThread callStackSymbols]);
return NULL;
}
jobject comp = (*env)->GetObjectField(env, peer, jf_Target);
(*env)->DeleteLocalRef(env, peer);
return comp;
}
+ (AWTView *) awtView:(JNIEnv*)env ofAccessible:(jobject)jaccessible
{
DECLARE_CLASS_RETURN(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility", NULL);
DECLARE_STATIC_METHOD_RETURN(jm_getAWTView, sjc_CAccessibility, "getAWTView", "(Ljavax/accessibility/Accessible;)J", NULL);
jlong jptr = (*env)->CallStaticLongMethod(env, sjc_CAccessibility, jm_getAWTView, jaccessible);
CHECK_EXCEPTION();
if (jptr == 0) return nil;
return (AWTView *)jlong_to_ptr(jptr);
}
- (id)getAxData:(JNIEnv*)env
{
NSString *a11yEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"sun.awt.mac.a11y.enabled" withEnv:env];
if ([@"false" isCaseInsensitiveLike:a11yEnabledProp]) {
return nil;
}
jobject jcomponent = [self awtComponent:env];
id ax = [[[CommonComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease];
(*env)->DeleteLocalRef(env, jcomponent);
return ax;
}
// NSAccessibility messages
- (id)accessibilityChildren
{
AWT_ASSERT_APPKIT_THREAD;
JNIEnv *env = [ThreadUtilities getJNIEnv];
(*env)->PushLocalFrame(env, 4);
id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]);
(*env)->PopLocalFrame(env, NULL);
return result;
}
- (BOOL)isAccessibilityElement
{
return NO;
}
- (id)accessibilityHitTest:(NSPoint)point
{
AWT_ASSERT_APPKIT_THREAD;
JNIEnv *env = [ThreadUtilities getJNIEnv];
(*env)->PushLocalFrame(env, 4);
id result = [[self getAxData:env] accessibilityHitTest:point];
(*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 *)accessibilitySelectedText
{
id focused = [self accessibilityFocusedUIElement];
if (![focused respondsToSelector:@selector(accessibilitySelectedText)]) return nil;
return [focused accessibilitySelectedText];
}
- (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText {
id focused = [self accessibilityFocusedUIElement];
if ([focused respondsToSelector:@selector(setAccessibilitySelectedText:)]) {
[focused setAccessibilitySelectedText:accessibilitySelectedText];
}
}
// same as above, but converts to RTFD
- (NSData *)accessibleSelectedTextAsRTFD
{
NSString *selectedText = [self accessibilitySelectedText];
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 respondsToSelector:@selector(setAccessibilitySelectedText:)]) return NO;
[focused setAccessibilitySelectedText: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 accessibilitySelectedText];
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 accessibilitySelectedText] 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];
else if ([super respondsToSelector:@selector(draggingSourceOperationMaskForLocal:)])
dragOp = [super 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];
else if ([super respondsToSelector:@selector(namesOfPromisedFilesDroppedAtDestination:)])
array = [super 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];
else if ([super respondsToSelector:@selector(ignoreModifierKeysWhileDragging)])
result = [super 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];
else if ([super respondsToSelector:@selector(draggingEntered:)])
dragOp = [super 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];
else if ([super respondsToSelector:@selector(draggingUpdated:)])
dragOp = [super 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];
else if ([super respondsToSelector:@selector(prepareForDragOperation:)])
result = [super 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];
else if ([super respondsToSelector:@selector(performDragOperation:)])
result = [super 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 ********************************/
static jclass jc_CInputMethod = NULL;
#define GET_CIM_CLASS() \
GET_CLASS(jc_CInputMethod, "sun/lwawt/macosx/CInputMethod");
#define GET_CIM_CLASS_RETURN(ret) \
GET_CLASS_RETURN(jc_CInputMethod, "sun/lwawt/macosx/CInputMethod", ret);
- (NSInteger) windowLevel
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [windowLevel]\n");
#endif // IM_DEBUG
NSWindow* const ownerWindow = [self window];
if (ownerWindow == nil) {
return NSNormalWindowLevel;
}
const NSWindowLevel ownerWindowLevel = [ownerWindow level];
if ( (ownerWindowLevel != NSNormalWindowLevel) && (ownerWindowLevel != NSFloatingWindowLevel) ) {
// the window level has been overridden, let's believe it
return ownerWindowLevel;
}
AWTWindow* const delegate = (AWTWindow*)[ownerWindow delegate];
if (delegate == nil) {
return ownerWindowLevel;
}
const jint styleBits = [delegate styleBits];
const BOOL isPopup = ( (styleBits & sun_lwawt_macosx_CPlatformWindow_IS_POPUP) != 0 );
if (isPopup) {
return NSPopUpMenuWindowLevel;
}
const BOOL isModal = ( (styleBits & sun_lwawt_macosx_CPlatformWindow_IS_MODAL) != 0 );
if (isModal) {
return NSFloatingWindowLevel;
}
return ownerWindowLevel;
}
- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
{
#ifdef IM_DEBUG
fprintf(stderr,
"AWTView InputMethod Selector Called : [insertText]: %s, replacementRange: location=%lu, length=%lu\n",
[aString UTF8String], replacementRange.location, replacementRange.length);
#endif // IM_DEBUG
NSMutableString * useString = [self parseString:aString];
// See JBR-6704
if (lastCtrlCombo && !fProcessingKeystroke && [useString length] == 1 && [useString characterAtIndex:0] == lastCtrlCombo) {
lastCtrlCombo = 0;
return;
}
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.
BOOL usingComplexIM = [self hasMarkedText] || !fProcessingKeystroke;
#ifdef IM_DEBUG
NSUInteger utf16Length = [useString lengthOfBytesUsingEncoding:NSUTF16StringEncoding];
NSUInteger utf8Length = [useString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"insertText kbdlayout %@ ",(NSString *)kbdLayout);
NSLog(@"utf8Length %lu utf16Length %lu", (unsigned long)utf8Length, (unsigned long)utf16Length);
NSLog(@"hasMarkedText: %s, fProcessingKeyStroke: %s", [self hasMarkedText] ? "YES" : "NO", fProcessingKeystroke ? "YES" : "NO");
#endif // IM_DEBUG
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
if (replacementRange.length > 0) {
DECLARE_METHOD(jm_selectRange, jc_CInputMethod, "selectRange", "(II)V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectRange, replacementRange.location,
replacementRange.length);
CHECK_EXCEPTION();
}
if (usingComplexIM) {
DECLARE_METHOD(jm_insertText, jc_CInputMethod, "insertText", "(Ljava/lang/String;)V");
jstring insertedText = NSStringToJavaString(env, useString);
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_insertText, insertedText);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, insertedText);
fKeyEventsNeeded = NO;
} else {
if (actualCharacters != nil) {
[actualCharacters release];
}
actualCharacters = [useString copy];
fKeyEventsNeeded = YES;
}
// Abandon input to reset IM and unblock input after entering accented
// symbols
[self abandonInput:nil];
}
+ (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\","
"selectionRange(%lu, %lu), replacementRange(%lu, %lu)\n", [incomingString UTF8String],
selectionRange.location, selectionRange.length, replacementRange.location, replacementRange.length);
#endif // IM_DEBUG
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
DECLARE_METHOD(jm_startIMUpdate, jc_CInputMethod, "startIMUpdate", "(Ljava/lang/String;)V");
DECLARE_METHOD(jm_addAttribute, jc_CInputMethod, "addAttribute", "(ZZII)V");
DECLARE_METHOD(jm_dispatchText, jc_CInputMethod, "dispatchText", "(IIZ)V");
if (replacementRange.length > 0) {
DECLARE_METHOD(jm_selectRange, jc_CInputMethod, "selectRange", "(II)V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectRange, replacementRange.location,
replacementRange.length);
CHECK_EXCEPTION();
}
// 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 = NSStringToJavaString(env, incomingString);
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_startIMUpdate, inProcessText);
CHECK_EXCEPTION();
(*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]]);
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_addAttribute, isThickUnderline,
isGray, effectiveRange.location, effectiveRange.length);
CHECK_EXCEPTION();
}
}
}
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_dispatchText,
selectionRange.location, selectionRange.length, JNI_FALSE);
CHECK_EXCEPTION();
// If the marked text is being cleared (zero-length string) don't handle the key event.
if ([incomingString length] == 0) {
fKeyEventsNeeded = NO;
}
}
- (void) unmarkText {
[self unmarkText:nil];
}
- (void) unmarkText:(jobject) component
{
#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.
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "(Ljava/awt/Component;)V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText, component);
CHECK_EXCEPTION();
}
- (BOOL) hasMarkedText
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [hasMarkedText]\n");
#endif // IM_DEBUG
if (!fInputMethodLOCKABLE) {
return NO;
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS_RETURN(NO);
DECLARE_FIELD_RETURN(jf_fCurrentText, jc_CInputMethod, "fCurrentText", "Ljava/text/AttributedString;", NO);
DECLARE_FIELD_RETURN(jf_fCurrentTextLength, jc_CInputMethod, "fCurrentTextLength", "I", NO);
jobject currentText = (*env)->GetObjectField(env, fInputMethodLOCKABLE, jf_fCurrentText);
CHECK_EXCEPTION();
jint currentTextLength = (*env)->GetIntField(env, fInputMethodLOCKABLE, jf_fCurrentTextLength);
CHECK_EXCEPTION();
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
if (!fInputMethodLOCKABLE) {
return nil;
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS_RETURN(nil);
DECLARE_METHOD_RETURN(jm_substringFromRange, jc_CInputMethod, "attributedSubstringFromRange", "(II)Ljava/lang/String;", nil);
jobject theString = (*env)->CallObjectMethod(env, fInputMethodLOCKABLE, jm_substringFromRange, theRange.location, theRange.length);
CHECK_EXCEPTION_NULL_RETURN(theString, nil);
if (!theString) {
return NULL;
}
id result = [[[NSAttributedString alloc] initWithString:JavaStringToNSString(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);
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
jarray array;
jboolean isCopy;
jint *_array;
NSRange range = NSMakeRange(NSNotFound, 0);
GET_CIM_CLASS_RETURN(range);
DECLARE_METHOD_RETURN(jm_markedRange, jc_CInputMethod, "markedRange", "()[I", range);
array = (*env)->CallObjectMethod(env, fInputMethodLOCKABLE, jm_markedRange);
CHECK_EXCEPTION();
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);
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
jarray array;
jboolean isCopy;
jint *_array;
NSRange range = NSMakeRange(NSNotFound, 0);
GET_CIM_CLASS_RETURN(range);
DECLARE_METHOD_RETURN(jm_selectedRange, jc_CInputMethod, "selectedRange", "()[I", range);
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [selectedRange]\n");
#endif // IM_DEBUG
array = (*env)->CallObjectMethod(env, fInputMethodLOCKABLE, jm_selectedRange);
CHECK_EXCEPTION();
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;
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS_RETURN(NSZeroRect);
DECLARE_METHOD_RETURN(jm_firstRectForCharacterRange, jc_CInputMethod,
"firstRectForCharacterRange", "(I)[I", NSZeroRect);
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 = (*env)->CallObjectMethod(env, fInputMethodLOCKABLE, jm_firstRectForCharacterRange,
theRange.location);
CHECK_EXCEPTION();
_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;
}
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS_RETURN(NSNotFound);
DECLARE_METHOD_RETURN(jm_characterIndexForPoint, jc_CInputMethod,
"characterIndexForPoint", "(II)I", NSNotFound);
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 = (*env)->CallIntMethod(env, fInputMethodLOCKABLE, jm_characterIndexForPoint,
(jint)flippedLocation.x, (jint)flippedLocation.y);
CHECK_EXCEPTION();
#ifdef IM_DEBUG
fprintf(stderr, "characterIndexForPoint returning %d\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
if (fInputMethodLOCKABLE) {
(*env)->DeleteGlobalRef(env, fInputMethodLOCKABLE);
}
fInputMethodLOCKABLE = inputMethod; // input method arg must be a GlobalRef
NSTextInputContext *curContxt = [NSTextInputContext currentInputContext];
kbdLayout = curContxt.selectedKeyboardInputSource;
[[NSNotificationCenter defaultCenter] addObserver:[AWTView class]
selector:@selector(keyboardInputSourceChanged:)
name:NSTextInputContextKeyboardSelectionDidChangeNotification
object:nil];
}
- (void)abandonInput:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [abandonInput]\n");
#endif // IM_DEBUG
[ThreadUtilities performOnMainThread:@selector(markedTextAbandoned:) on:[NSInputManager currentInputManager] withObject:self waitUntilDone:YES];
[self unmarkText:component];
}
-(void)enableImInteraction:(BOOL)enabled
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [enableImInteraction:%d]\n", enabled);
#endif // IM_DEBUG
AWT_ASSERT_APPKIT_THREAD;
fInputMethodInteractionEnabled = (enabled == YES) ? YES : NO;
}
/******************************** END NSTextInputClient Protocol ********************************/
- (void)viewDidChangeBackingProperties {
JNIEnv *env = [ThreadUtilities getJNIEnv];
if ((*env)->IsSameObject(env, m_cPlatformView, NULL)) {
return;
}
static double debugScale = -2.0;
if (debugScale == -2.0) { // default debugScale value in SGE is -1.0
debugScale = JNU_CallStaticMethodByName(env, NULL, "sun/java2d/SunGraphicsEnvironment",
"getDebugScale", "()D").d;
}
if (self.window.backingScaleFactor > 0 && debugScale < 0) {
if (self.layer.contentsScale != self.window.backingScaleFactor) {
self.layer.contentsScale = self.window.backingScaleFactor;
DECLARE_CLASS(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView");
DECLARE_METHOD(deliverChangeBackingProperties, jc_CPlatformView, "deliverChangeBackingProperties", "(F)V");
jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
(*env)->CallVoidMethod(env, jlocal, deliverChangeBackingProperties, self.window.backingScaleFactor);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, jlocal);
}
}
}
@end // AWTView
/*
* Class: sun_lwawt_macosx_CPlatformView
* Method: nativeCreateView
* Signature: (IIII)J
*/
JNIEXPORT jlong JNICALL
Java_sun_lwawt_macosx_CPlatformView_nativeCreateView
(JNIEnv *env, jobject obj, jint originX, jint originY, jint width, jint height, jlong windowLayerPtr)
{
__block AWTView *newView = nil;
JNI_COCOA_ENTER(env);
NSRect rect = NSMakeRect(originX, originY, width, height);
jobject cPlatformView = (*env)->NewWeakGlobalRef(env, obj);
CHECK_EXCEPTION();
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CALayer *windowLayer = jlong_to_ptr(windowLayerPtr);
newView = [[AWTView alloc] initWithRect:rect
platformView:cPlatformView
windowLayer:windowLayer];
}];
JNI_COCOA_EXIT(env);
return ptr_to_jlong(newView);
}
/*
* Class: sun_lwawt_macosx_CPlatformView
* Method: nativeSetAutoResizable
* Signature: (JZ)V;
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CPlatformView_nativeSetAutoResizable
(JNIEnv *env, jclass cls, jlong viewPtr, jboolean toResize)
{
JNI_COCOA_ENTER(env);
NSView *view = (NSView *)jlong_to_ptr(viewPtr);
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
if (toResize) {
[view setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable];
} else {
[view setAutoresizingMask: NSViewMinYMargin | NSViewMaxXMargin];
}
if ([view superview] != nil) {
[[view superview] setAutoresizesSubviews:(BOOL)toResize];
}
}];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CPlatformView
* Method: nativeGetNSViewDisplayID
* Signature: (J)I;
*/
JNIEXPORT jint JNICALL
Java_sun_lwawt_macosx_CPlatformView_nativeGetNSViewDisplayID
(JNIEnv *env, jclass cls, jlong viewPtr)
{
__block jint ret; //CGDirectDisplayID
JNI_COCOA_ENTER(env);
NSView *view = (NSView *)jlong_to_ptr(viewPtr);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
NSWindow *window = [view window];
ret = (jint)[[AWTWindow getNSWindowDisplayID_AppKitThread: window] intValue];
}];
JNI_COCOA_EXIT(env);
return ret;
}
/*
* Class: sun_lwawt_macosx_CPlatformView
* Method: nativeGetLocationOnScreen
* Signature: (J)Ljava/awt/Rectangle;
*/
JNIEXPORT jobject JNICALL
Java_sun_lwawt_macosx_CPlatformView_nativeGetLocationOnScreen
(JNIEnv *env, jclass cls, jlong viewPtr)
{
jobject jRect = NULL;
JNI_COCOA_ENTER(env);
__block NSRect rect = NSZeroRect;
NSView *view = (NSView *)jlong_to_ptr(viewPtr);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
NSRect viewBounds = [view bounds];
NSRect frameInWindow = [view convertRect:viewBounds toView:nil];
rect = [[view window] convertRectToScreen:frameInWindow];
//Convert coordinates to top-left corner origin
rect = ConvertNSScreenRect(NULL, rect);
}];
jRect = NSToJavaRect(env, rect);
JNI_COCOA_EXIT(env);
return jRect;
}
/*
* Class: sun_lwawt_macosx_CPlatformView
* Method: nativeIsViewUnderMouse
* Signature: (J)Z;
*/
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformView_nativeIsViewUnderMouse
(JNIEnv *env, jclass clazz, jlong viewPtr)
{
__block jboolean underMouse = JNI_FALSE;
JNI_COCOA_ENTER(env);
NSView *nsView = OBJC(viewPtr);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
NSPoint ptWindowCoords = [[nsView window] mouseLocationOutsideOfEventStream];
NSPoint ptViewCoords = [nsView convertPoint:ptWindowCoords fromView:nil];
underMouse = [nsView hitTest:ptViewCoords] != nil;
}];
JNI_COCOA_EXIT(env);
return underMouse;
}