JDK-4333326 : Multiple active carets - ambiguous focus
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.3.0
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_95,windows_nt
  • CPU: generic,x86
  • Submitted: 2000-04-26
  • Updated: 2002-01-15
  • Resolved: 2000-11-27
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
Hardware Platform : Intel Pentium II PC
OS Version : Window NT4.0 SP 5
JDK: 1.3 RC1
Various cases of confused focus are found in programs calling JTextField 
requestFocus method and popping modal dialogs.

A simple example that demonstrates multiple cursors is attached at the end.

- Launch app and without filling anything in the 3 JTextFields, click Ok
button and 3 separate JOptionPane dialogs will be shown. Each
JOptionPane actually requestFocus() for the respective JTextFields.
- Once over, notice that all 3 fields have cursors and worse still, I
can only enter data into the 3rd field. The 1st two fields no longer
response to my mouse clicks, tabulation and keyboard inputs.


Note: In the source codes, if requestFocus() is called before the
JOptionPane, the multiple cursors behave differently in that only the
middle JTextField does not responds.

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

public class TestCursor extends JPanel
{
    public JTextField field1;
    public JTextField field2;
    public JTextField field3;
    public TestCursor()
    {
        field1 = new JTextField( 10 );
        field2 = new JTextField( 10 );
        field3 = new JTextField( 10 );
        add( field1 );
        add( field2 );
        add( field3 );
        JButton btn = new JButton( "Ok" );
        btn.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent event )
            {
                if( field1.getText().trim().length() == 0 )
                {
                    showError( "Field1 is mandatory" );
                    field1.requestFocus();
                }
                if( field2.getText().trim().length() == 0 )
                {
                    showError( "Field2 is mandatory" );
                    field2.requestFocus();
                }
                if( field3.getText().trim().length() == 0 )
                {
                    showError( "Field3 is mandatory" );
                    field3.requestFocus();
                }
            }
        });
        add( btn );
    }

    public void showError( String message )
    {
        JOptionPane.showMessageDialog( this, message );
    }

    public static void main( String[] args )
    {
        JFrame frame = new JFrame();
        frame.setSize( new Dimension( 500, 100 ) );
        frame.getContentPane().add( new TestCursor() );
        frame.show();
    }
}



Name: ks88420			Date: 08/30/2000


java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

In a JFrame splitted by a JSplitPane, when the second panel is shown after two
dialogs in response to the first panel's action and one of the components (say,
BB) on the second panel request focus, the previous component on the first
panel that had the focus still "keeps" the focus (say, AA), and BB has another
focus. Dual focuses appear in the same window/frame. However, the focus on AA
does not respond to any event, and the actual focus has moved to BB.

This weird behavior can be reproduced by running the attached testing program.

Steps:
1) Compile and run it.
2) In the textfield of the first panel (AA), type in something, and then
press "Return" key or click on the "Search" button.
3) A message box is shown. Choose "Override".
4) Another message box is shown. Again, choose "Override".
5) The second panel is shown Now you can see two focuses appear in this window.

Notes:
1) For simplism, Here I use the same class for the first and second panel. They
can be different.
2) Even if I disable all the components in the first panel (restore the two
line from comments), the problem still appear.
3) The problem does not occur when this is only one message box before the
second panel is shown. I noticed that the focus is reset onto AA right after
the first message box is released (in the windowActivated event). However, why
showing one message box or dialog does not bring up this problem, but showing
two does?
4) After the problem occurs, the actual focus cannot return to the first panel.

The program I used:

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

public class FocusTest extends JFrame
{
        JScrollPane workPane;
        static PSearch panel0, panel1;
	JSplitPane split_V1;
	JPanel emptyPane;
	
	public FocusTest() {
        super("Focus Test");
		
		workPane=new JScrollPane();
		panel0=new PSearch();

		emptyPane=new JPanel();
		JLabel lblEmpty=new JLabel("Empty Pane");
		lblEmpty.setFont(new Font("SansSerif",Font.BOLD,18));
		emptyPane.add(lblEmpty);
		
	        split_V1=new JSplitPane(JSplitPane.VERTICAL_SPLIT,
panel0,emptyPane);

		getContentPane().add(split_V1, BorderLayout.CENTER);

		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
				if(JOptionPane.showConfirmDialog(null, "Do you
really want to exit?", "Focus Test",
									
	  JOptionPane.OK_CANCEL_OPTION)==JOptionPane.OK_OPTION) {
					System.exit(0);
				}
			}
			public void windowActivated(WindowEvent we) {
				FocusTest.panel0.txtSearch.requestFocus();
			}
			public void windowOpened(WindowEvent we) {
				FocusTest.panel0.txtSearch.requestFocus();
			}
        });
	  
    }

    public static void main(String s[]) {
		FocusTest window = new FocusTest();
		window.setSize(450, 450);
		window.setVisible(true);
    }


	protected class PSearch extends JPanel implements ActionListener {
		protected JTextField txtSearch=new JTextField(10);
		protected JLabel lblSearch=new JLabel("Enter search string: ");
		protected JButton btnSearch=new JButton("Search");
		
		public PSearch() {
			btnSearch.setActionCommand("Search");
			btnSearch.setMnemonic(KeyEvent.VK_S);
			lblSearch.setLabelFor(btnSearch);
			btnSearch.addActionListener(this);

			txtSearch.setActionCommand("Search");
			txtSearch.addActionListener(this);

			txtSearch.addFocusListener(new FocusAdapter(){
				public void focusGained(FocusEvent e){
				  txtSearch.selectAll();
				}
			});
			
			//Layout the components
			add(lblSearch);
			add(txtSearch);
			add(btnSearch);
		}

		public void actionPerformed(ActionEvent e){
			String cmd=e.getActionCommand();
			if(cmd.equals("Search") && FocusTest.panel1==null) {
					this.txtSearch.requestFocus();
					DlgException ex=new DlgException("This
is a test message!");
					if (ex.show()==JOptionPane.YES_OPTION) {
						//Do something here, then show
another messagebox or dialog.
						ex=new DlgException("Another
message...");
						ex.show();
					//Disable the components in panel0 does
not help.
					//for(int i=0;i<this.getComponentCount
();i++)
					//	this.getComponent(i).setEnabled
(false);
					
					FocusTest.panel1=new PSearch();
					split_V1.setBottomComponent
(FocusTest.panel1);
					split_V1.repaint();

					FocusTest.panel1.txtSearch.requestFocus
();
					}
			}
		}
	}
	
	protected class DlgException {
		private String msg=null;
		private String[] options={"Override",
								  "Cancel"};
		
		public DlgException(String msg) {
			this.msg =msg;
		}
		
		public int show(){
			return JOptionPane.showOptionDialog(null,
										
		msg,
										
		"Exception message",
										
		JOptionPane.YES_NO_OPTION,
										
		JOptionPane.WARNING_MESSAGE,
										
		null,
										
		options,
										
		null );
		}
	}

}
(Review ID: 108724)
======================================================================

Comments
WORK AROUND stephen.leung@eng ~May 2000 If the customer is trying to deploy his app ASAP, he may want to try this workaround. The idea is to avoid mixing JTextField.requestFocus and JOptionPane.showMessageDialog calls as much as possible - only call requestFocus when you absolutly must. The following is modified from the given test case from the bug description. I make sure that only one textfield will call requestFocus. ============================================================================ import javax.swing.*; import java.awt.*; import java.awt.event.*; public class TestCursor1 extends JPanel { public JTextField field1; public JTextField field2; public JTextField field3; public TestCursor1() { field1 = new JTextField( 10 ); field2 = new JTextField( 10 ); field3 = new JTextField( 10 ); add( field1 ); add( field2 ); add( field3 ); JButton btn = new JButton( "Ok" ); btn.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent event ) { int i=0; if( field1.getText().trim().length() == 0 ) { showError( "Field1 is mandatory" ); i=1; // field1.requestFocus(); } if( field2.getText().trim().length() == 0 ) { showError( "Field2 is mandatory" ); i=2; // field2.requestFocus(); } if( field3.getText().trim().length() == 0 ) { showError( "Field3 is mandatory" ); i=3; // field3.requestFocus(); } switch(i) { case 0: break; case 1: field1.requestFocus(); break; case 2: field2.requestFocus(); break; case 3: field3.requestFocus(); break; } } }); add( btn ); } public void showError( String message ) { JOptionPane.showMessageDialog( this, message ); } public static void main( String[] args ) { JFrame frame = new JFrame(); frame.setSize( new Dimension( 500, 100 ) ); frame.getContentPane().add( new TestCursor1() ); frame.show(); } } =========================================================================== stephen.leung@eng 2000-07-24 This is another workaround involves changing user code. We will use the API InputVerifier (suggested by Hania G) to check if a textfield is empty. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class TestCursorNew extends JPanel { static JFrame frame = null; public JTextField field1; public JTextField field2; public JTextField field3; public TestCursorNew() { field1 = new JTextField( 10 ); field2 = new JTextField( 10 ); field3 = new JTextField( 10 ); EmptyVerifier evf = new EmptyVerifier(); field1.setInputVerifier(evf); field2.setInputVerifier(evf); field3.setInputVerifier(evf); add( field1 ); add( field2 ); add( field3 ); JButton btn = new JButton( "Ok" ); btn.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent event ) { if( field1.getText().trim().length() == 0 ) { showError( "Field1 is mandatory" ); field1.requestFocus(); return; } if( field2.getText().trim().length() == 0 ) { showError( "Field2 is mandatory" ); field2.requestFocus(); return; } if( field3.getText().trim().length() == 0 ) { showError( "Field3 is mandatory" ); field3.requestFocus(); return; } } }); add( btn ); } public void showError( String message ) { JOptionPane.showMessageDialog( this, message ); } public static void main( String[] args ) { frame = new JFrame(); frame.setSize( new Dimension( 500, 100 ) ); frame.getContentPane().add( new TestCursorNew() ); frame.show(); } class EmptyVerifier extends InputVerifier { public boolean verify(JComponent input) { JTextField tf = (JTextField) input; if( tf.getText().trim().length() == 0 ) { JOptionPane.showMessageDialog (TestCursorNew.frame, "Current field is mandatory" ); return (false); } else { return (true); } } } } ============================================================================= stephen.leung@eng 2000-07-24 This is for the record only. The following describes a fix I prepared for the customer. The fix has tested by the customers, and they accepted it as workaround. The fix does not solve the multiple-focus issue but enable one to refocus. The multiple-focus issue appears in both 1.2.2 and 1.3. Problem of refocus only happens in 1.3. This is a one line change for jdk1.3 (comment out a line). In file src/share/classes/javax/swing/JComponent.java, public void grabFocus() { // if (hasFocus()) return; <- comment out this line Hania G. does not approve the fix because 1. it does not fix the root cause 2. it may break something
11-06-2004

SUGGESTED FIX ------- awt_Component.cpp ------- *** /tmp/dyKayOD Mon Oct 9 12:51:33 2000 --- awt_Component.cpp Mon Oct 9 11:02:32 2000 *************** *** 864,870 **** g_bMenuLoop = FALSE; break; ! case WM_SETFOCUS: mr = WmSetFocus((HWND)wParam); break; case WM_KILLFOCUS: mr = WmKillFocus((HWND)wParam); break; case WM_ACTIVATE: mr = WmActivate(LOWORD(wParam), (BOOL)HIWORD(wParam)); --- 864,872 ---- g_bMenuLoop = FALSE; break; ! case WM_SETFOCUS: ! if (::GetFocus() != GetHWnd()) break; ! mr = WmSetFocus((HWND)wParam); break; case WM_KILLFOCUS: mr = WmKillFocus((HWND)wParam); break; case WM_ACTIVATE: mr = WmActivate(LOWORD(wParam), (BOOL)HIWORD(wParam));
11-06-2004

EVALUATION I am able to recreate the problem with win32 jdk1.3 without Tao's fix for ESC# 525189. So it the problem is not introduced by his. I am not able to recreate the problem with Solaris jdk1.3. I am not able to recreate the problem with win32 jdk1.2.2_004 and jdk1.2.2. Under win32 jdk1.2.2_004 and jdk1.3, I see blinking cursors in all three textfields after clicking the OK buttons. In 1.2.2_004, I can regain focus by clicking the desired textfield. In 1.3, I cannot. Under Solaris jdk1.3, I don't see blinking cursors in all three textfields. I only see one which makes better sense. I tried not calling JOptionPane.showMessageDialog(). The problem goes away. My guess is : calling JTextField.reguestFocus from different textfields is OK. Focus will be set correctly. I believe it is the mixing of JOptionPane.showMessageDialog and JTextField.requestFocus calls the real problem. Since I can not recreate the problem with win32 jdk1.2.2_004, likely there is something new from jdk1.3 that is causing the new behavior. stephen.leung@eng 2000-05-02 =========================================================================== stephen.leung@eng 2000-07-24 In a nutshell I think this is the problem. Here is a snapshot of some sequence of events (in this order). === When the dialog box containing "Field1 is mandatory" is closed, FOCUS_GAINED goes to frame containing the textfields. FOCUS_LOST goes to the frame FOCUS_GAINED goes to the new dialog "Field2 is mandatory". FOCUS_GAINED goes to the frame === The last event is not correct. I looked at what jdk1.2.2 solaris does. FOCUS_GAINED should not be sent to the frame. ============================================================================== stuart.finlay@ireland 2000-10-11 I have implememnt Calvin Cheung's fox for 4352005 and found that it fixes this also. The spurious FOCUS_GAINED call to the frame which Stephen mentions above is eliminated with this fix. Diffs are given in the Suggested Fix section. See 4352005 for more details. ============================================================================== This was fixed as part of the new focus architecture. Refer to 4290675 for more information. scott.violet@eng 2000-11-27
27-11-2000