JDK-4867005 : When removing a JPanel, child JButton gets a FOCUS_GAINED
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.2
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2003-05-20
  • Updated: 2007-01-31
  • Resolved: 2006-09-26
Related Reports
Duplicate :  
Relates :  
Description
Name: rmT116609			Date: 05/20/2003


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

(Note this bug was also present in 1.4.1.)

FULL OS VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
Applet contains a JPanel that has several child components, one of which has the focus. Removal of that JPanel (from the JApplet) causes a FOCUS_GAINED event for another child component of that JPanel (the same JPanel which is being removed). Focus remains on that removed component.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.Run the applet
2.Mouse-click the first button
3.Watch the Java console output
4.Notice that second button gets the focus (and keeps it) after it has been removed.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expect the focus to be either:
1. Lost to all components of the applet
or
2. Gained by some Applet component that is still displayable.
ACTUAL -
Java console shows that after the panel is removed, the second button of that panel (which is no longer displayable) gets a FOCUS_GAINED event. The focus stays there (as long as you don't click away from the applet, e.g. to activate the console window).


ERROR MESSAGES/STACK TRACES THAT OCCUR :
Console output after clicking first button:
removing old panel
adding new panel
FOCUS_LOST by 1
FOCUS_GAINED by 2

(Note if you see a final "FOCUS_LOST by 2", it is because you clicked away from the applet -- don't do that.)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// JApplet1.java
// Demonstrates FOCUS_GAINED bug.

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

public class JApplet1 extends JApplet implements FocusListener, ActionListener
{
  private int nextBtnNum = 1;
  private JPanel curPanel;
  private JButton curBtns[];

  public void init()
  {
    getContentPane().setLayout(new BorderLayout(0,0));
    setSize(400,100);

    createCurPanel();
    getContentPane().add(BorderLayout.CENTER, curPanel);
    curBtns[0].requestFocus();
  }

  // Create a 3-button panel into the "curPanel" variable.
  private void createCurPanel()
  {
    curPanel = new JPanel();
    curPanel.setFocusCycleRoot(true);
    curPanel.setFocusTraversalPolicy(new ContainerOrderFocusTraversalPolicy());
    curPanel.addFocusListener(this);

    curBtns = new JButton[3];
    curPanel.setLayout(new FlowLayout(FlowLayout.CENTER,5,5));
    for (int m=0; m < 3; m++) {
      JButton btn = new JButton(String.valueOf(nextBtnNum++));
      curPanel.add(btn);
      btn.addActionListener(this);
      btn.addFocusListener(this);
      curBtns[m] = btn;
    }
  }

  // Replace curPanel by a new 3-button panel
  private void replacePanel()
  {
    System.out.println("removing old panel");
    getContentPane().remove(curPanel);
    createCurPanel();
    System.out.println("adding new panel");
    getContentPane().add(BorderLayout.CENTER, curPanel);
    validate();

    // If we don't do a requestFocus here, the focus will be left
    //   on one of the buttons of the panel that was removed.

    // If we want the focus to go to the new panel, we have to do
    //   the following:
    //SwingUtilities.invokeLater(
    //  new Runnable() { public void run() { curBtns[0].requestFocus(); }});

    // Simply calling
    //    curBtns[0].requestFocus();
    // here doesn't work. Is this another bug?
  }

  private String describeComponent(Component c)
  {
    if (c instanceof JButton) return ((JButton)c).getText();
    if (c == curPanel) return "curPanel";
    return c.toString();
  }

  public void focusGained(FocusEvent e)
  {
    System.out.println("FOCUS_GAINED by " + describeComponent(e.getComponent()));
  }

  public void focusLost(FocusEvent e)
  {
    System.out.println("FOCUS_LOST by " + describeComponent(e.getComponent()));
  }

  // Get button clicks
  public void actionPerformed(ActionEvent e)
  {
    replacePanel();
  }
}

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

CUSTOMER SUBMITTED WORKAROUND :
I made my own FocusTraversalPolicy that returns null from getComponentAfter for a component that I know I am about to remove. This involves marking every component that I am about to remove. This workaround is only a stopgap and we are anxiously awaiting a bug fix.
(Review ID: 185858) 
======================================================================

Comments
EVALUATION Looks similar to 4781423.
15-09-2005

EVALUATION Focus is not done synchronously. Therefore, when you remove the panel the focus manager tries to focus the next component, being the next in the list. Try setting the focus to something else prior to removing the panel. I need to investigate a little more why focus stays on that removed component though. ###@###.### 2003-06-03
03-06-2003