8080729: [macosx] java 7 and 8 JDialogs on multiscreen jump to parent frame on focus
authordmarkov
Fri, 13 May 2016 14:25:29 +0300
changeset 38970 aabcf4406038
parent 38416 dd0b515bc286
child 38971 0b6191af6176
8080729: [macosx] java 7 and 8 JDialogs on multiscreen jump to parent frame on focus Reviewed-by: ant, denis
jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java
jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
jdk/test/java/awt/Window/WindowJumpingTest/WindowJumpingTest.java
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Fri May 13 12:44:53 2016 +0300
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Fri May 13 14:25:29 2016 +0300
@@ -430,9 +430,6 @@
 
     @Override // PlatformWindow
     public void dispose() {
-        if (owner != null) {
-            CWrapper.NSWindow.removeChildWindow(owner.getNSWindowPtr(), getNSWindowPtr());
-        }
         contentView.dispose();
         nativeDispose(getNSWindowPtr());
         CPlatformWindow.super.dispose();
@@ -527,26 +524,6 @@
     public void setVisible(boolean visible) {
         final long nsWindowPtr = getNSWindowPtr();
 
-        // Process parent-child relationship when hiding
-        final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
-        if (!visible) {
-            // Unparent my children
-            for (Window w : target.getOwnedWindows()) {
-                WindowPeer p = acc.getPeer(w);
-                if (p instanceof LWWindowPeer) {
-                    CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
-                    if (pw != null && pw.isVisible()) {
-                        CWrapper.NSWindow.removeChildWindow(nsWindowPtr, pw.getNSWindowPtr());
-                    }
-                }
-            }
-
-            // Unparent myself
-            if (owner != null && owner.isVisible()) {
-                CWrapper.NSWindow.removeChildWindow(owner.getNSWindowPtr(), nsWindowPtr);
-            }
-        }
-
         // Configure stuff
         updateIconImages();
         updateFocusabilityForAutoRequestFocus(false);
@@ -619,20 +596,22 @@
         updateFocusabilityForAutoRequestFocus(true);
 
         // Manage parent-child relationship when showing
+        final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
+
         if (visible) {
-            // Add myself as a child
+            // Order myself above my parent
             if (owner != null && owner.isVisible()) {
-                CWrapper.NSWindow.addChildWindow(owner.getNSWindowPtr(), nsWindowPtr, CWrapper.NSWindow.NSWindowAbove);
+                CWrapper.NSWindow.orderWindow(nsWindowPtr, CWrapper.NSWindow.NSWindowAbove, owner.getNSWindowPtr());
                 applyWindowLevel(target);
             }
 
-            // Add my own children to myself
+            // Order my own children above myself
             for (Window w : target.getOwnedWindows()) {
                 final Object p = acc.getPeer(w);
                 if (p instanceof LWWindowPeer) {
                     CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
                     if (pw != null && pw.isVisible()) {
-                        CWrapper.NSWindow.addChildWindow(nsWindowPtr, pw.getNSWindowPtr(), CWrapper.NSWindow.NSWindowAbove);
+                        CWrapper.NSWindow.orderWindow(pw.getNSWindowPtr(), CWrapper.NSWindow.NSWindowAbove, nsWindowPtr);
                         pw.applyWindowLevel(w);
                     }
                 }
@@ -1052,8 +1031,8 @@
             // Order the window to front of the stack of child windows
             final long nsWindowSelfPtr = getNSWindowPtr();
             final long nsWindowOwnerPtr = owner.getNSWindowPtr();
-            CWrapper.NSWindow.removeChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr);
-            CWrapper.NSWindow.addChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove);
+            CWrapper.NSWindow.orderFront(nsWindowOwnerPtr);
+            CWrapper.NSWindow.orderWindow(nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove, nsWindowOwnerPtr);
         }
 
         applyWindowLevel(target);
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java	Fri May 13 12:44:53 2016 +0300
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java	Fri May 13 14:25:29 2016 +0300
@@ -206,15 +206,6 @@
         synchronized (lock) {
             final long nsWindowPtr = getNSWindowPtr();
 
-            // Process parent-child relationship when hiding
-            if (!visible) {
-                // Unparent myself
-                if (owner != null && owner.isVisible()) {
-                    CWrapper.NSWindow.removeChildWindow(
-                            owner.getNSWindowPtr(), nsWindowPtr);
-                }
-            }
-
             // Actually show or hide the window
             if (visible) {
                 CWrapper.NSWindow.orderFront(nsWindowPtr);
@@ -226,10 +217,10 @@
 
             // Manage parent-child relationship when showing
             if (visible) {
-                // Add myself as a child
+                // Order myself above my parent
                 if (owner != null && owner.isVisible()) {
-                    CWrapper.NSWindow.addChildWindow(owner.getNSWindowPtr(),
-                            nsWindowPtr, CWrapper.NSWindow.NSWindowAbove);
+                    CWrapper.NSWindow.orderWindow(nsWindowPtr,
+                            CWrapper.NSWindow.NSWindowAbove, owner.getNSWindowPtr());
 
                     // do not allow security warning to be obscured by other windows
                     applyWindowLevel(ownerWindow);
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h	Fri May 13 12:44:53 2016 +0300
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h	Fri May 13 14:25:29 2016 +0300
@@ -46,6 +46,7 @@
     AWTWindow *ownerWindow;
     jint preFullScreenLevel;
     NSRect standardFrame;
+    BOOL isMinimizing;
 }
 
 // An instance of either AWTWindow_Normal or AWTWindow_Panel
@@ -60,6 +61,7 @@
 @property (nonatomic) BOOL isEnabled;
 @property (nonatomic) jint preFullScreenLevel;
 @property (nonatomic) NSRect standardFrame;
+@property (nonatomic) BOOL isMinimizing;
 
 - (id) initWithPlatformWindow:(JNFWeakJObjectWrapper *)javaPlatformWindow
                   ownerWindow:owner
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m	Fri May 13 12:44:53 2016 +0300
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m	Fri May 13 14:25:29 2016 +0300
@@ -180,6 +180,7 @@
 @synthesize ownerWindow;
 @synthesize preFullScreenLevel;
 @synthesize standardFrame;
+@synthesize isMinimizing;
 
 - (void) updateMinMaxSize:(BOOL)resizable {
     if (resizable) {
@@ -304,6 +305,7 @@
     [self.nsWindow release]; // the property retains the object already
 
     self.isEnabled = YES;
+    self.isMinimizing = NO;
     self.javaPlatformWindow = platformWindow;
     self.styleBits = bits;
     self.ownerWindow = owner;
@@ -423,6 +425,68 @@
     [super dealloc];
 }
 
+// Tests wheather the corresponding Java paltform window is visible or not
++ (BOOL) isJavaPlatformWindowVisible:(NSWindow *)window {
+    BOOL isVisible = NO;
+    
+    if ([AWTWindow isAWTWindow:window] && [window delegate] != nil) {
+        AWTWindow *awtWindow = (AWTWindow *)[window delegate];
+        [AWTToolkit eventCountPlusPlus];
+        
+        JNIEnv *env = [ThreadUtilities getJNIEnv];
+        jobject platformWindow = [awtWindow.javaPlatformWindow jObjectWithEnv:env];
+        if (platformWindow != NULL) {
+            static JNF_MEMBER_CACHE(jm_isVisible, jc_CPlatformWindow, "isVisible", "()Z");
+            isVisible = JNFCallBooleanMethod(env, platformWindow, jm_isVisible) == JNI_TRUE ? YES : NO;
+            (*env)->DeleteLocalRef(env, platformWindow);
+            
+        }
+    }
+    return isVisible;
+}
+
+// Orders window's childs based on the current focus state
+- (void) orderChildWindows:(BOOL)focus {
+AWT_ASSERT_APPKIT_THREAD;
+
+    if (self.isMinimizing) {
+        // Do not perform any ordering, if iconify is in progress
+        return;
+    }
+
+    NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
+    NSWindow *window;
+    while ((window = [windowEnumerator nextObject]) != nil) {
+        if ([AWTWindow isJavaPlatformWindowVisible:window]) {
+            AWTWindow *awtWindow = (AWTWindow *)[window delegate];
+            AWTWindow *owner = awtWindow.ownerWindow;
+            if (IS(awtWindow.styleBits, ALWAYS_ON_TOP)) {
+                // Do not order 'always on top' windows
+                continue;
+            }
+            while (awtWindow.ownerWindow != nil) {
+                if (awtWindow.ownerWindow == self) {
+                    if (focus) {
+                        // Move the childWindow to floating level
+                        // so it will appear in front of its
+                        // parent which owns the focus
+                        [window setLevel:NSFloatingWindowLevel];
+                    } else {
+                        // Focus owner has changed, move the childWindow
+                        // back to normal window level
+                        [window setLevel:NSNormalWindowLevel];
+                    }
+                    // The childWindow should be displayed in front of
+                    // its nearest parentWindow
+                    [window orderWindow:NSWindowAbove relativeTo:[owner.nsWindow windowNumber]];
+                    break;
+                }
+                awtWindow = awtWindow.ownerWindow;
+            }
+        }
+    }
+}
+
 // NSWindow overrides
 - (BOOL) canBecomeKeyWindow {
 AWT_ASSERT_APPKIT_THREAD;
@@ -511,6 +575,30 @@
     return [self standardFrame];
 }
 
+// Hides/shows window's childs during iconify/de-iconify operation
+- (void) iconifyChildWindows:(BOOL)iconify {
+AWT_ASSERT_APPKIT_THREAD;
+
+    NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
+    NSWindow *window;
+    while ((window = [windowEnumerator nextObject]) != nil) {
+        if ([AWTWindow isJavaPlatformWindowVisible:window]) {
+            AWTWindow *awtWindow = (AWTWindow *)[window delegate];
+            while (awtWindow.ownerWindow != nil) {
+                if (awtWindow.ownerWindow == self) {
+                    if (iconify) {
+                        [window orderOut:window];
+                    } else {
+                        [window orderFront:window];
+                    }
+                    break;
+                }
+                awtWindow = awtWindow.ownerWindow;
+            }
+        }
+    }
+}
+
 - (void) _deliverIconify:(BOOL)iconify {
 AWT_ASSERT_APPKIT_THREAD;
 
@@ -524,16 +612,28 @@
     }
 }
 
+- (void)windowWillMiniaturize:(NSNotification *)notification {
+AWT_ASSERT_APPKIT_THREAD;
+
+    self.isMinimizing = YES;
+    // Excplicitly make myself a key window to avoid possible
+    // negative visual effects during iconify operation
+    [self.nsWindow makeKeyAndOrderFront:self.nsWindow];
+    [self iconifyChildWindows:YES];
+}
+
 - (void)windowDidMiniaturize:(NSNotification *)notification {
 AWT_ASSERT_APPKIT_THREAD;
 
     [self _deliverIconify:JNI_TRUE];
+    self.isMinimizing = NO;
 }
 
 - (void)windowDidDeminiaturize:(NSNotification *)notification {
 AWT_ASSERT_APPKIT_THREAD;
 
     [self _deliverIconify:JNI_FALSE];
+    [self iconifyChildWindows:NO];
 }
 
 - (void) _deliverWindowFocusEvent:(BOOL)focused oppositeWindow:(AWTWindow *)opposite {
@@ -579,6 +679,7 @@
     [AWTWindow setLastKeyWindow:nil];
 
     [self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
+    [self orderChildWindows:YES];
 }
 
 - (void) windowDidResignKey: (NSNotification *) notification {
@@ -606,6 +707,7 @@
     }
 
     [self _deliverWindowFocusEvent:NO oppositeWindow: opposite];
+    [self orderChildWindows:NO];
 }
 
 - (void) windowDidBecomeMain: (NSNotification *) notification {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Window/WindowJumpingTest/WindowJumpingTest.java	Fri May 13 14:25:29 2016 +0300
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8080729
+ * @summary Dialogs on multiscreen jump to parent frame on focus gain
+ * @author Dmitry Markov
+ * @library ../../regtesthelpers
+ * @build Util
+ * @run main WindowJumpingTest
+ */
+import java.awt.*;
+
+import test.java.awt.regtesthelpers.Util;
+
+public class WindowJumpingTest {
+    public static void main(String[] args) throws AWTException {
+        Robot r = Util.createRobot();
+
+        GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsDevice[] graphicsDevices = graphicsEnvironment.getScreenDevices();
+        if (graphicsDevices.length < 2) {
+            System.out.println("This is multi-screen test... Skipping!");
+            return;
+        }
+
+        Frame frame = new Frame("Frame", graphicsDevices[0].getDefaultConfiguration());
+        frame.setSize(400, 300);
+        frame.setVisible(true);
+        Util.waitForIdle(r);
+
+        Dialog dialog = new Dialog(frame, "Dialog", false, graphicsDevices[1].getDefaultConfiguration());
+        dialog.setSize(400, 300);
+        dialog.setVisible(true);
+        Util.waitForIdle(r);
+
+        checkGraphicsDevice(frame, graphicsDevices[0]);
+        checkGraphicsDevice(dialog, graphicsDevices[1]);
+
+        Util.clickOnComp(frame, r);
+        Util.waitForIdle(r);
+
+        checkGraphicsDevice(frame, graphicsDevices[0]);
+        checkGraphicsDevice(dialog, graphicsDevices[1]);
+
+        Util.clickOnComp(dialog, r);
+        Util.waitForIdle(r);
+
+        checkGraphicsDevice(frame, graphicsDevices[0]);
+        checkGraphicsDevice(dialog, graphicsDevices[1]);
+
+        dialog.dispose();
+        frame.dispose();
+    }
+
+    private static void checkGraphicsDevice(Window window, GraphicsDevice graphicsDevice) {
+        GraphicsDevice actualGraphicsDevice = window.getGraphicsConfiguration().getDevice();
+
+        if (!actualGraphicsDevice.equals(graphicsDevice)) {
+            System.err.println("Expected screen: " + graphicsDevice);
+            System.err.println("Actual screen: "+ actualGraphicsDevice);
+            throw new RuntimeException("Test FAILED: " + window + " is displayed on wrong screen");
+        }
+    }
+}
+