JDK-4235188 : JPopupMenus and JMenus persist when their JFrame becomes visible again.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.1.7,1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 1999-05-04
  • Updated: 2000-09-14
  • Resolved: 2000-09-14
Related Reports
Duplicate :  
Relates :  
Description

Name: skT88420			Date: 05/04/99


There seems to be a problem with JPopupMenus and JMenus when they are contained in a JFrame 
that is set invisible and then becomes visible again.  A sample program is included to 
demonstrate the problem.  Here are steps to see the problem:
1.  click on the login button on the login window
2.  click on the "Close in 5 sec" button (this starts a "thread safe" thread that will close
	the main window in 5 seconds)
3.  before 5 seconds has elapsed, click on the JLable named "Popup Menu" on the left side of 
	the main window so that the popup menu pops up.  
4.  wait 5 seconds 
5.  re-login with the login button  
6.  notice that the popup menu is still there (in fact you can't get rid of it)  
Something similar happens to the JMenus.  To see that replace step 3 above with a click on 
the JMenuBar at the top of the main Window.  You will notice that when you log back in the 
JMenu is still there and won't go away easily.  It will go away if you click on the JMenu 
title again, but clicks off of the JMenu, which normally makes it go away, won't work.

java full version "JDK1.1.7:12/16/1998-19:30"

Here is the sample program code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class PopupMenuTest extends JFrame
   implements ActionListener, MouseListener, Runnable
{
   // Constants
   
   private static final String button1String = "Login";
   private static final String button2String = "Close in 5 sec";
   private static final String menuString1 = "Menu Choice 1";
   private static final String menuString2 = "Menu Choice 2";
   private static final String menuString3 = "Menu Choice 3";
   private static final String menuString4 = "Menu Choice 4";
   private static final String label1String = "Popup Menu";

   // Variables
   
   private JFrame mainWindow;
   
   private JPopupMenu mainPopupMenu;
   
   private JMenu mainMenu;
   private JMenu mainMenu2;
   
   private JMenuBar menuBar;

   private JButton button1;
   private JButton button2;

   private JLabel label1;

   // Constructor
   
   public PopupMenuTest()
   {
      setTitle("Login");
      
      button1 = new JButton(button1String);
      
      getContentPane().add("Center", button1);
      
      button1.addActionListener(this);
      
      mainWindow = new JFrame("Main Window");
      
      mainWindow.setSize(new Dimension(500, 300));
      
      button2 = new JButton(button2String);
      
      mainWindow.getContentPane().add("South", button2);
      
      button2.addActionListener(this);
      
      label1 = new JLabel(label1String);
      
      mainWindow.getContentPane().add("West", label1);
      
      label1.addMouseListener(this);
      
      mainMenu = new JMenu("Main Menu");
      
      mainMenu.add(menuString1);
      mainMenu.add(menuString2);
      mainMenu.add(menuString3);
      mainMenu.add(menuString4);

      mainMenu2 = new JMenu("Menu2");
      
      mainMenu2.add(menuString1);
      mainMenu2.add(menuString2);
      mainMenu2.add(menuString3);
      mainMenu2.add(menuString4);

      menuBar = new JMenuBar();
      mainWindow.setJMenuBar(menuBar); 
      
      menuBar.add(mainMenu); 
      menuBar.add(mainMenu2); 
         
      pack();
      
      WindowAdapter wA = new WindowAdapter()
      {
         public void windowClosing(WindowEvent evt)
         {
            System.exit(0);
         }
      };
      
      addWindowListener(wA);
      mainWindow.addWindowListener(wA);
      
      setVisible(true);
      
   }
   
   // Methods
   
   // create the popup menu
   public void createPopup(Component comp, Point point)
   {
      mainPopupMenu = new JPopupMenu();
      
      mainPopupMenu.add(menuString1);
      mainPopupMenu.add(menuString2);
      mainPopupMenu.add(menuString3);
      mainPopupMenu.add(menuString4);
      
      mainPopupMenu.show(comp, point.x, point.y);
   
   
   }
   
   // ActionListener interface
   public void actionPerformed(ActionEvent evt)
   {
      // login button was pressed
      if(evt.getActionCommand().equals(button1String))
      {
         setVisible(false);
         mainWindow.setVisible(true);
      }
      else if(evt.getActionCommand().equals(button2String))
      {
         Thread tempThread = new Thread(this);

         tempThread.start();
      }
   }
   
   // MouseListener interface
   
   public void mouseClicked(MouseEvent evt)
   {
      createPopup(evt.getComponent(), evt.getPoint());
      
   }
   
   public void mousePressed(MouseEvent evt)
   {}
   
   public void mouseReleased(MouseEvent evt)
   {}
   
   public void mouseEntered(MouseEvent evt)
   {}
   
   public void mouseExited(MouseEvent evt)
   {}
   
   // Runnable interface
   // a seperate thread to set mainwindow to invisible
   public void run()
   {
      try
      {
         Thread.sleep(5000);
      }
      catch(java.lang.InterruptedException e)
      {
      }
      
      // invoke the actions in the event thread for "Thread Safeness"
      SwingUtilities.invokeLater(new Runnable() 
      {
         public void run()
         {
            mainWindow.setVisible(false);
            setVisible(true);
         }
      });

   }
   
   
   public static void main(String args[])
   {
      new PopupMenuTest();
   }
     
}
(Review ID: 57786) 
======================================================================

Comments
EVALUATION The problem was that JPopupMenu was not doing its own visiblity management but instead used isShowing(). When a cancel request came as a result of the invoker's toplevel window being hidden, setVisible believed that the menu was already popped down, and so did nothing. This caused the MenuGrabber to become confused, so that the menu was then 'orphaned'. Fixed in my WS by implementing visibility handling to JPopupMenu which is independant of isShowing(). georges.saab@Eng 1999-06-28 Somehow this fix didn't make it into Kestrel. Still happens in jdk-1.3fcs-M on Solaris 2.7 and Windows 98, 2nd Ed. Georges, maybe you can check and see why this didn't get into Kestrel. nancy.schorr@eng 1999-10-28 Name: pzR10082 Date: 08/24/2000 As pointed out by Georges, the problem is that JPopupMenu.isVisible() returns popup.isShowing(). In our case, when Window is closed, the visible property becomes false without setVisible(false) being called. A subsequent call to setVisible(false) has no effect because isVisible() already returns false. The fix Georges mentions was never putback for some reason. I'll make an attempt to reinvent it. ###@###.### 2000-08-24 ====================================================================== Name: pzR10082 Date: 09/12/2000 This was fixed along with 4303635 (file JPopupMenu.java version 1.153). JPopupMenu.isVisible() now doesn't regard whether popup is showing: if(popup != null) return true; else return false; ###@###.### 2000-09-12 ======================================================================
12-09-2000

SUGGESTED FIX Name: pzR10082 Date: 08/24/2000 A new field is introduced which holds visibility. In setVisible(), it is updated when the popup is shown or hidden. So in normal situation (i.e. when popup visibility is changed with setVisible() calls) isVisible() will return the same value as its old version. But popup visibility can be also changed without calling setVisible(). Obviously popup cannot be shown this way, but it can be hidden if its parent window is closed (as in the test) or if a component in its component hierarchy is hidden (strange case). Both ways setVisible(false) will be called from menuSelectionChanged() method, keeping `visible' field up-to-date. *** C:\TMP\geta217 Fri Aug 18 17:29:54 2000 --- JPopupMenu.java Fri Aug 18 17:26:57 2000 *************** *** 72,77 **** --- 72,78 ---- transient Component invoker; transient Popup popup; transient Frame frame; + transient boolean visible = false; private int desiredLocationX,desiredLocationY; private static PopupFactory popupFactory = new DefaultPopupFactory(); *************** *** 597,608 **** popupSize.height); } popup.show(invoker); ! } else if(popup != null) { firePopupMenuWillBecomeInvisible(); popup.hide(); popup.removeComponent(this); popup = null; } if (accessibleContext != null) { if (b) { --- 598,610 ---- popupSize.height); } popup.show(invoker); ! visible = true; } else if(popup != null) { firePopupMenuWillBecomeInvisible(); popup.hide(); popup.removeComponent(this); popup = null; + visible = false; } if (accessibleContext != null) { if (b) { *************** *** 622,631 **** * being displayed). */ public boolean isVisible() { ! if(popup != null) ! return popup.isShowing(); ! else ! return false; } /** --- 624,630 ---- * being displayed). */ public boolean isVisible() { ! return (popup != null) && visible; } /** ###@###.### 2000-08-24 ======================================================================
24-08-2000