7124321: [macosx] TrayIcon MouseListener is never triggered

Reviewed-by: anthony
This commit is contained in:
Leonid Romanov 2012-10-09 20:59:41 +04:00
parent 63a30e3268
commit 743e5c0d91
3 changed files with 179 additions and 27 deletions

View File

@ -26,6 +26,7 @@
package sun.lwawt.macosx; package sun.lwawt.macosx;
import sun.awt.SunToolkit; import sun.awt.SunToolkit;
import sun.lwawt.macosx.event.NSEvent;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@ -42,6 +43,16 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer {
private JDialog messageDialog; private JDialog messageDialog;
private DialogEventHandler handler; 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) { CTrayIcon(TrayIcon target) {
super(0, true); super(0, true);
@ -49,6 +60,7 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer {
this.handler = null; this.handler = null;
this.target = target; this.target = target;
this.popup = target.getPopupMenu(); this.popup = target.getPopupMenu();
this.dummyFrame = new Frame();
setPtr(createModel()); setPtr(createModel());
//if no one else is creating the peer. //if no one else is creating the peer.
@ -119,6 +131,8 @@ public class CTrayIcon extends CFRetainedResource implements TrayIconPeer {
disposeMessageDialog(); disposeMessageDialog();
} }
dummyFrame.dispose();
LWCToolkit.targetDisposedPeer(target, this); LWCToolkit.targetDisposedPeer(target, this);
target = null; 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); private native void setNativeImage(final long model, final long nsimage, final boolean autosize);
//invocation from the AWTTrayIcon.m private void postEvent(final AWTEvent event) {
public void performAction() {
SunToolkit.executeOnEventHandlerThread(target, new Runnable() { SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
public void run() { public void run() {
final String cmd = target.getActionCommand();
final ActionEvent event = new ActionEvent(target, ActionEvent.ACTION_PERFORMED, cmd);
SunToolkit.postEvent(SunToolkit.targetToAppContext(target), event); 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); private native Point2D nativeGetIconLocation(long trayIconModel);
public void displayMessageOnEDT(String caption, String text, 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.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setModal(false); dialog.setModal(false);
dialog.setModalExclusionType(Dialog.ModalExclusionType.TOOLKIT_EXCLUDE);
dialog.setAlwaysOnTop(true);
dialog.setAutoRequestFocus(false);
dialog.setResizable(false); dialog.setResizable(false);
dialog.setContentPane(op); dialog.setContentPane(op);

View File

@ -53,6 +53,7 @@ extern "C" {
- (jobject) peer; - (jobject) peer;
- (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize; - (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize;
- (NSPoint) getLocationOnScreen; - (NSPoint) getLocationOnScreen;
- (void) deliverJavaMouseEvent:(NSEvent*) event;
@end //AWTTrayIcon @end //AWTTrayIcon
@ -68,6 +69,7 @@ extern "C" {
-(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon; -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon;
-(void)setHighlighted:(BOOL)aFlag; -(void)setHighlighted:(BOOL)aFlag;
-(void)setImage:(NSImage*)anImage; -(void)setImage:(NSImage*)anImage;
-(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon;
@end //AWTTrayIconView @end //AWTTrayIconView

View File

@ -29,6 +29,7 @@
#import "CTrayIcon.h" #import "CTrayIcon.h"
#import "ThreadUtilities.h" #import "ThreadUtilities.h"
#include "GeomUtilities.h" #include "GeomUtilities.h"
#import "LWCToolkit.h"
#define kImageInset 4.0 #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 // 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'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 // 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 setImage: nil];
[view setTrayIcon: nil];
[view release]; [view release];
[theItem release]; [theItem release];
@ -115,6 +117,45 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
return [[view window] convertBaseToScreen: NSZeroPoint]; 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 @end //AWTTrayIcon
//================================================ //================================================
@ -123,7 +164,7 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
-(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon { -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon {
self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)]; self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)];
trayIcon = theTrayIcon; [self setTrayIcon: theTrayIcon];
isHighlighted = NO; isHighlighted = NO;
image = nil; image = nil;
@ -153,6 +194,10 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
} }
} }
-(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon {
trayIcon = theTrayIcon;
}
- (void)menuWillOpen:(NSMenu *)menu - (void)menuWillOpen:(NSMenu *)menu
{ {
[self setHighlighted:YES]; [self setHighlighted:YES];
@ -191,30 +236,57 @@ static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
]; ];
} }
- (void) mouseDown:(NSEvent *)e { - (void)mouseDown:(NSEvent *)event {
//find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr. [trayIcon deliverJavaMouseEvent: event];
JNIEnv *env = [ThreadUtilities getJNIEnv];
static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); // don't show the menu on ctrl+click: it triggers ACTION event, like right click
static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J"); if (([event modifierFlags] & NSControlKeyMask) == 0) {
static JNF_MEMBER_CACHE(jm_performAction, jc_CTrayIcon, "performAction", "()V"); //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr.
jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel); JNIEnv *env = [ThreadUtilities getJNIEnv];
if (res != 0) { static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon");
CPopupMenu *cmenu = jlong_to_ptr(res); static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J");
NSMenu* menu = [cmenu menu]; jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel);
[menu setDelegate:self];
[trayIcon.theItem popUpStatusItemMenu:menu]; if (res != 0) {
[self setNeedsDisplay:YES]; CPopupMenu *cmenu = jlong_to_ptr(res);
} else { NSMenu* menu = [cmenu menu];
JNFCallVoidMethod(env, trayIcon.peer, jm_performAction); [menu setDelegate:self];
[trayIcon.theItem popUpStatusItemMenu:menu];
[self setNeedsDisplay:YES];
}
} }
} }
- (void) rightMouseDown:(NSEvent *)e { - (void) mouseUp:(NSEvent *)event {
// Call CTrayIcon.performAction() method on right mouse press [trayIcon deliverJavaMouseEvent: event];
JNIEnv *env = [ThreadUtilities getJNIEnv]; }
static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon");
static JNF_MEMBER_CACHE(jm_performAction, jc_CTrayIcon, "performAction", "()V"); - (void) mouseDragged:(NSEvent *)event {
JNFCallVoidMethod(env, trayIcon.peer, jm_performAction); [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];
} }