2

Within a tab of my GUI, the user is allowed to edit an employee's name. The name also serves as the tab's label, so when the change is confirmed the tab should be updated to reflect this change and the new data is written to a data file.

The employees are stored in a HashMap in the class Employees. The tabs are populated by iterating through an ArrayList<String> of employees' names, which is gotten from calling the method Employees.getNames(). From the GUI, the user can type in a new name and then press the change name button. The button's ActionListener calls the method changeName(), which replaces the old name with the new name in the HashMap and updates the data file.

This works correctly the first time a user wants to change the employee's name, but subsequent changes yield an error. It appears that the JPanel which contains the JTextFields (see getEmployeeInfoPanel() below) is not updating the parameter name. This parameter is the employee's current name, while the new name is gotten from the JTextField.

An example to illustrate this problem is below. Essentially, the steps are:

1. old name = Mary is provided when the program starts
2. User changes name in JTextField, so oldName = Mary and newName = Mary S.
3. At this point, oldName should update to Mary S. as it is the new key.
   However, oldName remains as Mary so the HashMap cannot be  updated again.

The hierarchy of this particular screen is:

JFrame (entire application)
   |
    -- JPanel EmployeesPanel (this screen)
   |     |
   |      -- JPanel (for custom menu bar)
   |     |
   |      -- JTabbedPane (one tab for each employee)
   |             |
   |              -- JPanel (contains JLabels, JTextField, etc for this employee)
   |
    -- .....

And here is the relevant code from the GUI:

public class EmployeesPanel {
    private JTabbedPane pane;
    private Employees employees;
    ...
    public EmployeesPanel(JPanel panel, Container cards) {
        ...
        pane = new JTabbedPane();
        getEmployees();
    }

    private void getEmployees() {
                    ...
        employees = new Employees(properties, EMPLOYEES_TXT);
        //ArrayList of all employees' names
        names = employees.getNames();
        for(String name : names) {
            pane.addTab(name, getEmployeeInfoPanel(name));
        }
        pane.addTab("NEW EMPLOYEE", addEmployeePanel());
    }

    public JPanel addEmployeePanel() {
        ...
    }

    private JPanel getEmployeeInfoPanel(final String name) throws EmployeeException {
        JPanel infoPanel = new JPanel();
        infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.PAGE_AXIS));

        JLabel nameLabel = new JLabel("Employee name");
        JLabel wageLabel = new JLabel("Employee wage");
        final JTextField nameField = new JTextField(name, 30);
        final JTextField wageField = new JTextField(employees.getWage(name).toString(), 30);

        JButton changeNameButton = new JButton("CHANGE NAME");
        JButton changeWageButton = new JButton("CHANGE WAGE");

        changeNameButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                try {
                    String newName = nameField.getText();
                    employees.changeName(name, newName);
                    panel.validate();
                } catch (EmployeeException e) {
                    // TODO create popup warning
                }
            }
        });
        ...
        return infoPanel;
    }
}

and here is the code from the class Employees that changes the HashMap:

public void changeName(String oldName, String newName) throws EmployeeException {
    System.out.println("old name = " + oldName + ", new name = " + newName);
    if(employees.containsKey(oldName)) {
        BigDecimal wage = employees.get(oldName);
        employees.remove(oldName);
        employees.put(newName, wage);
        names.remove(oldName);
        names.add(newName);
        prop.remove(oldName);
        prop.setProperty(newName, wage.toString());
        saveProperties();
        System.out.println(names);
    } else {
        throw new EmployeeException("Could not change name because employee does not exist.");
    }
}

Here is an example. The first screen shot is from when the program is started; the employee names are populated into the appropriate tabs. The second screen shot is after attempting to change the employee's name. As you can see, the tab's label has not changed, which I assumed the call to validate() would do.

(Before)

enter image description here

(After pressing button)

enter image description here

And finally, the output that pressing the change name button twice produces, showing that the name has been changed in the ArrayList:

old name = Mary, new name = Mary S.
[Jane, Bob, Sue, Mary S.]
old name = Mary, new name = Mary S.
653647 [AWT-EventQueue-0] ERROR employees.EmployeeException - Could not change name because employee does not exist.
A D
  • 789
  • 1
  • 12
  • 29
  • I don't see the code where the error is occurring. – Hovercraft Full Of Eels Jun 13 '12 at 01:36
  • The error is occuring when the change name button is pressed. The button's ActionListener calls the method ``changeName()``, which replaces the employee's old name with the new name. The first time it successfully replaces the name but does not update the tab, and any subsequent changes result in the error. – A D Jun 13 '12 at 01:46
  • If you are setting the JTabbedPane's title as recommended by @trashgod, and still having the problem, then I fear your question and code does not show enough to guess the problem. – Hovercraft Full Of Eels Jun 13 '12 at 01:48
  • I'm going to edit my question to hopefully provide more clarity – A D Jun 13 '12 at 01:50
  • 1
    You need to create and post an [sscce](http://sscce.org). All that unrelated code -- and your problem was that you simply weren't calling `setTitleAt()`. Lord. – Hovercraft Full Of Eels Jun 13 '12 at 01:53
  • That wasn't the only problem I was having though, and I thought the title problem was actually a side effect of the main problem I was having. – A D Jun 13 '12 at 02:05
  • Again, you are posting much code but since you have yet to isolate the problem, you may not be posting the offending code. You and we are better off if you first put in the effort to isolate the problem by winnowing down the code til you're left with the minimal code that compiles, runs, and reproduces the problem, an [sscce](http://sscce.org). – Hovercraft Full Of Eels Jun 13 '12 at 02:06
  • I recommend that you accept @trashgod's answer, since it does answer your original main question, and then start a new query for the remaining problem, but with the new question post your [SSCCE](http://sscce.org). – Hovercraft Full Of Eels Jun 13 '12 at 02:07
  • I am working on it, you're right that it would make this much easier :) My last comment was just to clarify that the title wasn't the main issue I was having - the confusion was my fault due to a poorly worded question. – A D Jun 13 '12 at 02:09

1 Answers1

4

You may be looking for the setTitleAt() method.

Addendum: For comparison, here's an sscce that allows multiple edits.

TabEdit

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;

/**
 * @see http://stackoverflow.com/a/11007109/230513
 */
public class TabEdit extends JPanel {

    private static final int MAX = 5;
    private static final String NAME = "Tab ";
    private final JTabbedPane pane = new JTabbedPane();

    public TabEdit() {
        for (int i = 0; i < MAX; i++) {
            pane.add(NAME + String.valueOf(i), new TabContent(i));
        }
        this.add(pane);
    }

    private class TabContent extends JPanel {

        private TabContent(final int i) {
            final JTextField jtf = new JTextField(
                "Please edit the name of " + NAME + String.valueOf(i));
            this.add(jtf);
            jtf.addActionListener(new AbstractAction() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    pane.setTitleAt(i, jtf.getText());
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(320, 120);
        }
    }

    private void display() {
        JFrame f = new JFrame("TabEdit");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TabEdit().display();
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for the reply. This fixed the problem I was having with the tab's title, but I am still not able to change the employee's name multiple times. – A D Jun 13 '12 at 01:48
  • @AD: Again, I recommend that you accept this answer and ask your secondary question in a new thread. 1+. – Hovercraft Full Of Eels Jun 13 '12 at 02:10
  • @AD: I just added an action listener directly to the text field to leverage the `Enter` key binding. Feel free to adapt this sscce to any related question going forward. – trashgod Jun 13 '12 at 02:22
  • 1
    @trashgod by working with your sscce I was able to fix the main issue I was having as well. Instead of using the ``final String name`` parameter as ``oldName`` in the ``ActionListener``, I instead get the title of the tab and it works perfectly. Thanks again :) – A D Jun 13 '12 at 02:41