-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
1.4.0
-
x86
-
windows_xp
Name: jk109818 Date: 05/16/2002
FULL PRODUCT VERSION :
java version "1.4.0-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-rc-b91)
Java HotSpot(TM) Client VM (build 1.4.0-rc-b91, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [Version 5.1.2600]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Need two or more video cards/two monitors configured in a
virtual desktop configuration.
A DESCRIPTION OF THE PROBLEM :
The fix for bug #4425878 introduced a new Java runtime
propery
-Djavax.swing.adjustPopupLocationToFit which when set to
true will cause JPopupMenu to respect the screen's size,
adjusting the desired (x,y) screen coordinates so that the
popup will fit the current screen. The code that does this
adjustment is in the package private
adjustPopuLocationToFitScreen() method in JPopupMenu.
Unfortunately, the code does not properly handle systems
with multiple displays. When the popup is to appear on a
secondary screen the popup will always be forced to fit
onto the edge of the primary screen because the code
thinks the popup is offscreen.
The method uses Toolkit.getDefaultToolkit().getScreenSize
() which returns the screen dimensions of the primary
display adapter as expected. What the method should do is
determine the java.awt.GraphicsConfiguration where the
popup desired location is in and then using
GraphicsConfiguration.getBounds() to obtain the virtual
(x,y) position of the screen and its dimensions. The popup
should then be adjusted to fit within this virtual screen
space.
Furthermore, the adjustPopuLocationToFitScreen() should
also be corrected to respect the current screen's insets
(space taken up by Windows taskbar for instance) using
Toolkit.getDefaultToolkit().getScreenInsets
(GraphicsConfiguration).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Ensure you are running a system that has two monitors
configured as one single large virtual desktop.
Compile and run the test program:
java -Djavax.swing.adjustPopupLocationToFit=true Test
Move the frame onto a non-primary display/screen and right-
click to show the popup. Notice the popup is positioned
onto the primary display which is incorrect.
EXPECTED VERSUS ACTUAL BEHAVIOR :
Popup requested from a non-primary display/screen should
popup on the same screen.
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class Test extends JFrame
{
public static void main(String[] args)
{
new Test();
}
public Test()
{
super("Test");
installComponents((JPanel) getContentPane());
setBounds(100,100,200,200);
show();
}
protected void installComponents(JPanel cp)
{
popup = new JPopupMenu();
popup.add(new JMenuItem("One"));
popup.add(new JMenuItem("Two"));
popup.add(new JMenuItem("Three"));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopup(e.getPoint());
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopup(e.getPoint());
}
}
});
}
protected void showPopup(Point pt)
{
popup.show(Test.this, pt.x, pt.y);
}
JPopupMenu popup;
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
Source code we currently use to workaround this problem:
/**
* Given the upper-left corner point of where the popup
wants to be displayed
* and the popup's dimensions, adjust the popup upper-left
corner such that
* the popu can be displayed completely in the current
screen. <p>
*
* Method properly handles multiple monitors acting as a
virtual display and
* will properly respect screen's insets (space taken up
by a taskbar for instance)
* if any. <p>
*
* The popup's current screen (when there is more than
one) is identified by the
* screen where the supplied upper-left point is in. <p>
*
* {@link PopupFactory#getPopup} does not adjust the popup
location to fit the
* current screen which is why this method is required.
*
* @param x
* The x-coord of upper-left corner (in screen
coordinates) where the popup
* wants to be displayed at.
* @param y
* The y-coordinate of upper-left corner (in
screen space) where the popup
* wants to be displayed at.
* @param width
* The width of the popup
* @param height
* The height of the popup
* @param alignRight
* When popup extends beyond right-edge of the
current screen and this
* value is false, the popup will be positioned
against the right-edge
* of the screen; if false, popup will pop
leftwards.
* @param alignBottom
* When popup extends beyond bottom of screen and
this value is true,
* the popup will be positioned against the
bottom-edge of the current
* screen; if false, the popup will pop upwards.
* @return the adjusted popup upper-left location (in
screen coordinates)
*
* @see PopupFactory#getPopup
*/
public static Point adjustPopupLocationToFitScreen(int x,
int y,
int
width, int height,
boolean
alignRight, boolean alignBottom)
{
GraphicsConfiguration gc = getScreenAt(x, y);
if (gc == null) {
gc = getPrimaryGraphicsConfiguration();
}
Rectangle screen = gc.getBounds();
// it has been observed that insets may sometimes be
negative which is why we take abs
Insets insets = Toolkit.getDefaultToolkit
().getScreenInsets(gc);
int screenTop = screen.y + Math.abs(insets.top);
int screenLeft = screen.x + Math.abs(insets.left);
int screenBottom = screen.y + screen.height - Math.abs
(insets.bottom);
int screenRight = screen.x + screen.width - Math.abs
(insets.right);
if (x + width > screenRight) {
if (alignRight || (x >= screenRight)) {
x = screenRight - width;
} else {
x -= width;
}
}
if (y + height > screenBottom) {
if (alignBottom || (y >= screenBottom)) {
y = screenBottom - height;
} else {
y -= height;
}
}
if (x < screenLeft) {
x = screenLeft;
}
if (y < screenTop) {
y = screenTop;
}
return new Point(x,y);
}
/**
* Retrieves the GraphicsConfiguration (which identifies
the screen) where the
* given (x,y) screen coordiantes are located in. If (x,y)
is outside all screens,
* null will be returned.
*/
public static GraphicsConfiguration getScreenAt(int x, int
y)
{
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] screens = env.getScreenDevices();
for (int i = 0; i < screens.length; i++) {
GraphicsConfiguration config = screens
[i].getDefaultConfiguration();
Rectangle bounds = config.getBounds();
if (bounds.contains(x, y)) {
return config;
}
}
return null;
}
/**
* Returns the GraphicsConfiguration of the primary
display adapter (default
* screen).
*
* @post return != null
*/
public static GraphicsConfiguration
getPrimaryGraphicsConfiguration()
{
return GraphicsEnvironment.getLocalGraphicsEnvironment
()
.getDefaultScreenDevice()
.getDefaultConfiguration();
}
(Review ID: 139325)
======================================================================
- duplicates
-
JDK-4245587 JPopupMenus are obscured by windows taskbar
- Closed