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