--- old/src/macosx/native/sun/awt/AWTView.h 2012-08-10 16:51:40.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTView.h 2012-08-10 16:51:40.000000000 +0400 @@ -33,8 +33,8 @@ @private jobject m_cPlatformView; - // Handler for the tracking rect needed for Enter/Exit events management. - NSTrackingRectTag rolloverTrackingRectTag; + // Handler for the tracking area needed for Enter/Exit events management. + NSTrackingArea* rolloverTrackingArea; // TODO: NSMenu *contextualMenu; @@ -61,7 +61,7 @@ - (id) initWithRect:(NSRect) rect platformView:(jobject)cPlatformView windowLayer:(CALayer*)windowLayer; - (void) deliverJavaMouseEvent: (NSEvent *) event; -- (void) resetTrackingRect; +- (void) resetTrackingArea; - (void) deliverJavaKeyEventHelper: (NSEvent *) event; - (jobject) awtComponent:(JNIEnv *)env; --- old/src/macosx/native/sun/awt/AWTView.m 2012-08-10 16:51:42.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTView.m 2012-08-10 16:51:41.000000000 +0400 @@ -82,6 +82,7 @@ fPAHNeedsToSelect = NO; mouseIsOver = NO; + [self resetTrackingArea]; if (windowLayer != nil) { self.cglLayer = windowLayer; @@ -146,7 +147,7 @@ [[self window] makeFirstResponder: self]; }]; if ([self window] != NULL) { - [self resetTrackingRect]; + [self resetTrackingArea]; } } @@ -368,30 +369,32 @@ JNFCallVoidMethod(env, m_cPlatformView, jm_deliverMouseEvent, jEvent); } - -- (void) clearTrackingRect { - if (rolloverTrackingRectTag > 0) { - [self removeTrackingRect:rolloverTrackingRectTag]; - rolloverTrackingRectTag = 0; - } -} - -- (void) resetTrackingRect { - [self clearTrackingRect]; - rolloverTrackingRectTag = [self addTrackingRect:[self visibleRect] - owner:self - userData:NULL - assumeInside:NO]; +- (void) resetTrackingArea { + if (rolloverTrackingArea != nil) { + [self removeTrackingArea:rolloverTrackingArea]; + [rolloverTrackingArea release]; + } + + int options = (NSTrackingActiveInActiveApp | NSTrackingCursorUpdate | + NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | + NSTrackingEnabledDuringMouseDrag); + + rolloverTrackingArea = [[NSTrackingArea alloc] initWithRect:[self visibleRect] + options: options + owner:self + userInfo:nil + ]; + [self addTrackingArea:rolloverTrackingArea]; } - (void)updateTrackingAreas { [super updateTrackingAreas]; - [self resetTrackingRect]; + [self resetTrackingArea]; } - (void) resetCursorRects { [super resetCursorRects]; - [self resetTrackingRect]; + [self resetTrackingArea]; } -(void) deliverJavaKeyEventHelper: (NSEvent *) event { --- old/src/macosx/native/sun/awt/AWTWindow.m 2012-08-10 16:51:43.000000000 +0400 +++ new/src/macosx/native/sun/awt/AWTWindow.m 2012-08-10 16:51:42.000000000 +0400 @@ -238,10 +238,12 @@ return self; } -// checks that this window is under the mouse cursor and this point is not overlapped by others windows -- (BOOL) isTopmostWindowUnderMouse { ++ (BOOL) isAWTWindow:(NSWindow *)window { + return [window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]]; +} - int currentWinID = [self.nsWindow windowNumber]; +// returns id for the topmost window under mouse ++ (NSInteger) getTopmostWindowUnderMouseID { NSRect screenRect = [[NSScreen mainScreen] frame]; NSPoint nsMouseLocation = [NSEvent mouseLocation]; @@ -249,53 +251,91 @@ NSMutableArray *windows = (NSMutableArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID); - for (NSDictionary *window in windows) { - int layer = [[window objectForKey:(id)kCGWindowLayer] intValue]; + NSInteger layer = [[window objectForKey:(id)kCGWindowLayer] integerValue]; if (layer == 0) { - int winID = [[window objectForKey:(id)kCGWindowNumber] intValue]; CGRect rect; CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[window objectForKey:(id)kCGWindowBounds], &rect); if (CGRectContainsPoint(rect, cgMouseLocation)) { - return currentWinID == winID; - } else if (currentWinID == winID) { - return NO; + return [[window objectForKey:(id)kCGWindowNumber] integerValue]; } } } - return NO; + return -1; } -- (void) synthesizeMouseEnteredExitedEvents { +// checks that this window is under the mouse cursor and this point is not overlapped by others windows +- (BOOL) isTopmostWindowUnderMouse { + return [self.nsWindow windowNumber] == [AWTWindow getTopmostWindowUnderMouseID]; +} - int eventType = 0; - BOOL isUnderMouse = [self isTopmostWindowUnderMouse]; - BOOL mouseIsOver = [[self.nsWindow contentView] mouseIsOver]; - - if (isUnderMouse && !mouseIsOver) { - eventType = NSMouseEntered; - } else if (!isUnderMouse && mouseIsOver) { - eventType = NSMouseExited; - } else { - return; - } ++ (AWTWindow *) getTopmostWindowUnderMouse { + NSEnumerator *windowEnumerator = [[NSApp windows] objectEnumerator]; + NSWindow *window; + + NSInteger topmostWindowUnderMouseID = [AWTWindow getTopmostWindowUnderMouseID]; + + while ((window = [windowEnumerator nextObject]) != nil) { + if ([window windowNumber] == topmostWindowUnderMouseID) { + BOOL isAWTWindow = [AWTWindow isAWTWindow: window]; + return isAWTWindow ? (AWTWindow *) [window delegate] : nil; + } + } + return nil; +} - NSPoint screenLocation = [NSEvent mouseLocation]; - NSPoint windowLocation = [self.nsWindow convertScreenToBase: screenLocation]; - int modifierFlags = (eventType == NSMouseEntered) ? NSMouseEnteredMask : NSMouseExitedMask; ++ (void) synthesizeMouseEnteredExitedEventsForAllWindows { - NSEvent *mouseEvent = [NSEvent enterExitEventWithType: eventType - location: windowLocation - modifierFlags: modifierFlags + NSPoint screenLocation = [NSEvent mouseLocation]; + NSInteger topmostWindowUnderMouseID = [AWTWindow getTopmostWindowUnderMouseID]; + NSArray *windows = [NSApp windows]; + NSWindow *window; + + // synthesize mouse exited event + NSEnumerator *windowEnumerator = [windows objectEnumerator]; + while ((window = [windowEnumerator nextObject]) != nil) { + if ([AWTWindow isAWTWindow: window]) { + NSInteger windowID = [window windowNumber]; + BOOL isUnderMouse = (windowID == topmostWindowUnderMouseID); + BOOL mouseIsOver = [[window contentView] mouseIsOver]; + if (!isUnderMouse && mouseIsOver) { + NSEvent *mouseEvent = [NSEvent enterExitEventWithType: NSMouseExited + location: [window convertScreenToBase: screenLocation] + modifierFlags: NSMouseExitedMask timestamp: 0 - windowNumber: [self.nsWindow windowNumber] + windowNumber: windowID context: nil eventNumber: 0 trackingNumber: 0 userData: nil ]; + [[window contentView] deliverJavaMouseEvent: mouseEvent]; + } + } + } - [[self.nsWindow contentView] deliverJavaMouseEvent: mouseEvent]; + // synthesize mouse entered event + windowEnumerator = [windows objectEnumerator]; + while ((window = [windowEnumerator nextObject]) != nil) { + if ([AWTWindow isAWTWindow: window]) { + NSInteger windowID = [window windowNumber]; + BOOL isUnderMouse = (windowID == topmostWindowUnderMouseID); + BOOL mouseIsOver = [[window contentView] mouseIsOver]; + if (isUnderMouse && !mouseIsOver) { + NSEvent *mouseEvent = [NSEvent enterExitEventWithType: NSMouseEntered + location: [window convertScreenToBase: screenLocation] + modifierFlags: NSMouseEnteredMask + timestamp: 0 + windowNumber: windowID + context: nil + eventNumber: 0 + trackingNumber: 0 + userData: nil + ]; + [[window contentView] deliverJavaMouseEvent: mouseEvent]; + } + } + } } - (void) dealloc { @@ -825,7 +865,7 @@ // (this will also re-enable screen updates, which were disabled above) // TODO: send PaintEvent - [window synthesizeMouseEnteredExitedEvents]; + [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows]; }]; JNF_COCOA_EXIT(env); @@ -1040,6 +1080,30 @@ /* * Class: sun_lwawt_macosx_CPlatformWindow + * Method: nativeGetTopmostPlatformWindowUnderMouse + * Signature: (J)V + */ +JNIEXPORT jobject +JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeGetTopmostPlatformWindowUnderMouse +(JNIEnv *env, jclass clazz) +{ + jobject topmostWindowUnderMouse = nil; + + JNF_COCOA_ENTER(env); + AWT_ASSERT_APPKIT_THREAD; + + AWTWindow *awtWindow = [AWTWindow getTopmostWindowUnderMouse]; + if (awtWindow != nil) { + topmostWindowUnderMouse = [awtWindow.javaPlatformWindow jObject]; + } + + JNF_COCOA_EXIT(env); + + return topmostWindowUnderMouse; +} + +/* + * Class: sun_lwawt_macosx_CPlatformWindow * Method: nativeSynthesizeMouseEnteredExitedEvents * Signature: (J)V */ @@ -1055,7 +1119,7 @@ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; - [window synthesizeMouseEnteredExitedEvents]; + [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows]; }]; JNF_COCOA_EXIT(env); --- old/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2012-08-10 16:51:44.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2012-08-10 16:51:43.000000000 +0400 @@ -63,6 +63,7 @@ private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled); private static native void nativeSynthesizeMouseEnteredExitedEvents(long nsWindowPtr); private static native void nativeDispose(long nsWindowPtr); + public static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse(); private static native int nativeGetNSWindowDisplayID_AppKitThread(long nsWindowPtr); --- old/src/macosx/classes/sun/lwawt/LWWindowPeer.java 2012-08-10 16:51:45.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/LWWindowPeer.java 2012-08-10 16:51:44.000000000 +0400 @@ -38,6 +38,7 @@ import sun.java2d.loops.Blit; import sun.java2d.loops.CompositeType; import sun.java2d.pipe.Region; +import sun.lwawt.macosx.CPlatformWindow; import sun.util.logging.PlatformLogger; public class LWWindowPeer @@ -88,10 +89,16 @@ private volatile int windowState = Frame.NORMAL; - // A peer where the last mouse event came to. Used to generate - // MOUSE_ENTERED/EXITED notifications and by cursor manager to + // check that the mouse is over the window + private volatile boolean isMouseOver = false; + + // A peer where the last mouse event came to. Used by cursor manager to // find the component under cursor - private static volatile LWComponentPeer lastMouseEventPeer = null; + private static volatile LWComponentPeer lastCommonMouseEventPeer = null; + + // A peer where the last mouse event came to. Used to generate + // MOUSE_ENTERED/EXITED notifications + private volatile LWComponentPeer lastMouseEventPeer; // Peers where all dragged/released events should come to, // depending on what mouse button is being dragged according to Cocoa @@ -676,66 +683,62 @@ Rectangle r = getBounds(); // findPeerAt() expects parent coordinates LWComponentPeer targetPeer = findPeerAt(r.x + x, r.y + y); - LWWindowPeer lastWindowPeer = - (lastMouseEventPeer != null) ? lastMouseEventPeer.getWindowPeerOrSelf() : null; - LWWindowPeer curWindowPeer = - (targetPeer != null) ? targetPeer.getWindowPeerOrSelf() : null; if (id == MouseEvent.MOUSE_EXITED) { - // Sometimes we may get MOUSE_EXITED after lastMouseEventPeer is switched - // to a peer from another window. So we must first check if this peer is - // the same as lastWindowPeer - if (lastWindowPeer == this) { - if (isEnabled()) { + isMouseOver = false; + if (lastMouseEventPeer != null) { + if (lastMouseEventPeer.isEnabled()) { Point lp = lastMouseEventPeer.windowToLocal(x, y, - lastWindowPeer); + this); postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), - MouseEvent.MOUSE_EXITED, when, - modifiers, lp.x, lp.y, screenX, - screenY, clickCount, popupTrigger, - button)); + MouseEvent.MOUSE_EXITED, when, + modifiers, lp.x, lp.y, screenX, + screenY, clickCount, popupTrigger, + button)); + } + + // Sometimes we may get MOUSE_EXITED after lastCommonMouseEventPeer is switched + // to a peer from another window. So we must first check if this peer is + // the same as lastWindowPeer + if (lastCommonMouseEventPeer != null || lastCommonMouseEventPeer.getWindowPeerOrSelf() == this) { + lastCommonMouseEventPeer = null; } lastMouseEventPeer = null; } - } else { - if (targetPeer != lastMouseEventPeer) { - - if (id != MouseEvent.MOUSE_DRAGGED || lastMouseEventPeer == null) { - // lastMouseEventPeer may be null if mouse was out of Java windows - if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { - // Sometimes, MOUSE_EXITED is not sent by delegate (or is sent a bit - // later), in which case lastWindowPeer is another window - if (lastWindowPeer != this) { - Point oldp = lastMouseEventPeer.windowToLocal(x, y, lastWindowPeer); - // Additionally translate from this to lastWindowPeer coordinates - Rectangle lr = lastWindowPeer.getBounds(); - oldp.x += r.x - lr.x; - oldp.y += r.y - lr.y; - postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), - MouseEvent.MOUSE_EXITED, - when, modifiers, - oldp.x, oldp.y, screenX, screenY, - clickCount, popupTrigger, button)); - } else { - Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); - postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), - MouseEvent.MOUSE_EXITED, - when, modifiers, - oldp.x, oldp.y, screenX, screenY, - clickCount, popupTrigger, button)); - } - } - if (targetPeer != null && targetPeer.isEnabled() && id != MouseEvent.MOUSE_ENTERED) { - Point newp = targetPeer.windowToLocal(x, y, curWindowPeer); + } else if(id == MouseEvent.MOUSE_ENTERED) { + isMouseOver = true; + if (targetPeer != null) { + if (targetPeer.isEnabled()) { + Point lp = targetPeer.windowToLocal(x, y, this); postEvent(new MouseEvent(targetPeer.getTarget(), - MouseEvent.MOUSE_ENTERED, - when, modifiers, - newp.x, newp.y, screenX, screenY, - clickCount, popupTrigger, button)); - } + MouseEvent.MOUSE_ENTERED, when, + modifiers, lp.x, lp.y, screenX, + screenY, clickCount, popupTrigger, + button)); } + lastCommonMouseEventPeer = targetPeer; lastMouseEventPeer = targetPeer; } + } else { + CPlatformWindow topmostPlatforWindow = + CPlatformWindow.nativeGetTopmostPlatformWindowUnderMouse(); + LWWindowPeer topmostWindowPeer = + topmostPlatforWindow != null ? topmostPlatforWindow.getPeer() : null; + + if (topmostWindowPeer == this) { + //current window is the topmost window under mouse + generateMouseEnterExitEventsForComponents(when, button, x, y, + screenX, screenY, modifiers, clickCount, popupTrigger, + targetPeer); + } else if (topmostWindowPeer != null) { + // the topmost window under mouse is differ from the current window + LWComponentPeer topmostTargetPeer = + topmostWindowPeer != null ? topmostWindowPeer.findPeerAt(r.x + x, r.y + y) : null; + topmostWindowPeer.generateMouseEnterExitEventsForComponents(when, button, x, y, + screenX, screenY, modifiers, clickCount, popupTrigger, + topmostTargetPeer); + } + // TODO: fill "bdata" member of AWTEvent int eventButtonMask = (button > 0)? MouseEvent.getMaskForButton(button) : 0; @@ -785,19 +788,13 @@ // mouseClickButtons is updated below, after MOUSE_CLICK is sent } - // check if we receive mouseEvent from outside the window's bounds - // it can be either mouseDragged or mouseReleased - if (curWindowPeer == null) { - //TODO This can happen if this window is invisible. this is correct behavior in this case? - curWindowPeer = this; - } if (targetPeer == null) { //TODO This can happen if this window is invisible. this is correct behavior in this case? targetPeer = this; } - Point lp = targetPeer.windowToLocal(x, y, curWindowPeer); + Point lp = targetPeer.windowToLocal(x, y, this); if (targetPeer.isEnabled()) { MouseEvent event = new MouseEvent(targetPeer.getTarget(), id, when, modifiers, lp.x, lp.y, @@ -821,6 +818,38 @@ notifyUpdateCursor(); } + private void generateMouseEnterExitEventsForComponents(long when, + int button, int x, int y, int screenX, int screenY, + int modifiers, int clickCount, boolean popupTrigger, + LWComponentPeer targetPeer) { + + if (!isMouseOver || targetPeer == lastMouseEventPeer) { + return; + } + + // Generate Mouse Exit for components + if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { + Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); + postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), + MouseEvent.MOUSE_EXITED, + when, modifiers, + oldp.x, oldp.y, screenX, screenY, + clickCount, popupTrigger, button)); + } + lastCommonMouseEventPeer = targetPeer; + lastMouseEventPeer = targetPeer; + + // Generate Mouse Enter for components + if (targetPeer != null && targetPeer.isEnabled()) { + Point newp = targetPeer.windowToLocal(x, y, this); + postEvent(new MouseEvent(targetPeer.getTarget(), + MouseEvent.MOUSE_ENTERED, + when, modifiers, + newp.x, newp.y, screenX, screenY, + clickCount, popupTrigger, button)); + } + } + public void dispatchMouseWheelEvent(long when, int x, int y, int modifiers, int scrollType, int scrollAmount, int wheelRotation, double preciseWheelRotation, @@ -1065,11 +1094,11 @@ } public static LWWindowPeer getWindowUnderCursor() { - return lastMouseEventPeer != null ? lastMouseEventPeer.getWindowPeerOrSelf() : null; + return lastCommonMouseEventPeer != null ? lastCommonMouseEventPeer.getWindowPeerOrSelf() : null; } public static LWComponentPeer getPeerUnderCursor() { - return lastMouseEventPeer; + return lastCommonMouseEventPeer; } /*