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 <JavaNativeFoundation/JavaNativeFoundation.h>
  35 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
  36 
  37 #import "ThreadUtilities.h"
  38 #import "CMenuBar.h"
  39 
  40 static JNF_CLASS_CACHE(sjc_ScreenMenu, "com/apple/laf/ScreenMenu");
  41 
  42 static jint ns2awtModifiers(NSUInteger keyMods) {
  43     jint result = 0;
  44     if (keyMods & NSShiftKeyMask)        result |= java_awt_Event_SHIFT_MASK;
  45     if (keyMods & NSControlKeyMask)        result |= java_awt_Event_CTRL_MASK;
  46     if (keyMods & NSAlternateKeyMask)    result |= java_awt_Event_ALT_MASK;
  47     if (keyMods & NSCommandKeyMask)        result |= java_awt_Event_META_MASK;
  48     return result;
  49 }
  50 
  51 static jint ns2awtMouseButton(NSInteger mouseButton) {
  52     switch (mouseButton) {
  53         case 1: return java_awt_event_InputEvent_BUTTON1_MASK;
  54         case 2: return java_awt_event_InputEvent_BUTTON2_MASK;
  55         case 3: return java_awt_event_InputEvent_BUTTON3_MASK;
  56     }
  57     return 0;
  58 }
  59 
  60 
  61 @interface NativeToJavaDelegate : NSObject <JRSMenuDelegate, NSMenuDelegate>
  62 {
  63 @public
  64     NSMenu *nsmenu;
  65     JNFJObjectWrapper *javaObjectWrapper;
  66 }
  67 
  68 @property (nonatomic, retain) NSMenu *nsmenu;
  69 @property (nonatomic, retain) JNFJObjectWrapper *javaObjectWrapper;
  70 
  71 - (id)initFromMenu:(NSMenu *)menu javaObj:(JNFJObjectWrapper *)obj;
  72 - (NSMenu*)menu;
  73 @end
  74 
  75 
  76 @implementation NativeToJavaDelegate
  77 
  78 @synthesize nsmenu;
  79 @synthesize javaObjectWrapper;
  80 
  81 - (id)initFromMenu:(NSMenu *)aMenu javaObj:(JNFJObjectWrapper *)obj
  82 {
  83     self = [super init];
  84     if (self) {
  85         self.nsmenu = aMenu;
  86         self.javaObjectWrapper = obj;
  87     }
  88     return self;
  89 }
  90 
  91 - (NSMenu *)menu {
  92     return self.nsmenu;
  93 }
  94 
  95 - (void)menuWillOpen:(NSMenu *)menu
  96 {
  97     if (self.javaObjectWrapper == nil) {
  98 #ifdef DEBUG
  99         NSLog(@"_javaObject is NULL: (%s - %s : %d)", __FILE__, __FUNCTION__, __LINE__);
 100 #endif
 101         return;
 102     }
 103 
 104     JNIEnv *env = [ThreadUtilities getJNIEnv];
 105 JNF_COCOA_ENTER(env);
 106     //NSLog(@"menuWillOpen %@", [menu title]);
 107     static JNF_MEMBER_CACHE(jm_ScreenMenu_invokeOpenLater, sjc_ScreenMenu, "invokeOpenLater", "()V");
 108     JNFCallVoidMethod(env, [self.javaObjectWrapper jObject], jm_ScreenMenu_invokeOpenLater); // AWT_THREADING Safe (AWTRunLoopMode)
 109 JNF_COCOA_EXIT(env);
 110 
 111 }
 112 
 113 - (void)menuDidClose:(NSMenu *)menu
 114 {
 115     if (self.javaObjectWrapper == nil) {
 116 #ifdef DEBUG
 117         NSLog(@"_javaObject is NULL: (%s - %s : %d)", __FILE__, __FUNCTION__, __LINE__);
 118 #endif
 119         return;
 120     }
 121 
 122     JNIEnv *env = [ThreadUtilities getJNIEnv];
 123 JNF_COCOA_ENTER(env);
 124     //NSLog(@"menuDidClose %@", [menu title]);
 125     static JNF_MEMBER_CACHE(jm_ScreenMenu_invokeMenuClosing, sjc_ScreenMenu, "invokeMenuClosing", "()V");
 126     JNFCallVoidMethod(env, [self.javaObjectWrapper jObject], jm_ScreenMenu_invokeMenuClosing); // AWT_THREADING Safe (AWTRunLoopMode)
 127 JNF_COCOA_EXIT(env);
 128 }
 129 
 130 
 131 - (void)handleJavaMenuItemTargetedAtIndex:(NSUInteger)menuIndex rect:(NSRect)rect
 132 {
 133     if (self.javaObjectWrapper == nil) {
 134 #ifdef DEBUG
 135         NSLog(@"_javaObject is NULL: (%s - %s : %d)", __FILE__, __FUNCTION__, __LINE__);
 136 #endif
 137         return;
 138     }
 139 
 140     JNIEnv *env = [ThreadUtilities getJNIEnv];
 141 JNF_COCOA_ENTER(env);
 142     // Send that to Java so we can test which item was hit.
 143     static JNF_MEMBER_CACHE(jm_ScreenMenu_updateSelectedItem, sjc_ScreenMenu, "handleItemTargeted", "(IIIII)V");
 144     JNFCallVoidMethod(env, [self.javaObjectWrapper jObject], jm_ScreenMenu_updateSelectedItem, menuIndex,
 145                     NSMinY(rect), NSMinX(rect), NSMaxY(rect), NSMaxX(rect)); // AWT_THREADING Safe (AWTRunLoopMode)
 146 
 147 JNF_COCOA_EXIT(env);
 148 }
 149 
 150 
 151 // Called from event handler callback
 152 - (void)handleJavaMouseEvent:(NSEvent *)event
 153 {
 154     NSInteger kind = [event type];
 155     jint javaKind = 0;
 156 
 157     switch (kind) {
 158         case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp:
 159             javaKind = java_awt_event_MouseEvent_MOUSE_RELEASED;
 160             break;
 161         case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown:
 162             javaKind = java_awt_event_MouseEvent_MOUSE_PRESSED;
 163             break;
 164         case NSMouseMoved:
 165             javaKind = java_awt_event_MouseEvent_MOUSE_MOVED;
 166             break;
 167         case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged:
 168             javaKind = java_awt_event_MouseEvent_MOUSE_DRAGGED;
 169             break;
 170     }
 171 
 172     // Get the coordinates of the mouse in global coordinates (must be global, since our tracking rects are global.)
 173     NSPoint globalPoint = [event locationInWindow];
 174     jint javaX = globalPoint.x;
 175     jint javaY = globalPoint.y;
 176 
 177     // Convert the event modifiers into Java modifiers
 178     jint javaModifiers = ns2awtModifiers([event modifierFlags]) | ns2awtMouseButton([event buttonNumber]);
 179 
 180     // Get the event time
 181     jlong javaWhen = JNFNSTimeIntervalToJavaMillis([event timestamp]);
 182 
 183     // Call the mouse event handler, which will generate Java mouse events.
 184     JNIEnv *env = [ThreadUtilities getJNIEnv];
 185 JNF_COCOA_ENTER(env);
 186     static JNF_MEMBER_CACHE(jm_ScreenMenu_handleMouseEvent, sjc_ScreenMenu, "handleMouseEvent", "(IIIIJ)V");
 187     JNFCallVoidMethod(env, [self.javaObjectWrapper jObject], jm_ScreenMenu_handleMouseEvent, javaKind, javaX, javaY, javaModifiers, javaWhen); // AWT_THREADING Safe (AWTRunLoopMode)
 188 JNF_COCOA_EXIT(env);
 189 }
 190 
 191 @end
 192 
 193 
 194 /*
 195  * Class:     com_apple_laf_ScreenMenu
 196  * Method:    addMenuListeners
 197  * Signature: (Lcom/apple/laf/ScreenMenu;J[J)V
 198  */
 199 JNIEXPORT jlong JNICALL Java_com_apple_laf_ScreenMenu_addMenuListeners
 200 (JNIEnv *env, jclass clz, jobject listener, jlong nativeMenu)
 201 {
 202     NativeToJavaDelegate *delegate = nil;
 203 
 204 JNF_COCOA_ENTER(env);
 205 
 206     JNFJObjectWrapper *wrapper = [JNFJObjectWrapper wrapperWithJObject:listener withEnv:env];
 207     NSMenu *menu = jlong_to_ptr(nativeMenu);
 208 
 209     delegate = [[[NativeToJavaDelegate alloc] initFromMenu:menu javaObj:wrapper] autorelease];
 210     CFRetain(delegate); // GC
 211 
 212     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^{
 213         NSMenu *menu = delegate.nsmenu;
 214         if ([menu isJavaMenu]) {
 215             [menu setDelegate:delegate];
 216             [menu setJavaMenuDelegate:delegate];
 217         }
 218     }];
 219 
 220 JNF_COCOA_EXIT(env);
 221 
 222     return ptr_to_jlong(delegate);
 223 }
 224 
 225 /*
 226  * Class:     com_apple_laf_ScreenMenu
 227  * Method:    removeMenuListeners
 228  * Signature: (JJ)V
 229  */
 230 JNIEXPORT void JNICALL Java_com_apple_laf_ScreenMenu_removeMenuListeners
 231 (JNIEnv *env, jclass clz, jlong fModelPtr)
 232 {
 233     if (fModelPtr == 0L) return;
 234 
 235 JNF_COCOA_ENTER(env);
 236 
 237     NativeToJavaDelegate *delegate = (NativeToJavaDelegate *)jlong_to_ptr(fModelPtr);
 238 
 239     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^{
 240         NSMenu *menu = delegate.nsmenu;
 241         [menu setJavaMenuDelegate:nil];
 242         [menu setDelegate:nil];
 243         delegate.nsmenu = nil;
 244         delegate.javaObjectWrapper = nil;
 245     }];
 246 
 247     CFRelease(delegate); // GC
 248 
 249 JNF_COCOA_EXIT(env);
 250 }