JDK-5036549 : PopupMenu not adjusting its location correctly depending on taskbar's position.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2004-04-23
  • Updated: 2017-05-23
Description
Name: rmT116609			Date: 04/22/2004


FULL PRODUCT VERSION :
java version "1.4.2_03"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_03-b02)
Java HotSpot(TM) Client VM (build 1.4.2_03-b02, mixed mode)

java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
In 1.4 there is a package method in JPopupMenu called adjustPopupLocationToFitScreen() which will adjust the given popup location to account the desktop bounds, taskbar and multi-monitor configuration.

But the implementation failed to consider the screenInsets properly.
If the taskBar is present at the top or at the left of the screen the PopupMenu still goes inside the taskBar.

The current implementation is not adjusting the screenBounds accordingly.

Current Implementation:
---------------------------------
Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
	Point p = new Point(xposition, yposition);

        if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
            return p;

        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Rectangle screenBounds;
        Insets screenInsets;
        GraphicsConfiguration gc = null;
        // Try to find GraphicsConfiguration, that includes mouse
        // pointer position
        GraphicsEnvironment ge =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] gd = ge.getScreenDevices();
        for(int i = 0; i < gd.length; i++) {
            if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
                GraphicsConfiguration dgc =
                    gd[i].getDefaultConfiguration();
                if(dgc.getBounds().contains(p)) {
                    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 and insets
            screenInsets = toolkit.getScreenInsets(gc);
            screenBounds = gc.getBounds();
        } else {
            // If we don't have GraphicsConfiguration use primary screen
            // and empty insets
            screenInsets = new Insets(0, 0, 0, 0);
            screenBounds = new Rectangle(toolkit.getScreenSize());
        }

        int scrWidth = screenBounds.width -
                    Math.abs(screenInsets.left+screenInsets.right);
        int scrHeight = screenBounds.height -
                    Math.abs(screenInsets.top+screenInsets.bottom);

        Dimension size;

        size = JPopupMenu.this.getPreferredSize();

        if( (p.x + size.width) > screenBounds.x + scrWidth )
             p.x = screenBounds.x + scrWidth - size.width;

        if( (p.y + size.height) > screenBounds.y + scrHeight)
             p.y = screenBounds.y + scrHeight - 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;
    }



Correct implementation should be:-
Correct Implementation:
--------------------------------
Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
	Point p = new Point(xposition, yposition);

        if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
            return p;

        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Rectangle screenBounds;
        Insets screenInsets;
        GraphicsConfiguration gc = null;
        // Try to find GraphicsConfiguration, that includes mouse
        // pointer position
        GraphicsEnvironment ge =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] gd = ge.getScreenDevices();
        for(int i = 0; i < gd.length; i++) {
            if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
                GraphicsConfiguration dgc =
                    gd[i].getDefaultConfiguration();
                if(dgc.getBounds().contains(p)) {
                    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 and insets
            screenInsets = toolkit.getScreenInsets(gc);
            screenBounds = gc.getBounds();
        } else {
            // If we don't have GraphicsConfiguration use primary screen
            // and empty insets
            screenInsets = new Insets(0, 0, 0, 0);
            screenBounds = new Rectangle(toolkit.getScreenSize());
        }

        int scrWidth = screenBounds.width -
                    Math.abs(screenInsets.left+screenInsets.right);
        int scrHeight = screenBounds.height -
                    Math.abs(screenInsets.top+screenInsets.bottom);

/********************************************************/
/***********************Modification********************/
screenBounds.x += screenInsets.left;
screenBounds.y += screenInsets.top;
/********************************************************/
/***********************Modification********************/

        Dimension size;

        size = JPopupMenu.this.getPreferredSize();

        if( (p.x + size.width) > screenBounds.x + scrWidth )
             p.x = screenBounds.x + scrWidth - size.width;

        if( (p.y + size.height) > screenBounds.y + scrHeight)
             p.y = screenBounds.y + scrHeight - 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;
    }


I tested with latest JDK's 1.4.1 and  1.4.2.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Place the taskBar at the top or at the left and show any Javax.swing.JPopupMenu (run the given testcase) whose size is greater than the screen. You will notice that some portion of the PopupMenu still hides inside the taskbar.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The PopupMenu shouldn't hide inside the taskbar. It should be positioned below the taskBar.
ACTUAL -
The PopupMenu goes inside the taskbar.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

public class TabPaneTest extends JPanel
{
  public TabPaneTest()
  {
    final JButton popButton = new JButton("Show PopupMenu");
    this.add(popButton);

    final JPopupMenu pop = new JPopupMenu("Hai");
    for (int i = 0; i < 50; i++)
    {
      pop.add(new JMenuItem(new String("Item"+(i+1))));
    }

    popButton.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent ae)
      {
        pop.show(popButton, 0, 0);
      }
    });
  }

  public static void main(String[] args)
  {
    JFrame fr = new JFrame("TabPaneTest");
    fr.getContentPane().add(new TabPaneTest());
    fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    fr.pack();
    fr.setVisible(true);
  }

  private JPopupMenu _pop;
}
---------- END SOURCE ----------
(Incident Review ID: 230658) 
======================================================================

Comments
EVALUATION ###@###.###, you have some history working on these issues with ###@###.###. Would you mind owning this one? Thanks! ###@###.### 2004-04-23 Name: pzR10082 Date: 04/26/2004 JPopupMenu.adjustPopupLocationToFitScreen() works with screen insets incorrectly sometimes. It assumes taskbar at the bottom when it is at the top etc. ###@###.### ======================================================================
2004-09-25