# HG changeset patch # User mlapshin # Date 1211810289 -14400 # Node ID 4f26ea16c5c1b481f64e8b25b6843474069ad438 # Parent 4867bdd4fa38ccee96435ae64051d5e1d781780f 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 diff -r 4867bdd4fa38 -r 4f26ea16c5c1 jdk/src/share/classes/javax/swing/JPopupMenu.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; } diff -r 4867bdd4fa38 -r 4f26ea16c5c1 jdk/src/share/classes/javax/swing/PopupFactory.java --- 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) { diff -r 4867bdd4fa38 -r 4f26ea16c5c1 jdk/test/javax/swing/JPopupMenu/6694823/bug6694823.java --- /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); + } + }); + } +}