JDK-6877806 : JComboBox: Problem after clicking to open dropdown list
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6u16
  • Priority: P4
  • Status: Resolved
  • Resolution: Won't Fix
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2009-09-01
  • Updated: 2012-11-16
  • Resolved: 2012-11-16
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode, sharing)

Also reproduced problem with Java 1.5.0_20 and 1.5.0_11

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
(None)

A DESCRIPTION OF THE PROBLEM :
I have submitted below a test case of a JDialog with a JComboBox that has a PopupMenuListener.  In the PopupMenuListener's popupMenuWillBecomeVisible method, I open another modal dialog (A JOptionPane in this case to keep it simple).  When the user closes the second dialog, the dropdown list in the JComboBox does not work right.  It cannot be closed, and if you resize the JDialog, the dropdown list does not move with the ComboBox.

We have a similar situation in our company's production application which is impacting an important customer.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the test case ("javac ComboBug.java") -- no classpath or other parameters are needed.

Run the test case (ComboBug.class) -- again just "java ComboBug" does it.

Click LAUNCH

In the JDialog that pops up, click the down arrow on the JComboBox to open the JComboBox's dropdown list.  A JOptionPane will pop up. Click OK.   Note that the JCOmboBox's dropdown cannot be closed and if you resize the JDialog the dropdown does not move with the ComboBox but remains stationary.

Problem occurs with Java 1.6 and 1.5 but the problem does not occur  with 1.4.2.

Note: If you start the test case program with a parameter, e.g., "java ComboBug XXX", then the JOptionPane will not be popped up and no error occurs with Java 1.5 and 1.6 -- so popping up a modal dialog in the popupMenuWillBecomeVisible method is what seems to cause the problem.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected result after clicking OK in the JOptionPane, is to see the dropdown list under the ComboBox, and for it to close when one makes a selection or the JComboBox loses focus.  (This is what the test case does with Java 1.4.2.; it is also what happens if the JOptionPane popup is *not* launched, in Java 1.5 and 1.6 -- see "Steps to Reproduce", above).
ACTUAL -
Java 1.5 and d1.6: After clicking OK in the JOption Pane, the JComboBox's dropdown list cannot be closed and it does not move if you resize the JDialog.

(Java 1.4.2: Works correctly as expected.)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
None for the test case.

In our real application, with Java 1.4.2, after closing the popup launched in popupMenuWillBecomeVisible, the dropdown list initially  shows "stuckl", but as soon as the JComboBox loses focus or you do anything else, like click in the ComboBox itself, the dropdown disappears and then everything works fine.

In our real application with Java 1.5 or 1.6, you do not need to resize the JDialog with the JComboBox to get the dropdown list to "disconnect" from the ComboBox -- all you need to do is to move the JDialog.  Also, in the test case, the dropdown list stays inside the JDialog, and it goes away when you close the JDialog.  In our real application, you can move the JDialog so that the dropdown list is left positioned over some other application or empty screen space, and when you close the JDialog with the JComboBox in our real application the dropdown list does not go away but remains visible: the application catches an IllegalComponentStateException: "component must be showing on the screen to determine its location".

(Our real application is 5meg and over 1500 user classes.  It is very difficult to "extract" test cases from it.  I hope the test case provided is "close enough" to what we see in production that the problem is the same.)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// The problem occurs with the JComboBox when you click the
// widget that opens the dropdown list and thepopup menu listener
// popupMenuWillBecomeVisible method launches a Dialog (here,
// just a JOptionPane).  After
// the second dialog is closed, the dropdown list is
// messed up -- it won't close and if you resize the
// JDialog the JComboBox is in, the dropdown list even
// moves around separate from the JCOmboBox text field.
// Problem occurs with Java 1.5 and 1.6 -- Java 1.4.2
// worked OK.

// If you specify a parameter when you launch the program,
// e.g.    java ComboBug XXX
// then the JOptionPane does not pop up when you
// open the dropdown list in the JComboBox, and
// then there is no problem in Java 1.5 or 1.6.  The
// problem seems to be triggered by launching a
// dialog in the popupMenuWillBecomeVisible method.

// Compile the program (no classpath entries needed).
// Run the class file.  Click LAUNCH.  Click the dropdown
// list opener in the JComboBox in the Dialog that
// comes up.  Click OK in the JOptionPane that
// pops up.  Now see what happens with the JComboBox's
// dropdown list (including when you resize the JDialog).
  

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;

import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;


public class ComboBug extends JApplet {

  public JFrame frame;

  static public String useOP = "";

  public static void main(String[] argv) {
      ComboBug app = new ComboBug();
      if (argv.length > 0) {
          useOP = argv[0];
      }
      System.out.println("ARG0: " + useOP);
      app.init();
  }


  public void init() {


      frame = new JFrame("Click LAUNCH to open JDialog with JComboBox in it");
      frame.setSize(600,400);
      GridBagLayout layout = new GridBagLayout();
      GridBagConstraints gbc = new GridBagConstraints();
      gbc.gridx = 0;
      gbc.gridy = 0;
      gbc.gridwidth = 1;
      gbc.anchor = GridBagConstraints.NORTH;
      gbc.fill = GridBagConstraints.NONE;
      gbc.insets = new Insets( 2,2,2,2 ) ;

      JButton b = new JButton("LAUNCH") ;

      b.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent ae) {
              try {
                DLG dlg = new DLG(frame, useOP);
                dlg.show();
                System.out.println("Showed dialog");
              } catch (Exception e) {
                e.printStackTrace();
              }
           }
      });

      frame.getContentPane().setLayout(layout);
      layout.setConstraints(b, gbc);
      frame.getContentPane().add(b);


      frame.setVisible(true);
      System.out.println("Version 4.08 " + getClass().getName() +
              "  " + System.getProperty( "java.version" ));
  }
}

       

class DLG extends JDialog {

  JPanel   panelx;
  Object[] items = {"ONE", "TWO"};

  JComboBox jsp;
  String useOP;
  JFrame frame;



  public DLG(JFrame frame, String useOP) {
      super(frame, true);
      this.useOP = useOP;
      this.frame=frame;
      Container content = getContentPane();

      panelx = new JPanel();

      jsp = new JComboBox();
      DefaultComboBoxModel mdl1 = new DefaultComboBoxModel (items);
      jsp.setModel(mdl1);
      jsp.setEditable(true);
    
      //PR 8832 - Update the popup list as soon as the menu is about
      // to be shown when the user clicks on the combobox arrow.
      jsp.addPopupMenuListener(new PopupMenuListener() {
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                updatePopUpList();
            }
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            }
            public void popupMenuCanceled(PopupMenuEvent e) {
            }
      });

      GridBagLayout layoutx = new GridBagLayout();
      GridBagConstraints gbcx = new GridBagConstraints();
      gbcx.gridx = 0;
      gbcx.gridy = 0;
      gbcx.gridwidth = 1;
      gbcx.anchor = GridBagConstraints.NORTH;
      gbcx.fill = GridBagConstraints.NONE;
      gbcx.insets = new Insets( 2,2,2,2 ) ;

      JButton bb = new JButton("CLOSE") ;

      bb.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent ae) {
                dispose();
           }
      });

      panelx.setLayout(layoutx);
      layoutx.setConstraints(jsp, gbcx);
      panelx.add(jsp);

      gbcx.gridy = 1;
      gbcx.insets = new Insets( 82,2,2,2 ) ;
      gbcx.anchor = GridBagConstraints.SOUTH;
      layoutx.setConstraints(bb, gbcx);
      panelx.add(bb);

      content.add(panelx);
      //pack();

      setSize(600,400);
      //setResizable(false);
        
  }

  public void updatePopUpList() {
          if (useOP.length() == 0) {
             JOptionPane.showMessageDialog(frame,
                 "Important thing is that there is a popup or not");
          }
          System.out.println("popupMenuWillBecomeVisible invoked");
          //DefaultComboBoxModel mdl2 = new DefaultComboBoxModel (items);
          //jsp.setModel(mdl2);

  }

}





---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
No workaround, however, in our production application the user does have a different way of accomplishing the function that is not working here, but this alternative is inconvenient and the customer is complaining.

Release Regression From : 1.4.2_17
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
it is not feasible to fix it without breaking existing applications, closed as will not fix
16-11-2012

EVALUATION The modal dialog blocks JPopupMenu in the middle of setVisible() method and it got stuck in the invalid state. Showing modal dialogs from the Swing listeners has always been a headache for us, we should have specified the fact that modal dialogs are allowed to be shown in a listener *only* with help of SwingUtilities.invokeLater(). Wrapping the updatePopUpList() method from the test case does the trick and the popup doesn't go to the invalid state any more. This problem is quite common and addressed by the RFE #4178930, I hope we'll make it for JDK 7. However there is one thing to keep in mind: the test case works in the "expected" way on JDK 1.4 by pure accident, because the popup must be closed when the focus goes to the newly open dialog. In JDK 1.5 an 1.6 the popup support was almost completely rewritten and became much more advanced, there is no way to return it to the JDK 1.4 state. To make an acceptable workaround you need to show the popup again after the dialog was closed and you also need to break the recursion with help of the boolean flag. Please see the workaround in the workaround section of this CR. With the workaround an all those considerations I downgrade this bug.
09-09-2009

WORK AROUND Here the updated PopupMenuListener for the test case from the description: jsp.addPopupMenuListener(new PopupMenuListener() { // boolean flag to break the recursion private boolean flag; public void popupMenuWillBecomeVisible(PopupMenuEvent e) { if (!flag) { flag = true; // it is important to show modal dialogs inside invokeLater() SwingUtilities.invokeLater(new Runnable() { public void run() { updatePopUpList(); // the popup was closed by the dialog, // make it visible again jsp.getUI().setPopupVisible(jsp, true); flag = false; } }); } } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { if (!flag) { unselectArrowButton(); } } public void popupMenuCanceled(PopupMenuEvent e) { if (!flag) { unselectArrowButton(); } } private void unselectArrowButton() { for (int i = 0; i < jsp.getComponentCount(); i++) { Component c = jsp.getComponent(i); if (c instanceof AbstractButton) { AbstractButton button = (AbstractButton) c; button.getModel().setPressed(false); button.getModel().setArmed(false); } } } });
09-09-2009