From 743e5c0d91b90231822411769c092220205e8e72 Mon Sep 17 00:00:00 2001 From: Leonid Romanov Date: Tue, 9 Oct 2012 20:59:41 +0400 Subject: [PATCH] 7124321: [macosx] TrayIcon MouseListener is never triggered Reviewed-by: anthony --- .../classes/sun/lwawt/macosx/CTrayIcon.java | 86 ++++++++++++- jdk/src/macosx/native/sun/awt/CTrayIcon.h | 2 + jdk/src/macosx/native/sun/awt/CTrayIcon.m | 118 ++++++++++++++---- 3 files changed, 179 insertions(+), 27 deletions(-) diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java index fac844e4229..4a19e5a064d 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CTrayIcon.java @@ -26,6 +26,7 @@ package sun.lwawt.macosx; import sun.awt.SunToolkit; +import sun.lwawt.macosx.event.NSEvent; import javax.swing.*; import java.awt.*; @@ -42,6 +43,16 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer { private JDialog messageDialog; private DialogEventHandler handler; + // In order to construct MouseEvent object, we need to specify a + // Component target. Because TrayIcon isn't Component's subclass, + // we use this dummy frame instead + private final Frame dummyFrame; + + // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events + // on MOUSE_RELEASE. Click events are only generated if there were no drag + // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button + private static int mouseClickButtons = 0; + CTrayIcon(TrayIcon target) { super(0, true); @@ -49,6 +60,7 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer { this.handler = null; this.target = target; this.popup = target.getPopupMenu(); + this.dummyFrame = new Frame(); setPtr(createModel()); //if no one else is creating the peer. @@ -119,6 +131,8 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer { disposeMessageDialog(); } + dummyFrame.dispose(); + LWCToolkit.targetDisposedPeer(target, this); target = null; @@ -161,17 +175,78 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer { private native void setNativeImage(final long model, final long nsimage, final boolean autosize); - //invocation from the AWTTrayIcon.m - public void performAction() { + private void postEvent(final AWTEvent event) { SunToolkit.executeOnEventHandlerThread(target, new Runnable() { public void run() { - final String cmd = target.getActionCommand(); - final ActionEvent event = new ActionEvent(target, ActionEvent.ACTION_PERFORMED, cmd); SunToolkit.postEvent(SunToolkit.targetToAppContext(target), event); } }); } + //invocation from the AWTTrayIcon.m + private void handleMouseEvent(NSEvent nsEvent) { + int buttonNumber = nsEvent.getButtonNumber(); + final SunToolkit tk = (SunToolkit)Toolkit.getDefaultToolkit(); + if ((buttonNumber > 2 && !tk.areExtraMouseButtonsEnabled()) + || buttonNumber > tk.getNumberOfButtons() - 1) { + return; + } + + int jeventType = NSEvent.nsToJavaEventType(nsEvent.getType()); + + int jbuttonNumber = MouseEvent.NOBUTTON; + int jclickCount = 0; + if (jeventType != MouseEvent.MOUSE_MOVED) { + jbuttonNumber = NSEvent.nsToJavaButton(buttonNumber); + jclickCount = nsEvent.getClickCount(); + } + + int jmodifiers = NSEvent.nsToJavaMouseModifiers(buttonNumber, + nsEvent.getModifierFlags()); + boolean isPopupTrigger = NSEvent.isPopupTrigger(jmodifiers); + + int eventButtonMask = (jbuttonNumber > 0)? + MouseEvent.getMaskForButton(jbuttonNumber) : 0; + long when = System.currentTimeMillis(); + + if (jeventType == MouseEvent.MOUSE_PRESSED) { + mouseClickButtons |= eventButtonMask; + } else if (jeventType == MouseEvent.MOUSE_DRAGGED) { + mouseClickButtons = 0; + } + + // The MouseEvent's coordinates are relative to screen + int absX = nsEvent.getAbsX(); + int absY = nsEvent.getAbsY(); + + MouseEvent mouseEvent = new MouseEvent(dummyFrame, jeventType, when, + jmodifiers, absX, absY, absX, absY, jclickCount, isPopupTrigger, + jbuttonNumber); + mouseEvent.setSource(target); + postEvent(mouseEvent); + + // fire ACTION event + if (jeventType == MouseEvent.MOUSE_PRESSED && isPopupTrigger) { + final String cmd = target.getActionCommand(); + final ActionEvent event = new ActionEvent(target, + ActionEvent.ACTION_PERFORMED, cmd); + postEvent(event); + } + + // synthesize CLICKED event + if (jeventType == MouseEvent.MOUSE_RELEASED) { + if ((mouseClickButtons & eventButtonMask) != 0) { + MouseEvent clickEvent = new MouseEvent(dummyFrame, + MouseEvent.MOUSE_CLICKED, when, jmodifiers, absX, absY, + absX, absY, jclickCount, isPopupTrigger, jbuttonNumber); + clickEvent.setSource(target); + postEvent(clickEvent); + } + + mouseClickButtons &= ~eventButtonMask; + } + } + private native Point2D nativeGetIconLocation(long trayIconModel); public void displayMessageOnEDT(String caption, String text, @@ -256,6 +331,9 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer { dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.setModal(false); + dialog.setModalExclusionType(Dialog.ModalExclusionType.TOOLKIT_EXCLUDE); + dialog.setAlwaysOnTop(true); + dialog.setAutoRequestFocus(false); dialog.setResizable(false); dialog.setContentPane(op); diff --git a/jdk/src/macosx/native/sun/awt/CTrayIcon.h b/jdk/src/macosx/native/sun/awt/CTrayIcon.h index c5ceebf3414..9501e7ddde4 100644 --- a/jdk/src/macosx/native/sun/awt/CTrayIcon.h +++ b/jdk/src/macosx/native/sun/awt/CTrayIcon.h @@ -53,6 +53,7 @@ extern "C" { - (jobject) peer; - (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize; - (NSPoint) getLocationOnScreen; +- (void) deliverJavaMouseEvent:(NSEvent*) event; @end //AWTTrayIcon @@ -68,6 +69,7 @@ extern "C" { -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon; -(void)setHighlighted:(BOOL)aFlag; -(void)setImage:(NSImage*)anImage; +-(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon; @end //AWTTrayIconView diff --git a/jdk/src/macosx/native/sun/awt/CTrayIcon.m b/jdk/src/macosx/native/sun/awt/CTrayIcon.m index b9c2942f1b7..a69995f84aa 100644 --- a/jdk/src/macosx/native/sun/awt/CTrayIcon.m +++ b/jdk/src/macosx/native/sun/awt/CTrayIcon.m @@ -29,6 +29,7 @@ #import "CTrayIcon.h" #import "ThreadUtilities.h" #include "GeomUtilities.h" +#import "LWCToolkit.h" #define kImageInset 4.0 @@ -76,8 +77,9 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { // Its a bad idea to force the item to release our view by setting // the item's view to nil: it can lead to a crash in some scenarios. // The item will release the view later on, so just set the view's image - // to nil since we are done with it. + // and tray icon to nil since we are done with it. [view setImage: nil]; + [view setTrayIcon: nil]; [view release]; [theItem release]; @@ -115,6 +117,45 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { return [[view window] convertBaseToScreen: NSZeroPoint]; } +-(void) deliverJavaMouseEvent: (NSEvent *) event { + [AWTToolkit eventCountPlusPlus]; + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + + NSPoint eventLocation = [event locationInWindow]; + NSPoint localPoint = [view convertPoint: eventLocation fromView: nil]; + localPoint.y = [view bounds].size.height - localPoint.y; + + NSPoint absP = [NSEvent mouseLocation]; + NSEventType type = [event type]; + + NSRect screenRect = [[NSScreen mainScreen] frame]; + absP.y = screenRect.size.height - absP.y; + jint clickCount; + + clickCount = [event clickCount]; + + static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/event/NSEvent"); + static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDD)V"); + jobject jEvent = JNFNewObject(env, jctor_NSEvent, + [event type], + [event modifierFlags], + clickCount, + [event buttonNumber], + (jint)localPoint.x, (jint)localPoint.y, + (jint)absP.x, (jint)absP.y, + [event deltaY], + [event deltaX]); + if (jEvent == nil) { + // Unable to create event by some reason. + return; + } + + static JNF_CLASS_CACHE(jc_TrayIcon, "sun/lwawt/macosx/CTrayIcon"); + static JNF_MEMBER_CACHE(jm_handleMouseEvent, jc_TrayIcon, "handleMouseEvent", "(Lsun/lwawt/macosx/event/NSEvent;)V"); + JNFCallVoidMethod(env, peer, jm_handleMouseEvent, jEvent); +} + @end //AWTTrayIcon //================================================ @@ -123,7 +164,7 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon { self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)]; - trayIcon = theTrayIcon; + [self setTrayIcon: theTrayIcon]; isHighlighted = NO; image = nil; @@ -153,6 +194,10 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { } } +-(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon { + trayIcon = theTrayIcon; +} + - (void)menuWillOpen:(NSMenu *)menu { [self setHighlighted:YES]; @@ -191,30 +236,57 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { ]; } -- (void) mouseDown:(NSEvent *)e { - //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr. - JNIEnv *env = [ThreadUtilities getJNIEnv]; - static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); - static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J"); - static JNF_MEMBER_CACHE(jm_performAction, jc_CTrayIcon, "performAction", "()V"); - jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel); - if (res != 0) { - CPopupMenu *cmenu = jlong_to_ptr(res); - NSMenu* menu = [cmenu menu]; - [menu setDelegate:self]; - [trayIcon.theItem popUpStatusItemMenu:menu]; - [self setNeedsDisplay:YES]; - } else { - JNFCallVoidMethod(env, trayIcon.peer, jm_performAction); +- (void)mouseDown:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; + + // don't show the menu on ctrl+click: it triggers ACTION event, like right click + if (([event modifierFlags] & NSControlKeyMask) == 0) { + //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr. + JNIEnv *env = [ThreadUtilities getJNIEnv]; + static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); + static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J"); + jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel); + + if (res != 0) { + CPopupMenu *cmenu = jlong_to_ptr(res); + NSMenu* menu = [cmenu menu]; + [menu setDelegate:self]; + [trayIcon.theItem popUpStatusItemMenu:menu]; + [self setNeedsDisplay:YES]; + } } } -- (void) rightMouseDown:(NSEvent *)e { - // Call CTrayIcon.performAction() method on right mouse press - JNIEnv *env = [ThreadUtilities getJNIEnv]; - static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); - static JNF_MEMBER_CACHE(jm_performAction, jc_CTrayIcon, "performAction", "()V"); - JNFCallVoidMethod(env, trayIcon.peer, jm_performAction); +- (void) mouseUp:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) mouseDragged:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) rightMouseDown:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) rightMouseUp:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) rightMouseDragged:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) otherMouseDown:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) otherMouseUp:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; +} + +- (void) otherMouseDragged:(NSEvent *)event { + [trayIcon deliverJavaMouseEvent: event]; }