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 }