1 /* 2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import "ScreenMenu.h" 27 28 #import "com_apple_laf_ScreenMenu.h" 29 #import "java_awt_Event.h" 30 #import "java_awt_event_KeyEvent.h" 31 #import "java_awt_event_InputEvent.h" 32 #import "java_awt_event_MouseEvent.h" 33 34 #import <JavaRuntimeSupport/JavaRuntimeSupport.h> 35 36 #import "ThreadUtilities.h" 37 #import "CMenuBar.h" 38 #import "JNIUtilities.h" 39 40 static jclass sjc_ScreenMenu = NULL; 41 #define GET_SCREENMENU_CLASS() \ 42 GET_CLASS(sjc_ScreenMenu, "com/apple/laf/ScreenMenu"); 43 44 static jint ns2awtModifiers(NSUInteger keyMods) { 45 jint result = 0; 46 if (keyMods & NSShiftKeyMask) result |= java_awt_Event_SHIFT_MASK; 47 if (keyMods & NSControlKeyMask) result |= java_awt_Event_CTRL_MASK; 48 if (keyMods & NSAlternateKeyMask) result |= java_awt_Event_ALT_MASK; 49 if (keyMods & NSCommandKeyMask) result |= java_awt_Event_META_MASK; 50 return result; 51 } 52 53 static jint ns2awtMouseButton(NSInteger mouseButton) { 54 switch (mouseButton) { 55 case 1: return java_awt_event_InputEvent_BUTTON1_MASK; 56 case 2: return java_awt_event_InputEvent_BUTTON2_MASK; 57 case 3: return java_awt_event_InputEvent_BUTTON3_MASK; 58 } 59 return 0; 60 } 61 62 63 @interface NativeToJavaDelegate : NSObject <JRSMenuDelegate, NSMenuDelegate> 64 { 65 @public 66 NSMenu *nsmenu; 67 jobject javaObject; 68 } 69 70 @property (nonatomic, retain) NSMenu *nsmenu; 71 @property (nonatomic) jobject javaObject; 72 73 - (id)initFromMenu:(NSMenu *)menu javaObj:(jobject)obj; 74 - (NSMenu*)menu; 75 @end 76 77 78 @implementation NativeToJavaDelegate 79 80 @synthesize nsmenu; 81 @synthesize javaObject; 82 83 - (id)initFromMenu:(NSMenu *)aMenu javaObj:(jobject)obj 84 { 85 self = [super init]; 86 if (self) { 87 self.nsmenu = aMenu; 88 self.javaObject = obj; 89 } 90 return self; 91 } 92 93 - (NSMenu *)menu { 94 return self.nsmenu; 95 } 96 97 - (void)menuWillOpen:(NSMenu *)menu 98 { 99 if (self.javaObject == nil) { 100 #ifdef DEBUG 101 NSLog(@"_javaObject is NULL: (%s - %s : %d)", __FILE__, __FUNCTION__, __LINE__); 102 #endif 103 return; 104 } 105 106 JNIEnv *env = [ThreadUtilities getJNIEnv]; 107 JNI_COCOA_ENTER(env); 108 //NSLog(@"menuWillOpen %@", [menu title]); 109 GET_SCREENMENU_CLASS(); 110 DECLARE_METHOD(jm_ScreenMenu_invokeOpenLater, sjc_ScreenMenu, "invokeOpenLater", "()V"); 111 (*env)->CallVoidMethod(env, self.javaObject, jm_ScreenMenu_invokeOpenLater); 112 CHECK_EXCEPTION(); 113 JNI_COCOA_EXIT(env); 114 115 } 116 117 - (void)menuDidClose:(NSMenu *)menu 118 { 119 if (self.javaObject == nil) { 120 #ifdef DEBUG 121 NSLog(@"_javaObject is NULL: (%s - %s : %d)", __FILE__, __FUNCTION__, __LINE__); 122 #endif 123 return; 124 } 125 126 JNIEnv *env = [ThreadUtilities getJNIEnv]; 127 JNI_COCOA_ENTER(env); 128 //NSLog(@"menuDidClose %@", [menu title]); 129 GET_SCREENMENU_CLASS(); 130 DECLARE_METHOD(jm_ScreenMenu_invokeMenuClosing, sjc_ScreenMenu, "invokeMenuClosing", "()V"); 131 (*env)->CallVoidMethod(env, self.javaObject, jm_ScreenMenu_invokeMenuClosing); 132 CHECK_EXCEPTION(); 133 JNI_COCOA_EXIT(env); 134 } 135 136 137 - (void)handleJavaMenuItemTargetedAtIndex:(NSUInteger)menuIndex rect:(NSRect)rect 138 { 139 if (self.javaObject== nil) { 140 #ifdef DEBUG 141 NSLog(@"_javaObject is NULL: (%s - %s : %d)", __FILE__, __FUNCTION__, __LINE__); 142 #endif 143 return; 144 } 145 146 JNIEnv *env = [ThreadUtilities getJNIEnv]; 147 JNI_COCOA_ENTER(env); 148 // Send that to Java so we can test which item was hit. 149 GET_SCREENMENU_CLASS(); 150 DECLARE_METHOD(jm_ScreenMenu_updateSelectedItem, sjc_ScreenMenu, "handleItemTargeted", "(IIIII)V"); 151 (*env)->CallVoidMethod(env, self.javaObject, jm_ScreenMenu_updateSelectedItem, menuIndex, 152 NSMinY(rect), NSMinX(rect), NSMaxY(rect), NSMaxX(rect)); 153 CHECK_EXCEPTION(); 154 155 JNI_COCOA_EXIT(env); 156 } 157 158 /* 159 * The input is an NSTimeInterval (a double representing seconds and fractions of seconds) 160 * 0.0 means midnight Jan 1, 2001. 161 * The output is a Java long representing time in milliseconds since midnight Jan 1st 1970. 162 * There is a Cocoa constant representing that difference : NSTimeIntervalSince1970 163 */ 164 static jlong NSTimeIntervalToJavaMilliseconds(NSTimeInterval interval) { 165 NSTimeInterval interval1970 = interval + NSTimeIntervalSince1970; 166 return (jlong)(interval1970 * 1000); 167 } 168 169 // Called from event handler callback 170 - (void)handleJavaMouseEvent:(NSEvent *)event 171 { 172 NSInteger kind = [event type]; 173 jint javaKind = 0; 174 175 switch (kind) { 176 case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: 177 javaKind = java_awt_event_MouseEvent_MOUSE_RELEASED; 178 break; 179 case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown: 180 javaKind = java_awt_event_MouseEvent_MOUSE_PRESSED; 181 break; 182 case NSMouseMoved: 183 javaKind = java_awt_event_MouseEvent_MOUSE_MOVED; 184 break; 185 case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged: 186 javaKind = java_awt_event_MouseEvent_MOUSE_DRAGGED; 187 break; 188 } 189 190 // Get the coordinates of the mouse in global coordinates (must be global, since our tracking rects are global.) 191 NSPoint globalPoint = [event locationInWindow]; 192 jint javaX = globalPoint.x; 193 jint javaY = globalPoint.y; 194 195 // Convert the event modifiers into Java modifiers 196 jint javaModifiers = ns2awtModifiers([event modifierFlags]) | ns2awtMouseButton([event buttonNumber]); 197 198 // Get the event time 199 jlong javaWhen = NSTimeIntervalToJavaMilliseconds([event timestamp]); 200 201 // Call the mouse event handler, which will generate Java mouse events. 202 JNIEnv *env = [ThreadUtilities getJNIEnv]; 203 JNI_COCOA_ENTER(env); 204 GET_SCREENMENU_CLASS(); 205 DECLARE_METHOD(jm_ScreenMenu_handleMouseEvent, sjc_ScreenMenu, "handleMouseEvent", "(IIIIJ)V"); 206 (*env)->CallVoidMethod(env, self.javaObject, jm_ScreenMenu_handleMouseEvent, 207 javaKind, javaX, javaY, javaModifiers, javaWhen); 208 CHECK_EXCEPTION(); 209 JNI_COCOA_EXIT(env); 210 } 211 212 @end 213 214 215 /* 216 * Class: com_apple_laf_ScreenMenu 217 * Method: addMenuListeners 218 * Signature: (Lcom/apple/laf/ScreenMenu;J[J)V 219 */ 220 JNIEXPORT jlong JNICALL Java_com_apple_laf_ScreenMenu_addMenuListeners 221 (JNIEnv *env, jclass clz, jobject listener, jlong nativeMenu) 222 { 223 NativeToJavaDelegate *delegate = nil; 224 225 JNI_COCOA_ENTER(env); 226 227 jobject listenerRef = (*env)->NewGlobalRef(env, listener); 228 NSMenu *menu = jlong_to_ptr(nativeMenu); 229 230 delegate = [[[NativeToJavaDelegate alloc] initFromMenu:menu javaObj:listenerRef] autorelease]; 231 CFRetain(delegate); // GC 232 233 [ThreadUtilities performOnMainThreadWaiting:YES block:^{ 234 NSMenu *menu = delegate.nsmenu; 235 if ([menu isJavaMenu]) { 236 [menu setDelegate:delegate]; 237 [menu setJavaMenuDelegate:delegate]; 238 } 239 }]; 240 241 JNI_COCOA_EXIT(env); 242 243 return ptr_to_jlong(delegate); 244 } 245 246 /* 247 * Class: com_apple_laf_ScreenMenu 248 * Method: removeMenuListeners 249 * Signature: (JJ)V 250 */ 251 JNIEXPORT void JNICALL Java_com_apple_laf_ScreenMenu_removeMenuListeners 252 (JNIEnv *env, jclass clz, jlong fModelPtr) 253 { 254 if (fModelPtr == 0L) return; 255 256 JNI_COCOA_ENTER(env); 257 258 NativeToJavaDelegate *delegate = (NativeToJavaDelegate *)jlong_to_ptr(fModelPtr); 259 260 [ThreadUtilities performOnMainThreadWaiting:YES block:^{ 261 NSMenu *menu = delegate.nsmenu; 262 [menu setJavaMenuDelegate:nil]; 263 [menu setDelegate:nil]; 264 delegate.nsmenu = nil; 265 }]; 266 267 (*env)->DeleteGlobalRef(env, delegate.javaObject); 268 delegate.javaObject = nil; 269 270 CFRelease(delegate); // GC 271 272 JNI_COCOA_EXIT(env); 273 }