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
--- 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);
+ }
+ });
+ }
+}