Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4234793
Votes 130
Synopsis PopupMenuListener popupMenuCanceled is never called
Category java:classes_swing
Reported Against 1.2 , 1.1.7 , 1.2.2 , swing1.0.2
Release Fixed 1.4.2(mantis)
State 10-Fix Delivered, bug
Priority: 4-Low
Related Bugs 4242595 , 4260392 , 4146601
Submit Date 03-MAY-1999
Description




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

class PopupBug extends JFrame
{
    public static void main(String[] args)
    {
        PopupBug a = new PopupBug();
        a.setSize(600, 300);
        a.addWindowListener(
            new WindowAdapter()
            {
                public void windowClosing(WindowEvent evt)
                {
                    System.exit(0);
                }
            }
        );
        a.setVisible(true);
    }
    
    class foo extends AbstractAction
    {
        foo()
        {
            super("foo");
        }
        public void actionPerformed(ActionEvent evt) 
        {
            text.append("action fired\n");
        }
    }
        
    private JTextArea text;
    PopupBug()
    {
        text = new JTextArea();
        Container content = getContentPane();
        content.setLayout(new BorderLayout());
        content.add(new JScrollPane(text), BorderLayout.CENTER);
        JTextArea desc = new JTextArea();
        desc.append("Right Click to display popup menu in text area\n");
        desc.append("Cancel menu by clicking outside menu\n");
        desc.append("Note that *popupMenuwillBecomeInvisible* is called instead of *popupMenuCanceled*");
        content.add(desc, BorderLayout.SOUTH);
        text.addMouseListener(new PopupMouseAdapter());
    }
        
    class PopupMouseAdapter extends MouseAdapter
    {
        private void checkPopup(MouseEvent evt)
        {            
            if (evt.isPopupTrigger())
            {
                int x = evt.getX();
                int y = evt.getY();
                PopupMenuListener ml = new PopupMenuListener()
                {
                    public void popupMenuWillBecomeVisible(PopupMenuEvent e)
                    {
                    }
                    public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
                    {
                        text.append("in popupMenuWillBecomeInvisible\n");
                    }
                    public void popupMenuCanceled(PopupMenuEvent e)
                    {
                        text.append("in popupMenuCanceled\n");
                    }
                };
                JPopupMenu m = new JPopupMenu();
                //m.setLightWeightPopupEnabled(false);
                m.addPopupMenuListener(ml);                        
                m.add(new foo());                
                m.show((Component) evt.getSource(), x, y); 
            }
        }
        public void mousePressed(MouseEvent evt)
        {
            checkPopup(evt);
        }
        public void mouseReleased(MouseEvent evt)
        {
            checkPopup(evt);
        }
        public void mouseClicked(MouseEvent evt)
        {
            checkPopup(evt);
        }
    }
}
(Review ID: 57353) 
======================================================================
Work Around
N/A
Evaluation
The problem is that the BasicPopupMenuUI doens't call JPopupMenu.firePopupMenuCancelled. Previously, the cancelPopupMenu() code and logic to cancel the Popup was in JPopupMenu but was moved into BasicPopupMenuUI. Since firePopupMenuCancelled() is protected, BasicPopupMenuUI has no way of calling this method thereby sending out the popupMenuCancelled() notification.

The solution is to make the firePopupMenuCancelled() method public or add a public or package private method which will call the protected firePopupMenuCancelled() method.

  xxxxx@xxxxx   2000-03-14


This looks trickier than I thought. The code was refactored so that the MouseGrabber was taken out of JPopupMenu and put into BasicPopupMenuUI. The MouseGrabber.cancelPopupMenu() method should call the JPopupMenu.firePopupMenuCancelled(). This should be done after the statement lastGrapped==getFirstPopup. I'm not too keen on this since it looks a bit messy and I'll have to change firePopupMenuCanceled() from protected to public - which is inconsistent with the other JPopupMenu methods. Still thinking about it.

  xxxxx@xxxxx   2000-03-28

I tried to add the firePopupMenuCancelled() call in MouseGrabber.cancelPopupMenu() and found that it worked with the current code. However, it's still a hack and not very resiliant to change in complementary classes. It NPE'd when I was refactoring JComboBox. I'm going to abandon this tack and a more drastic change should be thought out. Pehaps with the instroduction of a new interface.
  xxxxx@xxxxx   2000-04-03

Now that 1.4 and 1.4.1 are out the door and stable, I revisited making JPopupMenu.firePopupMenuCanceled() public and called from the MouseGrabber and the CancelAction (ESCAPE key). This seems to work correctly. The semantics should be that the PopupMenuCanceled event should be before the PopupMenuWillBecomeInvisible event. Also, should ensure that the JComboBox also sends the canceled event when the ESCAPE key is pressed. The diffs are in the suggested fix section.

Unfortunately, we cannot make an API change until 1.5 so I'm going to try another approach by using a client property.


  xxxxx@xxxxx   2002-09-04
Comments
  
  Include a link with my name & email   

Submitted On 13-OCT-1999
bfinkelman
what i want to know is, how this can be worked around.  In the related bug, the
4242595,
the person who submitted it claims that they have a work-around by having an
implementation of
 popupMenuWillBecomeInvisible() that checks for a selected MenuItem.  I've
tried JMenuItem-->isSelected() and
JPopupMenu>>getSelectionModel()>>isSelected() and neither one ever
returns true.  I've also tried adding my popup menu as
an action listener to each menu item and if an item is selected, I set a
private inst var to true, hoping that when popupMenuWillBecomeInvisible() 
is invoked, I can tell whether or not something has been selected.  This also
doesn't work because the PopupMenuEvent is
triggered before the ActionEvent.  If there is no way to work around this bug,
I have a memory leak problem.  My project has project-specific
implementations of JPopupMenu, JMenuItem, and JPopupSeparator that aren't
getting garbage collected when the popup
menu is cancelled.  Every field in this huge application triggers a popup menu
so this is a potentially large memory leak.  Is anyone at Sun even
going to evaluate this bug since it definitely seems like a problem on their
side and not something developers should have to 
work around!


Submitted On 20-DEC-1999
AtleWilhelmsen
You could make a workaround by using some sort of timer and listen for all the
popup menu action events.
If no action events has been sent and 1 secound
has elapsed since the popupMenuWillBecomeInvisible event was sent then
is a 99% probability that the popup menu was
canceled.


Submitted On 28-APR-2000
bradmcc
Our application uses multiple java windows with
Jtrees and JTables.  When the
user switches between consoles, we are missing not only
popupMenuCanceled notifications, but we are also
missing popupMenuWillBecomeInvisible notifications!


Submitted On 16-NOV-2000
steven.cox
This bug is also present in 1.3.


Submitted On 28-FEB-2001
kwagen
I have tried this workaround and it seems to work (at least with 1.3):

public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
{
  EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
  AWTEvent event = queue.peekEvent();
  boolean canceled = false;
  if (event == null)
    canceled = true;
  else
  {
    if (event instanceof InvocationEvent)
      canceled = true;
    else if (event instanceof KeyEvent)
    {
      if (((KeyEvent)event).getKeyChar() == 27)
        canceled = true;
    }
  }

  System.out.println("##### Popup menu has been canceled: " + canceled);
}


Submitted On 23-JUN-2001
dbernath
Two years and running on this defect.  Is there a year in 
which this will be fixed, potentially in a release that 
application developers can use (like 1.3.x) or will this 
eventually get closed out as fixed in release (insert 
codename here).


Submitted On 05-OCT-2001
StrickyShooter
These bug is still 2 years old and sun doesn't fixed it, 
what's up? Sun knows the bug why to hell they don't fix it.


Submitted On 30-NOV-2001
lzwcomp
Sun's too busy trying to crank out the next new and 
greatest version of the JDK rather than support the code 
which is out in the field which developers have to use to 
produce real products for real customers.

IS ANYONE AT SUN LISTENING!  FIX THE DAMN BUGS IN THE 
CURRENT JDK AND DON'T PAWN OFF THE PROBLEM AS FIXED IN SOME 
FUTURE RELEASE!


Submitted On 23-APR-2002
DarioDariol
3 years old!
What we are waiting for?


Submitted On 08-DEC-2002
frkane
Three and a half year old (jdk1.4.1_01) and still annoying 
everyone! I think, since its claimed as to be fixed noone at 
Sun will ever look at this issue again.


Submitted On 06-JUN-2003
inf-collin
It has been fixed in 1.4.2 beta ! 
yeeeeppeeee ! 


Submitted On 03-SEP-2003
ronnie9
This bug is not completely fixed as of JDK 1.4.2_01, but it’s 
much better than before… at least popupMenuCanceled() 
gets called most of the time. 

There is still one rather large problem. If the popupMenu us 
canceled due to clicking on another JcomboBox then 
popupMenuCanceled() does not get called - only the call to 
popupMenuWillBecomeInvisible() gets made. I’ve done some 
testing, and it only seems to be a problem if you click on a 
different JComboBox. Clicking on, say, a JCheckBox will result 
in  popupMenuCanceled() getting called, as it should be.

Unfortunately, this is still a big problem if popupMenuCanceled
() is not always called when the popupMenu gets cancelled, 
so this bug should not be closed until this mechanism works 
ALL of the time!



PLEASE NOTE: JDK6 is formerly known as Project Mustang