United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4743225 Size of JComboBox list is wrong when list is populated via PopupMenuListener
JDK-4743225 : Size of JComboBox list is wrong when list is populated via PopupMenuListener

Details
Type:
Bug
Submit Date:
2002-09-06
Status:
Closed
Updated Date:
2010-10-26
Project Name:
JDK
Resolved Date:
2011-03-08
Component:
client-libs
OS:
linux,windows_xp
Sub-Component:
javax.swing
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.1,6,6u3,6u10,6u20
Fixed Versions:

Related Reports
Backport:
Backport:
Backport:
Duplicate:
Duplicate:
Duplicate:
Relates:
Relates:
Relates:

Sub Tasks

Description
Name: jk109818			Date: 09/06/2002


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

FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
I'm exploiting the new capability in JDK 1.4 to add a
PopupMenuListener to a JComboBox to populate the list just
before it is shown.  When I click on the combo box to open
the popup list, the contents of the popup list looks fine,
but the size of the list is wrong.  The size of the list is
apparently calculated from the previous number of entries in
the combo box model.  It should use the new count of entries
instead.  To get the popup list to size correctly, you must
close the popup list and open it again.

I reported this problem once before on 6/17/2002.  It was
assigned an internal review ID of 153748.  But I haven't
seen any further progress, and the bug doesn't show up in
the bug database, so I'm submitting it again.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the provided demonstration program.
2. Click on the arrow to open the popup list for the combo box.
3. Observe that the list is sized to show three items, which
is the OLD size of the list.
4. Close the popup list and open it again to observe that
the list is now sized for 8 items, the default maximum list
size.

EXPECTED VERSUS ACTUAL BEHAVIOR :
When opening the combo box, it is sized for 3 items, but the
list really contains 10 items.  It should size the list for
8 items, the default maximum list size.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.event.*;

class ComboBug extends AbstractListModel implements ComboBoxModel,
PopupMenuListener {
   JFrame f;
   Object selected;
   Object[] items;

   public static void main(String[] args) {
      new ComboBug();
   }

   ComboBug() {
      f = new JFrame("Combo demo");
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      items = new Object[] {"First", "Second", "Third"};
      setSelectedItem(items[0]);

      JComboBox combo = new JComboBox(this);
      combo.addPopupMenuListener(this);
      f.getContentPane().add(combo, BorderLayout.CENTER);
      f.pack();
      f.show();
   }

   public int getSize() {
      return items.length;
   }

   public Object getElementAt(int index) {
      return items[index];
   }

   public void setSelectedItem(Object obj) {
      selected = obj;
      fireContentsChanged(this, -1, -1); // Same as what DefaultComboBoxModel fires
   }

   public Object getSelectedItem() {
      return selected;
   }

   public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      int oldSize = items.length;
      items = new Object[] {"One", "Two", "Three", "Four", "Five", "Six",
"Seven", "Eight", "Nine", "Ten"};
      fireIntervalRemoved(this, 0, oldSize - 1);
      fireIntervalAdded(this, 0, items.length - 1);
      setSelectedItem(items[0]);
   }

   public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
   public void popupMenuCanceled(PopupMenuEvent e) {}
}

---------- END SOURCE ----------
(Review ID: 164152) 
======================================================================

                                    

Comments
EVALUATION

The fundamental problem is that the size of the popup portion of the combo box has been computed by the time the firePopupMenuWillBecomeVisible. The sequence of events for computing the bounds and showing the popup is as follows:

   BasicComboPopup.show() 
   -> getPopupLocation - Compute the size of the list (component added to popup)
     ->getPopupHeightForRowCount()  - Height of popup made here.
   ->JPopupMenu.show(...);
     ->setVisible(true);
        ->firePopupMenuWillBecomeVisible() - Notification of popup.
        ->getPopup()
           ->adjustPopupLocationToFitScreen
              ->size = JPopupMenu.this.getPreferredSize();

The solution could be to create a custom subclass of JScrollBar for BasicComboPopup which would implement the getPreferredSize method to compute the preferred size base on the getPopupHeightForRowCount().

This would be a good idea since size calcultions should be done in getPreferredSize, etc... as much as possible.

###@###.### 2002-09-06
                                     
2002-09-06
SUGGESTED FIX

------- BasicComboPopup.java -------
512,514c512
<       return new JScrollPane( list,
<                               ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
<                               ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
---
>       return new PopupScrollPane();
516a515,537
>     // Fix for 4743225
>     class PopupScrollPane extends JScrollPane {
>
>       public PopupScrollPane() {
>           super(list, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
>                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
>       }
>
>       public Dimension getPreferredSize() {
>           Dimension popupSize = comboBox.getSize();
>           Insets insets = getInsets();
>
>           // reduce the width of the scrollpane by the insets so that the popup
>           // is the same width as the combo box.
>           popupSize.setSize(popupSize.width - (insets.right + insets.left),
>                             getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
>           Rectangle popupBounds = computePopupBounds( 0, comboBox.getBounds().height,
>                                                       popupSize.width, popupSize.height);
>           return popupBounds.getSize();
>       }
>     }
>
>
1091d1111
<       Dimension scrollSize = popupBounds.getSize();
1093a1114,1115
>       /*
>       Dimension scrollSize = popupBounds.getSize();
1096c1118
<       scroller.setMinimumSize( scrollSize );
---
>       scroller.setMinimumSize( scrollSize ); */
                                     
2004-10-02
WORK AROUND

Subclass BasicComboPopup and implement createScroller() to return a custom JScrollPane subclass that implements getPreferredSize. Not an easy solution. 
                                     
2004-10-02
EVALUATION

There are two fixes offered
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4743225

I tried one Submitted On 18-FEB-2006 
it doesn't fix the test case in description

The fix Submitted On 25-MAR-2004
fixes it, but it is a real hack
it just quickly shows and hides the combobox's popup 
first it might lead to popup blinking 
second it uses awt.Robot which is designed for regression tests only

ComboBox popup doesn't expect to be updated in popupMenuWillBecomeVisible() method,
before showing on the screen we calculate the popup size and location first 
then we call popupMenuWillBecomeVisible() and PopupMenuListener's 
and then we show the popup 

actually we should have documented this behaviour when PopupMenuListener appears

The complete design is difficult and risky to be changed,
I don't think we can change in backward compatible manner now

We'll back to this issue after Mustang
                                     
2006-03-22
EVALUATION

Contribution-Forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?messageID=12475&forumID=1463
                                     
2006-04-05
WORK AROUND

public void popupMenuWillBecomeVisible(PopupMenuEvent ev) {
                if(!willBecomeVisble){
                    JComboBox list = (JComboBox)ev.getSource();

                    .... // make some changes on the data model

                    willBecomeVisble = true; // the flag is needed to prevent a loop
                    try{
                        list.getUI().setPopupVisible( list, true );
                    }finally{
                        willBecomeVisble = false;
                    }
                }
            }
                                     
2006-10-26



Hardware and Software, Engineered to Work Together