6694823: A popup menu can be partially hidden under the task bar in applets
authormlapshin
Mon, 26 May 2008 17:58:09 +0400
changeset 675 4f26ea16c5c1
parent 674 4867bdd4fa38
child 676 8cf833d60e87
6694823: A popup menu can be partially hidden under the task bar in applets Summary: In applets popup menu is shifted above the task bar Reviewed-by: peterz
jdk/src/share/classes/javax/swing/JPopupMenu.java
jdk/src/share/classes/javax/swing/PopupFactory.java
jdk/test/javax/swing/JPopupMenu/6694823/bug6694823.java
--- a/jdk/src/share/classes/javax/swing/JPopupMenu.java	Fri May 23 20:14:20 2008 +0400
+++ b/jdk/src/share/classes/javax/swing/JPopupMenu.java	Mon May 26 17:58:09 2008 +0400
@@ -41,6 +41,7 @@
 import javax.swing.plaf.ComponentUI;
 import javax.swing.plaf.basic.BasicComboPopup;
 import javax.swing.event.*;
+import sun.security.util.SecurityConstants;
 
 import java.applet.Applet;
 
@@ -320,17 +321,67 @@
      * This adustment may be cancelled by invoking the application with
      * -Djavax.swing.adjustPopupLocationToFit=false
      */
-    Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
-        Point p = new Point(xposition, yposition);
+    Point adjustPopupLocationToFitScreen(int xPosition, int yPosition) {
+        Point popupLocation = new Point(xPosition, yPosition);
+
+        if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless()) {
+            return popupLocation;
+        }
 
-        if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
-            return p;
+        // Get screen bounds
+        Rectangle scrBounds;
+        GraphicsConfiguration gc = getCurrentGraphicsConfiguration(popupLocation);
+        Toolkit toolkit = Toolkit.getDefaultToolkit();
+        if(gc != null) {
+            // If we have GraphicsConfiguration use it to get screen bounds
+            scrBounds = gc.getBounds();
+        } else {
+            // If we don't have GraphicsConfiguration use primary screen
+            scrBounds = new Rectangle(toolkit.getScreenSize());
+        }
 
-        Toolkit toolkit = Toolkit.getDefaultToolkit();
-        Rectangle screenBounds;
+        // Calculate the screen size that popup should fit
+        Dimension popupSize = JPopupMenu.this.getPreferredSize();
+        int popupRightX = popupLocation.x + popupSize.width;
+        int popupBottomY = popupLocation.y + popupSize.height;
+        int scrWidth = scrBounds.width;
+        int scrHeight = scrBounds.height;
+        if (!canPopupOverlapTaskBar()) {
+            // Insets include the task bar. Take them into account.
+            Insets scrInsets = toolkit.getScreenInsets(gc);
+            scrBounds.x += scrInsets.left;
+            scrBounds.y += scrInsets.top;
+            scrWidth -= scrInsets.left + scrInsets.right;
+            scrHeight -= scrInsets.top + scrInsets.bottom;
+        }
+        int scrRightX = scrBounds.x + scrWidth;
+        int scrBottomY = scrBounds.y + scrHeight;
+
+        // Ensure that popup menu fits the screen
+        if (popupRightX > scrRightX) {
+            popupLocation.x = scrRightX - popupSize.width;
+            if( popupLocation.x < scrBounds.x ) {
+                popupLocation.x = scrBounds.x ;
+            }
+        }
+        if (popupBottomY > scrBottomY) {
+            popupLocation.y = scrBottomY - popupSize.height;
+            if( popupLocation.y < scrBounds.y ) {
+                popupLocation.y = scrBounds.y;
+            }
+        }
+
+        return popupLocation;
+    }
+
+    /**
+     * Tries to find GraphicsConfiguration
+     * that contains the mouse cursor position.
+     * Can return null.
+     */
+    private GraphicsConfiguration getCurrentGraphicsConfiguration(
+            Point popupLocation) {
         GraphicsConfiguration gc = null;
-        // Try to find GraphicsConfiguration, that includes mouse
-        // pointer position
         GraphicsEnvironment ge =
             GraphicsEnvironment.getLocalGraphicsEnvironment();
         GraphicsDevice[] gd = ge.getScreenDevices();
@@ -338,50 +389,36 @@
             if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
                 GraphicsConfiguration dgc =
                     gd[i].getDefaultConfiguration();
-                if(dgc.getBounds().contains(p)) {
+                if(dgc.getBounds().contains(popupLocation)) {
                     gc = dgc;
                     break;
                 }
             }
         }
-
         // If not found and we have invoker, ask invoker about his gc
         if(gc == null && getInvoker() != null) {
             gc = getInvoker().getGraphicsConfiguration();
         }
-
-        if(gc != null) {
-            // If we have GraphicsConfiguration use it to get
-            // screen bounds
-            screenBounds = gc.getBounds();
-        } else {
-            // If we don't have GraphicsConfiguration use primary screen
-            screenBounds = new Rectangle(toolkit.getScreenSize());
-        }
-
-        Dimension size;
-
-        size = JPopupMenu.this.getPreferredSize();
+        return gc;
+    }
 
-        // Use long variables to prevent overflow
-        long pw = (long) p.x + (long) size.width;
-        long ph = (long) p.y + (long) size.height;
-
-        if( pw > screenBounds.x + screenBounds.width )
-             p.x = screenBounds.x + screenBounds.width - size.width;
-
-        if( ph > screenBounds.y + screenBounds.height)
-             p.y = screenBounds.y + screenBounds.height - size.height;
-
-        /* Change is made to the desired (X,Y) values, when the
-           PopupMenu is too tall OR too wide for the screen
-        */
-        if( p.x < screenBounds.x )
-            p.x = screenBounds.x ;
-        if( p.y < screenBounds.y )
-            p.y = screenBounds.y;
-
-        return p;
+    /**
+     * Checks that there are enough security permissions
+     * to make popup "always on top", which allows to show it above the task bar.
+     */
+    static boolean canPopupOverlapTaskBar() {
+        boolean result = true;
+        try {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(
+                        SecurityConstants.SET_WINDOW_ALWAYS_ON_TOP_PERMISSION);
+            }
+        } catch (SecurityException se) {
+            // There is no permission to show popups over the task bar
+            result = false;
+        }
+        return result;
     }
 
 
--- a/jdk/src/share/classes/javax/swing/PopupFactory.java	Fri May 23 20:14:20 2008 +0400
+++ b/jdk/src/share/classes/javax/swing/PopupFactory.java	Mon May 26 17:58:09 2008 +0400
@@ -548,47 +548,46 @@
         }
 
         /**
-         * Returns true if the Popup can fit on the screen.
+         * Returns true if popup can fit the screen and the owner's top parent.
+         * It determines can popup be lightweight or mediumweight.
          */
         boolean fitsOnScreen() {
+            boolean result = false;
             Component component = getComponent();
-
             if (owner != null && component != null) {
-                Container parent;
-                int width = component.getWidth();
-                int height = component.getHeight();
-                for(parent = owner.getParent(); parent != null ;
-                    parent = parent.getParent()) {
-                    if (parent instanceof JFrame ||
-                        parent instanceof JDialog ||
-                        parent instanceof JWindow) {
+                Container parent = (Container) SwingUtilities.getRoot(owner);
+                int popupWidth = component.getWidth();
+                int popupHeight = component.getHeight();
+                Rectangle parentBounds = parent.getBounds();
+                if (parent instanceof JFrame ||
+                    parent instanceof JDialog ||
+                    parent instanceof JWindow) {
+
+                    Insets i = parent.getInsets();
+                    parentBounds.x += i.left;
+                    parentBounds.y += i.top;
+                    parentBounds.width -= i.left + i.right;
+                    parentBounds.height -= i.top + i.bottom;
 
-                        Rectangle r = parent.getBounds();
-                        Insets i = parent.getInsets();
-                        r.x += i.left;
-                        r.y += i.top;
-                        r.width -= (i.left + i.right);
-                        r.height -= (i.top + i.bottom);
-
-                        GraphicsConfiguration gc = parent.getGraphicsConfiguration();
+                    if (JPopupMenu.canPopupOverlapTaskBar()) {
+                        GraphicsConfiguration gc =
+                                parent.getGraphicsConfiguration();
                         Rectangle popupArea = getContainerPopupArea(gc);
-                        return r.intersection(popupArea).contains(x, y, width, height);
-
-                    } else if (parent instanceof JApplet) {
-                        Rectangle r = parent.getBounds();
-                        Point p  = parent.getLocationOnScreen();
-
-                        r.x = p.x;
-                        r.y = p.y;
-                        return r.contains(x, y, width, height);
-                    } else if (parent instanceof Window ||
-                               parent instanceof Applet) {
-                        // No suitable swing component found
-                        break;
+                        result = parentBounds.intersection(popupArea)
+                                .contains(x, y, popupWidth, popupHeight);
+                    } else {
+                        result = parentBounds
+                                .contains(x, y, popupWidth, popupHeight);
                     }
+                } else if (parent instanceof JApplet) {
+                    Point p = parent.getLocationOnScreen();
+                    parentBounds.x = p.x;
+                    parentBounds.y = p.y;
+                    result = parentBounds
+                            .contains(x, y, popupWidth, popupHeight);
                 }
             }
-            return false;
+            return result;
         }
 
         Rectangle getContainerPopupArea(GraphicsConfiguration gc) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JPopupMenu/6694823/bug6694823.java	Mon May 26 17:58:09 2008 +0400
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6694823
+ * @summary Checks that popup menu cannot be partially hidden
+ * by the task bar in applets.
+ * @author Mikhail Lapshin
+ * @run main bug6694823
+ */
+
+import javax.swing.*;
+import java.awt.*;
+import sun.awt.SunToolkit;
+
+public class bug6694823 {
+    private static JFrame frame;
+    private static JPopupMenu popup;
+    private static SunToolkit toolkit;
+    private static Insets screenInsets;
+
+    public static void main(String[] args) throws Exception {
+        toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
+        SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                createGui();
+            }
+        });
+
+        // Get screen insets
+        screenInsets = toolkit.getScreenInsets(frame.getGraphicsConfiguration());
+        if (screenInsets.bottom == 0) {
+            // This test is only for configurations with taskbar on the bottom
+            return;
+        }
+
+        // Show popup as if from a standalone application
+        // The popup should be able to overlap the task bar
+        showPopup(false);
+
+        // Emulate applet security restrictions
+        toolkit.realSync();
+        System.setSecurityManager(new SecurityManager());
+
+        // Show popup as if from an applet
+        // The popup shouldn't overlap the task bar. It should be shifted up.
+        showPopup(true);
+
+        toolkit.realSync();
+        System.out.println("Test passed!");
+        frame.dispose();
+    }
+
+    private static void createGui() {
+        frame = new JFrame();
+        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+        frame.setUndecorated(true);
+
+        popup = new JPopupMenu("Menu");
+        for (int i = 0; i < 7; i++) {
+            popup.add(new JMenuItem("MenuItem"));
+        }
+        JPanel panel = new JPanel();
+        panel.setComponentPopupMenu(popup);
+        frame.add(panel);
+
+        frame.setSize(200, 200);
+    }
+
+    private static void showPopup(final boolean shouldBeShifted) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                // Place frame just above the task bar
+                Dimension screenSize = toolkit.getScreenSize();
+                frame.setLocation(screenSize.width / 2,
+                        screenSize.height - frame.getHeight() - screenInsets.bottom);
+                frame.setVisible(true);
+
+                // Place popup over the task bar
+                Point frameLoc = frame.getLocationOnScreen();
+                int x = 0;
+                int y = frame.getHeight()
+                        - popup.getPreferredSize().height + screenInsets.bottom;
+                popup.show(frame, x, y);
+
+                if (shouldBeShifted) {
+                    if (popup.getLocationOnScreen()
+                            .equals(new Point(frameLoc.x, frameLoc.y + y))) {
+                        throw new RuntimeException("Popup is not shifted");
+                    }
+                } else {
+                    if (!popup.getLocationOnScreen()
+                            .equals(new Point(frameLoc.x, frameLoc.y + y))) {
+                        throw new RuntimeException("Popup is unexpectedly shifted");
+                    }
+                }
+                popup.setVisible(false);
+            }
+        });
+    }
+}