Link to home
Start Free TrialLog in
Avatar of AttilaB
AttilaB

asked on

Detecting user input: Java jTextfield with DocumentListener causes erratic GUI Action

I have a larger program that one JFrame is used both for displaying program output and detecting user typing
in JTextfield controls that is processed by a DocumentListener so that if the user changes the contents of the JTextfield
the program can perform the appropriate action corresponding action to that user change. The program can also write to this JTextfield if buttons are clicked. Problem: Basically there seems to be a discrepancy between changes made in the contents of the JTextfield manually and programmatically. The update of the textfield doesn't show correctly under certain circumstances.

To illustrate the problem, here is the simplified input form:
User generated image
By activating ->Board ->Get Selected Parts from the menus the string "Test" gets loaded into 'jtxtfldCompInGroup2' text field.
Then by pressing 'jbtnRditGroup2' (Edit Group) button this gets copied to 'jtxtfldGroup2_Edit', to the textfield below:
(Also note that the 'Edit Group' button is disabled at this point.)
User generated image
Now, I can manually type in the 'Test2, Test3, Test4' text into 'jtxtfldGroup2_Edit'. As I am doing this, the DocumentListener is
looking at what I am typing in the jTextfield and it will perform the appropriate action for the changed content of the
'jtxtfldGroup2_Edit' JTextfield:
User generated image
Now, I can click the 'Save Group' button ('jbtnSaveNewGroup') and it will overwrite the contents of the original
top text field 'jtxtfldCompInGroup2' correctly. The DocumentListener is also working correctly at this point, I can perform
updates multiple times successfully:
User generated image
This will work multiple times correctly copying from bottom to top, and 'Edit Group' to copy from top to bottom is enabled again
but no longer working:
User generated image
Copying from the non-editable text field to the editable text field will no longer work now, the contents of the editable
text field is not changed by pressing the button:
User generated image
At this point the bottom editable text-field becomes non-functional for manual editing, save group will blank out the
top textfield instead of writing the contents:
User generated image
Also, the functionality if the editable text box is broken at this point, no longer can be edited manually, no matter
what you type the contents get messed up. (This works the same with the 'Clear Group' button too):
User generated image
The code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class GUI_NotUpdate extends JFrame {
    
    private JMenuBar menuBar1;
    private JMenu mnuTopLevel_View;
    private JMenu mnuBoard;
    private JMenuItem jmiGetSelectedParts;
    private JLabel jlblTitle;
    private JLabel jlblGroupsSelected;
    private JComboBox jcmboGroupNames;
    private JLabel jlblAddNewGroup;
    private JTextField jtxtfldGroupName;
    private JLabel jlblGroupName2;
    private JButton jbtnDeleteGroup;
    private JButton jbtnClearNewGroup;
    private JTextField jtxtfldCompInGroup1;
    private JLabel jlblCompSelInGroup1;
    private JSeparator separator1;
    private JSeparator separator2;
    private JButton jbtnEditGroup;
    private JButton jbtnSaveGroup;
    private JLabel jlblGroupName1;
    private JTextField jtxtfldCompInGroup2;
    private JLabel jlblCompSelInGroup2;
    private JButton jbtnSaveNewGroup;
    private JButton jbtnReAnnotatePCB;
    private JSeparator separator3;
    private JTextField jtxtfldGroup1_Edit;
    private JTextField jtxtfldGroup2_Edit;
    private JLabel jlblCompSelInGroup3;
    private JLabel jlblCompSelInGroup4;
    private JButton jbtnEditGroup2;
         
     // instantiate action listener class that is used for all the menus:
           private MenuActionListener menuActionListener = new MenuActionListener();
           
           // These constants will identify which of the text fields was
           // changed by the user, for the method handling document listener for
           // text areas:
           private final int  COMPSINGROUP1_FOCUSED = 1;
           private final int  COMPSINGROUP2_FOCUSED = 2;
           
           private int lastChangedTextFieldID = 0;
    
        
public GUI_NotUpdate() {
	    super("     Re-Annotate Screen" ); // Child window initialization
	    try {
                    // Set the look and feel for the current OS (Windows) Scheme
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	                                   
                } catch (Exception e) {
                    e.printStackTrace();
	                       }
		initComponents();                              
            }   // End of Constructor


	private void jcmboGroupNamesItemStateChanged(ItemEvent e) {
		// TODO add your code here
	}

	private void jtxtfldGroupNameActionPerformed(ActionEvent e) {
		// TODO add your code here
	}

	private void jbtnDeleteGroupActionPerformed(ActionEvent e) {
		System.out.println("Delete Group 1 Clicked");
	}

	private void jbtnClearNewGroupActionPerformed(ActionEvent e) {
	    System.out.println("Clear New Group Clicked");
            jtxtfldGroup2_Edit.setText("");
	}

	private void jtxtfldCompInGroup1ActionPerformed(ActionEvent e) {
		// TODO add your code here
	}

	private void jbtnEditGroupActionPerformed(ActionEvent e) {
	    System.out.println("Edit Group 1 Clicked");
	    // copy 'jtxtfldCompInGroup1' contents to 'jtxtfldGroup1_Edit'
            jtxtfldGroup1_Edit.setText(jtxtfldCompInGroup1.getText());
	}

	private void jbtnSaveGroupActionPerformed(ActionEvent e) {
		System.out.println("Save Group 1 Clicked");
	}

	private void jtxtfldCompInGroup2ActionPerformed(ActionEvent e) {
		// TODO add your code here
	}

	private void jbtnSaveNewGroupActionPerformed(ActionEvent e) {
	    System.out.println("Save Group 2 Clicked");
            jtxtfldCompInGroup2.setText(jtxtfldGroup2_Edit.getText());
	   // jtxtfldGroup2_Edit.setText("");  // CANNOT BE SET PROGRAMMATICALLY!!!!
	    jbtnEditGroup2.setEnabled(true);
	}

	private void jbtnReAnnotatePCBActionPerformed(ActionEvent e) {
		System.out.println("Re-Annotate PCB Clicked");
	}

	private void jtxtfldGroup1_EditActionPerformed(ActionEvent e) {
		// TODO add your code here
	}

	private void jbtnEditGroup2ActionPerformed(ActionEvent e) {
	    System.out.println("Edit Group 2 Clicked");
            // copy 'jtxtfldCompInGroup2' contents to 'jtxtfldGroup2_Edit'
            jbtnEditGroup2.setEnabled(false);
            String text = jtxtfldCompInGroup2.getText();
	    jtxtfldGroup2_Edit.setText(text);
	}

	private void initComponents() {
		// JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
		menuBar1 = new JMenuBar();
		mnuTopLevel_View = new JMenu();
		mnuBoard = new JMenu();
		jmiGetSelectedParts = new JMenuItem();
		jlblTitle = new JLabel();
		jlblGroupsSelected = new JLabel();
		jcmboGroupNames = new JComboBox();
		jlblAddNewGroup = new JLabel();
		jtxtfldGroupName = new JTextField();
		jlblGroupName2 = new JLabel();
		jbtnDeleteGroup = new JButton();
		jbtnClearNewGroup = new JButton();
		jtxtfldCompInGroup1 = new JTextField();
		jlblCompSelInGroup1 = new JLabel();
		separator1 = new JSeparator();
		separator2 = new JSeparator();
		jbtnEditGroup = new JButton();
		jbtnSaveGroup = new JButton();
		jlblGroupName1 = new JLabel();
		jtxtfldCompInGroup2 = new JTextField();
		jlblCompSelInGroup2 = new JLabel();
		jbtnSaveNewGroup = new JButton();
		jbtnReAnnotatePCB = new JButton();
		separator3 = new JSeparator();
		jtxtfldGroup1_Edit = new JTextField();
		jtxtfldGroup2_Edit = new JTextField();
		jlblCompSelInGroup3 = new JLabel();
		jlblCompSelInGroup4 = new JLabel();
		jbtnEditGroup2 = new JButton();

		//======== this ========
		Container contentPane = getContentPane();
		contentPane.setLayout(null);
		//======== menuBar1 ========
		{
			//======== mnuTopLevel_View ========
			{
				mnuTopLevel_View.setText("View");
			}
			menuBar1.add(mnuTopLevel_View);

			//======== mnuBoard ========
			{
				mnuBoard.setText("Board");

				//---- jmiGetSelectedParts ----
				jmiGetSelectedParts.setText("Get Selected Parts");
				mnuBoard.add(jmiGetSelectedParts);
                                jmiGetSelectedParts.addActionListener(menuActionListener);
			}
			menuBar1.add(mnuBoard);
		}
		setJMenuBar(menuBar1);

		//---- jlblTitle ----
		jlblTitle.setText("Select / Edit Groups of Components for Re-Annotating PCB");
		jlblTitle.setForeground(Color.blue);
		jlblTitle.setFont(new Font("Times New Roman", Font.BOLD, 14));
		contentPane.add(jlblTitle);
		jlblTitle.setBounds(65, 5, 380, 25);

		//---- jlblGroupsSelected ----
		jlblGroupsSelected.setText("Groups Already Selected:");
		jlblGroupsSelected.setFont(new Font("Tahoma", Font.BOLD, 12));
		contentPane.add(jlblGroupsSelected);
		jlblGroupsSelected.setBounds(35, 35, 160, jlblGroupsSelected.getPreferredSize().height);

		//---- jcmboGroupNames ----
		jcmboGroupNames.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent e) {
				jcmboGroupNamesItemStateChanged(e);
			}
		});
		contentPane.add(jcmboGroupNames);
		jcmboGroupNames.setBounds(55, 70, 385, jcmboGroupNames.getPreferredSize().height);

		//---- jlblAddNewGroup ----
		jlblAddNewGroup.setText("Save Group");
		jlblAddNewGroup.setFont(new Font("Tahoma", Font.BOLD, 12));
		contentPane.add(jlblAddNewGroup);
		jlblAddNewGroup.setBounds(35, 220, 170, jlblAddNewGroup.getPreferredSize().height);

		//---- jtxtfldGroupName ----
		jtxtfldGroupName.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jtxtfldGroupNameActionPerformed(e);
			}
		});
		contentPane.add(jtxtfldGroupName);
		jtxtfldGroupName.setBounds(55, 260, 385, jtxtfldGroupName.getPreferredSize().height);

		//---- jlblGroupName2 ----
		jlblGroupName2.setText("Group Name:");
		contentPane.add(jlblGroupName2);
		jlblGroupName2.setBounds(55, 245, 115, jlblGroupName2.getPreferredSize().height);

		//---- jbtnDeleteGroup ----
		jbtnDeleteGroup.setText("Delete Group");
		jbtnDeleteGroup.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnDeleteGroup.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnDeleteGroupActionPerformed(e);
			}
		});
		contentPane.add(jbtnDeleteGroup);
		jbtnDeleteGroup.setBounds(355, 185, 130, jbtnDeleteGroup.getPreferredSize().height);

		//---- jbtnClearNewGroup ----
		jbtnClearNewGroup.setText("Clear Group");
		jbtnClearNewGroup.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnClearNewGroup.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnClearNewGroupActionPerformed(e);
			}
		});
		contentPane.add(jbtnClearNewGroup);
		jbtnClearNewGroup.setBounds(355, 370, 130, jbtnClearNewGroup.getPreferredSize().height);

		//---- jtxtfldCompInGroup1 ----
		jtxtfldCompInGroup1.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jtxtfldCompInGroup1ActionPerformed(e);
			}
		});
		contentPane.add(jtxtfldCompInGroup1);
                jtxtfldCompInGroup1.setEditable(false);
		jtxtfldCompInGroup1.setBounds(15, 115, 480, jtxtfldCompInGroup1.getPreferredSize().height);

		//---- jlblCompSelInGroup1 ----
		jlblCompSelInGroup1.setText("Components Currently Saved in Group:");
		contentPane.add(jlblCompSelInGroup1);
		jlblCompSelInGroup1.setBounds(20, 100, 195, jlblCompSelInGroup1.getPreferredSize().height);
		contentPane.add(separator1);
		separator1.setBounds(15, 145, separator1.getPreferredSize().width, 2);
		contentPane.add(separator2);
		separator2.setBounds(20, 215, 465, 10);

		//---- jbtnEditGroup ----
		jbtnEditGroup.setText("Edit Group");
		jbtnEditGroup.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnEditGroup.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnEditGroupActionPerformed(e);
			}
		});
		contentPane.add(jbtnEditGroup);
		jbtnEditGroup.setBounds(35, 185, 130, jbtnEditGroup.getPreferredSize().height);

		//---- jbtnSaveGroup ----
		jbtnSaveGroup.setText("Save Group");
		jbtnSaveGroup.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnSaveGroup.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnSaveGroupActionPerformed(e);
			}
		});
		contentPane.add(jbtnSaveGroup);
		jbtnSaveGroup.setBounds(195, 185, 130, jbtnSaveGroup.getPreferredSize().height);

		//---- jlblGroupName1 ----
		jlblGroupName1.setText("Group Name:");
		contentPane.add(jlblGroupName1);
		jlblGroupName1.setBounds(new Rectangle(new Point(55, 55), jlblGroupName1.getPreferredSize()));

		//---- jtxtfldCompInGroup2 ----
		jtxtfldCompInGroup2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jtxtfldCompInGroup2ActionPerformed(e);
			}
		});
		contentPane.add(jtxtfldCompInGroup2);
                jtxtfldCompInGroup2.setEditable(false);
		jtxtfldCompInGroup2.setBounds(15, 300, 475, 20);

		//---- jlblCompSelInGroup2 ----
		jlblCompSelInGroup2.setText("Components Currently Saved in Group:");
		contentPane.add(jlblCompSelInGroup2);
		jlblCompSelInGroup2.setBounds(15, 285, 210, 14);

		//---- jbtnClearGroup ----
		jbtnSaveNewGroup.setText("Save Group");
		jbtnSaveNewGroup.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnSaveNewGroup.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnSaveNewGroupActionPerformed(e);
			}
		});
		contentPane.add(jbtnSaveNewGroup);
		jbtnSaveNewGroup.setBounds(195, 370, 130, jbtnSaveNewGroup.getPreferredSize().height);

		//---- jbtnReAnnotatePCB ----
		jbtnReAnnotatePCB.setText("Re-Annotate PCB");
		jbtnReAnnotatePCB.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnReAnnotatePCB.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnReAnnotatePCBActionPerformed(e);
			}
		});
		contentPane.add(jbtnReAnnotatePCB);
		jbtnReAnnotatePCB.setBounds(185, 425, 143, jbtnReAnnotatePCB.getPreferredSize().height);
		contentPane.add(separator3);
		separator3.setBounds(20, 405, 465, 10);

		//---- jtxtfldGroup1_Edit ----
		jtxtfldGroup1_Edit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jtxtfldCompInGroup1ActionPerformed(e);
				jtxtfldGroup1_EditActionPerformed(e);
			}
		});
		contentPane.add(jtxtfldGroup1_Edit);
		jtxtfldGroup1_Edit.setBounds(15, 155, 480, 20);

		//---- jtxtfldGroup2_Edit ----
		jtxtfldGroup2_Edit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jtxtfldCompInGroup2ActionPerformed(e);
			}
		});
		contentPane.add(jtxtfldGroup2_Edit);
		jtxtfldGroup2_Edit.setBounds(15, 340, 475, jtxtfldGroup2_Edit.getPreferredSize().height);

		//---- jlblCompSelInGroup3 ----
		jlblCompSelInGroup3.setText(" Edit Components in Group:");
		contentPane.add(jlblCompSelInGroup3);
		jlblCompSelInGroup3.setBounds(15, 140, 195, 14);

		//---- jlblCompSelInGroup4 ----
		jlblCompSelInGroup4.setText(" Edit Components in Group:");
		contentPane.add(jlblCompSelInGroup4);
		jlblCompSelInGroup4.setBounds(15, 325, 195, 14);

		//---- jbtnEditGroup2 ----
		jbtnEditGroup2.setText("Edit Group");
		jbtnEditGroup2.setFont(new Font("Tahoma", Font.BOLD, 11));
		jbtnEditGroup2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				jbtnEditGroupActionPerformed(e);
				jbtnEditGroup2ActionPerformed(e);
			}
		});
		contentPane.add(jbtnEditGroup2);
		jbtnEditGroup2.setBounds(35, 370, 130, 23);

		{ // compute preferred size
			Dimension preferredSize = new Dimension();
			for(int i = 0; i < contentPane.getComponentCount(); i++) {
				Rectangle bounds = contentPane.getComponent(i).getBounds();
				preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
				preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
			}
			Insets insets = contentPane.getInsets();
			preferredSize.width += insets.right;
			preferredSize.height += insets.bottom;
			contentPane.setMinimumSize(preferredSize);
			contentPane.setPreferredSize(preferredSize);
		}
		pack();
		setLocationRelativeTo(getOwner());
                
	    // Add a listener to the underlying Document object for JTextField, which is automatically created.   
	                        // Listen for changes in the text entered in all the text fields:
	                        jtxtfldGroup1_Edit.getDocument().addDocumentListener(new DocumentListener() {
	                          public void changedUpdate(DocumentEvent e) {
	                            changeDetected(COMPSINGROUP1_FOCUSED);
	                          }
	                          public void removeUpdate(DocumentEvent e) {
	                            changeDetected(COMPSINGROUP1_FOCUSED);
	                          }
	                          public void insertUpdate(DocumentEvent e) {
	                            changeDetected(COMPSINGROUP1_FOCUSED);
	                          }
	                        });
	                        
	                        jtxtfldGroup2_Edit.getDocument().addDocumentListener(new DocumentListener() {
	                          public void changedUpdate(DocumentEvent e) {
	                            changeDetected(COMPSINGROUP2_FOCUSED);
	                          }
	                          public void removeUpdate(DocumentEvent e) {
	                            changeDetected(COMPSINGROUP2_FOCUSED);
	                          }
	                          public void insertUpdate(DocumentEvent e) {
	                            changeDetected(COMPSINGROUP2_FOCUSED);
	                          }
	                        });      
		  
	}   // End of component initialization
                 
          
    // The change is detected in EITHER of the MONITORED textfields, modify selection in the PCB accortdingly: 
    // Keep track of which textfield was changed last time. If the textfield ID changes clear board
    // and see what the last character is comma or space, if yes then update selection.
    public void changeDetected(int textFieldRefNumber) {
                   System.out.println("Change Detected: " + textFieldRefNumber);

                   String updatedText = "";
                   
                   // when the change is detected look at the last character in textfield changed.
                   // if the last character is a space or comma update selection in PCB
                   if (textFieldRefNumber == COMPSINGROUP1_FOCUSED ){
                       // get the text field contents, if last character is space or comma select parts:
                       updatedText = jtxtfldGroup1_Edit.getText();
                   }
                   else{
                       // get the other text field contents,
                       updatedText = jtxtfldGroup2_Edit.getText();
                   }
                   
               String lastChar = updatedText.substring(updatedText.length()-1, updatedText.length());
                       //  if last character is space or comma select parts:
                       if(lastChar.equals(",")){
                           
                       }
                   // Save last changed textfield reference
                   lastChangedTextFieldID = textFieldRefNumber;
               }
    
    public static void main(String[] args) {
                    GUI_NotUpdate gUI_NotUpdate = new GUI_NotUpdate();
                    
                    gUI_NotUpdate.setSize(530,530);
                    gUI_NotUpdate.setResizable(false);
                    gUI_NotUpdate.setVisible(true);
                    gUI_NotUpdate.setAlwaysOnTop(true);
                    
                }
    
   // MENU ACTION LISTENER - inner class actionlistener for all menus:
   private class MenuActionListener implements ActionListener {
       
           public MenuActionListener(){   
                            }
                   public void actionPerformed(ActionEvent e) {

                            String actionCommand = e.getActionCommand();
                            
                           if (actionCommand == "Save New Selection") {
                               System.out.println("Save New Selection Selected");
                               // casconMainScreen.clearSelectionInPCB();
                           }
                           if (actionCommand == "Clear Group") {
                               System.out.println("Clear Group Selected");
                               jbtnClearNewGroup.setText("");
                           }
                                                
                           if (actionCommand == "Get Selected Parts") {
                               jtxtfldCompInGroup2.setText("Test");
                           }               
                       }
   }        // End of inner class MenuActionListener
   
}  // End of Class

Open in new window



What is happening here, and what can I do about it?
ASKER CERTIFIED SOLUTION
Avatar of krakatoa
krakatoa
Flag of United Kingdom of Great Britain and Northern Ireland 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
Avatar of AttilaB
AttilaB

ASKER

Well, your code actually works as expected. So this IS the solution. I could not make it fail when I tried, with any sequence of actions on the GUI.

You are declaring and initializing String lastChar at the beginning, outside the block.
I do it just right before I use it, but I initialize it every time before taking the substring, anyway.

I don't understand why my code causes the problems I am experiencing at run-time only, and yours doesn't.

What does NPEs mean?
NPE - NullPpointerException. (Acutally I didn't think you had runtime errors from your question, but maybe I missed something).

Well the lastChar String should be tested, shouldn't it? Because if it is null, then there's no point calling equals() on it. I don't know the likely null and non-null conditions of your String vars without going over the whole code, but I'd say that unless you are 100% sure some String can never return null, then a test for null comes in handy. ;)
Avatar of AttilaB

ASKER

Thanks for your help and explanation!
By the way -

lastChar = updatedText.substring(updatedText.length()-1, updatedText.length());

Open in new window


can be simply

lastChar = updatedText.substring(updatedText.length()-1);

Open in new window



... and because you test "updatedText" for a length more than zero, if it IS zero, then lastChar will fail . . . so you might want to give that whole passage there some more thought.
Added one last comment to my previous. Ciao.
Avatar of AttilaB

ASKER

Thanks!