Link to home
Start Free TrialLog in
Avatar of musial
musial

asked on

JComboBox problems

After some unsuccessful digging I decided to post my two JComboBox related problems here hoping that someone would  tell me what I'm doing wrong.

1. When I add an action listener to my JComboBox, the second and all following selections do not generate popups, they generate an error message instead. The message reads: "java.lang.Error:convertScreenLocationToParent: no window ancestor found".

2. Using setLightWeightPopupEnabled(false) produces a quick flash of the popup's background in a random location of the screen before the popup is displayed.

Any hints will be highly appreciated.

Kris
musial@gale.com
Avatar of fontaine
fontaine

Could you post some sample code reproducing the problem?
Avatar of musial

ASKER

Certainly.
I developed a very small application demonstrating the two problems that I had posted.
The aplication consists of four classes: ParentFrame (main class), ChildFrame, Combo, and EditListener.

ParentFrame has a New button. When the button is clicked, the ChildFrame (JInternalFrame) is shown with the Combo (JComboBox) and a Close button in it.

The ChildFrame can be closed by either making a selection from the Combo, or by clicking the Close button.

Problem 1:

When the frame is closed by making a selection from the Combo, the next selection (in a new frame) will not generate a popup, instead the message 'convertScreenLocationToParent' is displayed.
When the frame is closed by clicking the button, it's OK.
The simple solution would be to construct the Combo each time the ChildFrame is constructed (instead of constructing it only once at the beginning and storing it in a static variable), but this solution is unacceptable, because the potential lists are long, selections frequent, and the application is slow already.

Problem 2:

Disabling the lightweight popup for the Combo using setLightWeightPopupEnabled(false) produces a quick flash of the popup's background in the top left corner of the ParentFrame.

The same problems occurred when the application was developed using JDK 1.1.5 and VisualCafe, although the flash for VisualCafe is much shorter.


The classes:

//---------------------------------------------------------------
// ParentFrame

import com.sun.java.swing.*;
import java.awt.event.*;
import java.awt.*;


public class ParentFrame extends JFrame implements ActionListener
  {
  static JDesktopPane desktop;
  static ChildFrame child = null;
  static Combo combo;


  public ParentFrame(String title)
    {
    super(title);

    JPanel panel = new JPanel();
    panel.setLayout(new BorderLayout());
    getContentPane().add(panel);

    desktop = new JDesktopPane();
    panel.add(desktop, BorderLayout.CENTER);
   
    combo = new Combo();

    JButton button = new JButton("New");
    button.addActionListener(this);
    panel.add(button, BorderLayout.NORTH);


    setSize(600, 500);
    setVisible(true);

    addWindowListener(new WindowAdapter()
      {
      public void windowClosing(WindowEvent e)
        {
        System.exit(0);
        }
      });
    }


  public void actionPerformed (ActionEvent event)
    {
    if (child == null)
      {
      child = new ChildFrame("ChildFrame");
      desktop.add(child, 0);
      }
    }

 
  public static void main(String argv[])
    {
    new ParentFrame("ParentFrame");
    }
  }


//---------------------------------------------------------------
// ChildFrame

import com.sun.java.swing.*;
import java.awt.*;


public class ChildFrame extends JInternalFrame
  {
  public ChildFrame(String title)
    {
    super(title);

    Container pane = getContentPane();
    pane.setLayout(new BorderLayout());
    pane.add(ParentFrame.combo, BorderLayout.NORTH);

    JButton close = new JButton("Close");
    close.addActionListener(new EditListener());
    pane.add(close, BorderLayout.SOUTH);
 
    setSize(300, 200);
    setLocation(100, 100);
    }
  }
 

//---------------------------------------------------------------
// Combo

import com.sun.java.swing.*;
import java.awt.*;


public class Combo extends JComboBox
  {
  static private String[] cntList = {"String", "Number", "Date", "Text", "Controlled"};

  public Combo()
    {
    super(cntList);
    setLightWeightPopupEnabled(false);
    addActionListener(new EditListener());
    }
  }


//---------------------------------------------------------------
// EditListener

import java.awt.event.*;


public class EditListener implements ActionListener
  {
  public void actionPerformed (ActionEvent event)
    {
    if (ParentFrame.child != null)
      {
      ParentFrame.desktop.remove(ParentFrame.child);
      ParentFrame.child = null;
      ParentFrame.desktop.repaint();
      }
    }
  }







replace


      ParentFrame.desktop.remove(ParentFrame.child);
      ParentFrame.child = null;
      ParentFrame.desktop.repaint();

with

  ParentFrame.child.setVisible(false);
  ParentFrame.child.dispose();
  ParentFrame.combo=null;
  ParentFrame.child = null;
  ParentFrame.desktop.repaint();

-------------------------
Replace

    pane.add(ParentFrame.combo, BorderLayout.NORTH);

with

    if (ParentFrame.combo == null)
          ParentFrame.combo=new Combo();
    pane.add(ParentFrame.combo, BorderLayout.NORTH);


and try it
 
Avatar of musial

ASKER

evijay

The reasons why the answer is not acceptable:

1. The proposed solution addresses only problem 1 and in the way I wanted to avoid for the reason explained in the problem clarification, which stated:

"The simple solution would be to construct the Combo each time the ChildFrame is constructed (instead of constructing it only once at the beginning and storing it in a static variable), but this solution is unacceptable, because the potential lists are long, selections frequent, and the application is slow already."

BTW: the same effect can be obtained by inserting only one line:
"ParentFrame.combo=new Combo();" in ChildFrame before adding combo to the frame, instead of proposed two replacements.

2. Problem 2 was not addressed.

P.S.
I apologize for submitting two problems at the same time but they were related and they could have been illustrated by the same simple application.

Kris

Avatar of musial

ASKER

Adjusted points to 130
ASKER CERTIFIED SOLUTION
Avatar of mjenkins
mjenkins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I took the liberty of cleaning up your code quite a bit. The use of statics was inappropriate and somewhat dangerous. I hope you don't mind. I also did away with EditListener altogether because I felt that this functionality really needed to be implemented within ChildFrame (you could argue that it belongs in ParentFrame, but I made a design decision).

The big problem that you were running into is the fact that all Java components (light and heavy) are designed to be added to a parent only once. Your re-use of the static Combo breaks this rule. Before you react, wait just a moment. Your argument about not wanting to recreate the Combo for each child is really an agument about the Combo DATA. By using a static MODEL for the Combo, you can reuse the DATA without the overhead of RELOADING it. The JComboBox itself is really just a view of the Model. The view is trivial to build and allows multiple models to work with it/on it at the same time.

Does this solve your problems? I can clarify further if you need.
Avatar of musial

ASKER

Excellent answer. No need for further clarification. Thanks for comments regarding the design, too. However, it addressed only one of the two problems that I posted. The quick flash still occurrs (in the top left corner of the main frame) when setLightWeightPopupEnabled is set to false.
Well, unfortunately the answer is: because there is a bug in the AWT that is exposed by the JPopupMenu class. There is a call to setLocation in the setVisible method that doesn't work properly. As long as you use a lightWeight popup this bug does not rear its ugly head. Actually there is also a medium weight popup implemented as a panel -- but I digress. You will also notice that the "flash" popup is a) empty and b) smaller than the actual popup. This is because a) it happens during the setLocation call and b) it actually appears partially underneath the parent Frame's title bar (it appears with the ULHC at 0,0 in respect to the Frame's window - not its client area ). For some reason, the Window object that is the popup flashes on BEFORE changing locations to line up with the combo box.

So the reason you haven't recieved a solution to problem 2 is that the fix needs to come from JavaSoft (Sun).