8169589: [macosx] Activating a JDialog puts to back another dialog
authordmarkov
Fri, 02 Dec 2016 12:45:52 +0300
changeset 42731 631236c16dff
parent 42730 9ed3ac7896dc
child 42732 93c50fe10c2a
8169589: [macosx] Activating a JDialog puts to back another dialog Reviewed-by: aivanov, serb
jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
jdk/src/java.desktop/share/classes/java/awt/Window.java
jdk/src/java.desktop/share/classes/sun/awt/AWTAccessor.java
jdk/test/java/awt/Dialog/DialogAboveFrame/DialogAboveFrameTest.java
--- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Dec 01 10:34:10 2016 -0800
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Fri Dec 02 12:45:52 2016 +0300
@@ -33,11 +33,14 @@
 import java.awt.event.*;
 import java.beans.*;
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 import javax.swing.*;
 
 import sun.awt.*;
 import sun.awt.AWTAccessor.ComponentAccessor;
+import sun.awt.AWTAccessor.WindowAccessor;
 import sun.java2d.SurfaceData;
 import sun.java2d.opengl.CGLSurfaceData;
 import sun.lwawt.*;
@@ -1063,29 +1066,70 @@
         return true;
     }
 
+    private boolean isOneOfOwnersOrSelf(CPlatformWindow window) {
+        while (window != null) {
+            if (this == window) {
+                return true;
+            }
+            window = window.owner;
+        }
+        return false;
+    }
+
+    private CPlatformWindow getRootOwner() {
+        CPlatformWindow rootOwner = this;
+        while (rootOwner.owner != null) {
+            rootOwner = rootOwner.owner;
+        }
+        return rootOwner;
+    }
+
     private void orderAboveSiblings() {
-        if (owner == null) {
-            return;
+        // Recursively pop up the windows from the very bottom, (i.e. root owner) so that
+        // the windows are ordered above their nearest owner; ancestors of the window,
+        // which is going to become 'main window', are placed above their siblings.
+        CPlatformWindow rootOwner = getRootOwner();
+        if (rootOwner.isVisible()) {
+            CWrapper.NSWindow.orderFront(rootOwner.getNSWindowPtr());
         }
+        final WindowAccessor windowAccessor = AWTAccessor.getWindowAccessor();
+        orderAboveSiblingsImpl(windowAccessor.getOwnedWindows(rootOwner.target));
+    }
 
-        // NOTE: the logic will fail if we have a hierarchy like:
-        //       visible root owner
-        //          invisible owner
-        //              visible dialog
-        // However, this is an unlikely scenario for real life apps
-        if (owner.isVisible()) {
-            // Recursively pop up the windows from the very bottom so that only
-            // the very top-most one becomes the main window
-            owner.orderAboveSiblings();
+    private void orderAboveSiblingsImpl(Window[] windows) {
+        ArrayList<Window> childWindows = new ArrayList<Window>();
+
+        final ComponentAccessor componentAccessor = AWTAccessor.getComponentAccessor();
+        final WindowAccessor windowAccessor = AWTAccessor.getWindowAccessor();
 
-            // Order the window to front of the stack of child windows
-            final long nsWindowSelfPtr = getNSWindowPtr();
-            final long nsWindowOwnerPtr = owner.getNSWindowPtr();
-            CWrapper.NSWindow.orderFront(nsWindowOwnerPtr);
-            CWrapper.NSWindow.orderWindow(nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove, nsWindowOwnerPtr);
+        // Go through the list of windows and perform ordering.
+        for (Window w : windows) {
+            final Object p = componentAccessor.getPeer(w);
+            if (p instanceof LWWindowPeer) {
+                CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
+                if (pw != null && pw.isVisible()) {
+                    // If the window is one of ancestors of 'main window' or is going to become main by itself,
+                    // the window should be ordered above its siblings; otherwise the window is just ordered
+                    // above its nearest parent.
+                    if (pw.isOneOfOwnersOrSelf(this)) {
+                        CWrapper.NSWindow.orderFront(pw.getNSWindowPtr());
+                    } else {
+                        CWrapper.NSWindow.orderWindow(pw.getNSWindowPtr(), CWrapper.NSWindow.NSWindowAbove,
+                                pw.owner.getNSWindowPtr());
+                    }
+                    pw.applyWindowLevel(w);
+                }
+            }
+            // Retrieve the child windows for each window from the list and store them for future use.
+            // Note: we collect data about child windows even for invisible owners, since they may have
+            // visible children.
+            childWindows.addAll(Arrays.asList(windowAccessor.getOwnedWindows(w)));
         }
-
-        applyWindowLevel(target);
+        // If some windows, which have just been ordered, have any child windows, let's start new iteration
+        // and order these child windows.
+        if (!childWindows.isEmpty()) {
+            orderAboveSiblingsImpl(childWindows.toArray(new Window[0]));
+        }
     }
 
     protected void applyWindowLevel(Window target) {
--- a/jdk/src/java.desktop/share/classes/java/awt/Window.java	Thu Dec 01 10:34:10 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/java/awt/Window.java	Fri Dec 02 12:45:52 2016 +0300
@@ -4122,6 +4122,10 @@
             public void setTrayIconWindow(Window w, boolean isTrayIconWindow) {
                 w.isTrayIconWindow = isTrayIconWindow;
             }
+
+            public Window[] getOwnedWindows(Window w) {
+                return w.getOwnedWindows_NoClientCode();
+            }
         }); // WindowAccessor
     } // static
 
--- a/jdk/src/java.desktop/share/classes/sun/awt/AWTAccessor.java	Thu Dec 01 10:34:10 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/awt/AWTAccessor.java	Fri Dec 02 12:45:52 2016 +0300
@@ -360,6 +360,12 @@
          * Marks the specified window as an utility window for TrayIcon.
          */
         void setTrayIconWindow(Window w, boolean isTrayIconWindow);
+
+        /**
+         * Return an array containing all the windows this
+         * window currently owns.
+         */
+        Window[] getOwnedWindows(Window w);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Dialog/DialogAboveFrame/DialogAboveFrameTest.java	Fri Dec 02 12:45:52 2016 +0300
@@ -0,0 +1,81 @@
+/*
+ * 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 8169589
+ * @summary Activating a dialog puts to back another dialog owned by the same frame
+ * @author Dmitry Markov
+ * @library ../../regtesthelpers
+ * @build Util
+ * @run main DialogAboveFrameTest
+ */
+
+import java.awt.Color;
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.Point;
+import java.awt.Robot;
+
+import test.java.awt.regtesthelpers.Util;
+
+public class DialogAboveFrameTest {
+    public static void main(String[] args) {
+        Robot robot = Util.createRobot();
+
+        Frame frame = new Frame("Frame");
+        frame.setBackground(Color.BLUE);
+        frame.setBounds(200, 50, 300, 300);
+        frame.setVisible(true);
+
+        Dialog dialog1 = new Dialog(frame, "Dialog 1", false);
+        dialog1.setBackground(Color.RED);
+        dialog1.setBounds(100, 100, 200, 200);
+        dialog1.setVisible(true);
+
+        Dialog dialog2 = new Dialog(frame, "Dialog 2", false);
+        dialog2.setBackground(Color.GREEN);
+        dialog2.setBounds(400, 100, 200, 200);
+        dialog2.setVisible(true);
+
+        Util.waitForIdle(robot);
+
+        Util.clickOnComp(dialog2, robot);
+        Util.waitForIdle(robot);
+
+        Point point = dialog1.getLocationOnScreen();
+        int x = point.x + (int)(dialog1.getWidth() * 0.9);
+        int y = point.y + (int)(dialog1.getHeight() * 0.9);
+
+        try {
+            if (!robot.getPixelColor(x, y).equals(dialog1.getBackground())) {
+                throw new RuntimeException("Test FAILED: Dialog is behind the frame");
+            }
+        } finally {
+            frame.dispose();
+            dialog1.dispose();
+            dialog2.dispose();
+        }
+    }
+}
+