GUI
[Agent practical 7 of 9]


Now lets add some MenuItems.

Menus are heirarchically structured, starting with a MenuBar (at the top of the application), which may have multiple drop-down Menus, each of which can then have multiple MenuItems.

Here's the code for a menu with a "Open..." MenuItem:

   MenuBar menuBar = new MenuBar();
   Menu fileMenu = new Menu("File");
   menuBar.add(fileMenu);
   MenuItem openMenuItem = new MenuItem("Open...");
   fileMenu.add(openMenuItem);
   setMenuBar(menuBar);

Hopefully this should make some sense. Note that we don't use the Frame's add() method to add the MenuBar to the GUI: there is a special method to set the MenuBar.

Add this code to your buildGui(), between the code setting the size and the code setting the frame visible.


Next we need to make ourselves a listener for the menu. We may as well make the Model class the listener as well as the Frame. To do this, we need to implement ActionListener, as it says in the MenuItem docs.

Change the class declaration so that after it says it extends the Frame class, it also says it implements ActionListener (there is nothing to stop classes listening to objects inside themselves, or, indeed, extended GUI components acting as their own listeners).

   public class Model extends Frame implements ActionListener {


Add the following code inside the Model class, but outside the other methods - this is another method for this class and shouldn't go inside any existing methods, just the class block.

   public void actionPerformed(ActionEvent e) {
      MenuItem clickedMenuItem = (MenuItem)e.getSource();
      if (clickedMenuItem.getLabel().equals("Open...")) {
         world.setData(io.readData());
         buildAgents();
      }
   }

Note that we put buildAgents() after setData() so the agents spread correctly across the world (remember that they randomise across the world's width and height).

Note also the use of the .equals method to compare two Strings: as we saw in the second set of lectures this compares the words stored inside a String variable, not the String variable as an object, which == does. So:

String a = "hello";
String b = "hello";
String c = a;

a.equals(b) // true
a == b // false
a == c // true

Using == to try and compare the content of two Strings is a classic mistake, and a really hard bug to spot.

Finally, also note the "chaining" of results: the JVM would work out the result of clickedMenuItem.getLabel() first, get that String, and then call this String's .equals method.

If you compile the code above, you'll notice there's a problem - we got rid of the IO class object "io" at the start of the practical. Moreover, even if we hadn't got rid of it, it'd still be in the constructor, and not in this method, so it'd be out of scope here. We could put it in this method, but then it'd only exist for the milliseconds it took for this method to run, before being binned, and as we also want to use it for writing data later, that would see a waste. So, where should we put our IO object so that all the methods in this class can use it, and so that it exists as long as the Model class is running?

Have a think about this, then go on to Part Three, for the answer.