Main Page

Previous Next

Supporting the File Menu

To support the menu items in the File menu, we must add some code to the actionPerformed() method in the FileAction class. We can try to put a skeleton together but a problem presents itself immediately: the ultimate source of an event will be either a toolbar button (a JButton object) or a menu item (a JMenuItem object) that was created from a FileAction object. How do we figure out what the action was that originated the event? We only have one definition of the actionPerformed() method shared amongst all FileAction class objects so we need a way to determine which particular FileAction object caused the event. That way we can decide what we should do in response to the event.

Each FileAction object stores a String that was passed to the constructor as the name argument, and was then passed on to the base class constructor. If only we had thought of saving it, we could compare the name for the current object with the name for each of the FileAction objects in the SketchFrame class. Then we could tell which object the actionPerformed() method was called for.

All is not lost though. We can call the getValue() method for the ActionEvent object to retrieve the name for the action object that caused the event. We can then compare that with the name for each of the FileAction objects that we store as members of the SketchFrame class. We can therefore implement the actionPerformed() member of the FileAction class like this:

public void actionPerformed(ActionEvent e) {
  String name = (String)getValue(NAME);
  if(name.equals(saveAction.getValue(NAME))) {
    // Code to handle file Save operation...
  } else if(name.equals(saveAsAction.getValue(NAME))) {
    // Code to handle file Save As operation...
  } else if(name.equals(openAction.getValue(NAME))) {
    // Code to handle file Open operation...
  } else if(name.equals(newAction.getValue(NAME))) {
    // Code to handle file New operation...
  } if(name.equals(printAction.getValue(NAME))) {
    // Code to handle Print operation..

Calling getValue() with the argument as the NAME key that is defined in the Action interface returns the String object that was stored for the FileAction object when it was created. If the name for the current object matches that of a particular FileAction object, then that must be the action to which the event applies, so we know what to do. We have one if or else–if block for each action, and we will code these one by one.

Many of these operations will involve dialogs. We need to get at the file system and display the list of directories and files to choose from, for an Open operation for instance. It sounds like a lot of work, and it certainly would be, if it weren't for a neat facility provided by the JFileChooser class.

Using a File Chooser

The JFileChooser class in the javax.swing package provides an easy to use mechanism for creating file dialogs for opening and saving files. You can use a single object of this class to create all the file dialogs you need, so add a member to the SketchFrame class to store a reference to a JFileChooser object that we will create in the constructor:

private JFileChooser files;                   // File chooser dialog

There are several JFileChooser constructors but we will discuss just a couple of them here. The default constructor creates an object with the current directory as the default directory but that won't quite do for our purposes. What we want is for the default directory to be the DEFAULT_DIRECTORY, which we defined in the Constants interface, so we'll use the constructor that accepts a File object specifying a default directory to create a JFileChooser object in the SketchFrame constructor. Add the following statement to the constructor following the statements that we added earlier that ensured DEFAULT_DIRECTORY actually existed on the hard drive:

files = new JFileChooser(DEFAULT_DIRECTORY);

Any dialogs created by the files object will have DEFAULT_DIRECTORY as the default directory that is displayed. We can now use the files object to implement the event handling for the File menu. There are a considerable number of methods in the JFileChooser class, so rather than trying to summarize them all, which would take many pages of text and be incredibly boring, let's try out the ones that we can apply to Sketcher to support the File menu.

File Save Operations

In most cases we will want to display a modal file save dialog when the Save menu item or toolbar button is selected. As luck would have it, the JFileChooser class has a method showSaveDialog() that does precisely what we want. All we have to do is pass it a reference to the Component object that will be the parent for the dialog to be displayed. The method returns a value indicating how the dialog was closed. We could display a save dialog in a FileAction() method with the statement:

int result = files.showSaveDialog(SketchFrame.this);

This will automatically create a File Save dialog with the SketchFrame object as parent, and Save and Cancel buttons. The SketchFrame.this notation is used to refer to the this pointer for the SketchFrame object from within a method of an inner class object of type FileAction. The file chooser dialog will be displayed centered in the parent component – our SketchFrame object here. If you specify the parent component as null, the dialog will be centered on the screen. This also applies to all the other methods we will discuss that display file chooser dialogs.

When you need a file open dialog, you can call the showOpenDialog() member of a JFileChooser object. Don't be fooled here though. A save dialog and an open dialog are essentially the same. They only differ in minor details – the title bar and one of the button labels. The sole purpose of both dialogs is simply to select a file – for whatever purpose. If you wanted to be perverse, you could pop up a save dialog to open a file and vice versa!

You also have the ability to display a customized dialog from a JFileChooser object. Although it's not strictly necessary for us here – we could make do with the standard file dialogs – we will adopt a custom approach, as it will give us some experience of using a few more JFileChooser methods.

You can display a dialog by calling the showDialog() method for the JFileChooser object supplying two arguments. The first is the parent component for the dialog window, and the second is the approve button text – the approve button being the button that you click on to expedite the operation rather than cancel it. You could display a dialog with a Save button with the statement:

int result = files.showDialog(SketchFrame.this, "Save");

If you pass null as the second argument here, the button text will be whatever was set previously – possibly the default.

Before you display a custom dialog, though, you would normally do a bit more customizing of what is to be displayed. We will be using the following JFileChooser methods to customize our dialogs:




The String object passed as an argument is set as the dialog title bar text.


The String object passed as an argument is set as the approve button label.


The String object passed as an argument is set as the approve button tooltip.


The character passed as an argument is set as the approve button mnemonic defining a shortcut. The shortcut will appear as part of the tooltip.

A file chooser can be set to select files, directories, or both. You can determine which of these is allowed by calling the setFileSelectionMode() method. The argument must be one of the constants FILES_ONLY, DIRECTORIES_ONLY, and FILES_AND_DIRECTORIES as defined in the JFileChooser class, FILES_ONLY being the default. You also have the getFileSelectionMode() method to enable you to determine what selection mode is set. To allow multiple selections to be made from the list in the file dialog, you call the setMultiSelectionEnabled()method for your JFileChooser object with the argument true.

If you want the dialog to have a particular file selected when it opens, you can pass a File object specifying that file to the setSelectedFile() method for the JFileChooser object. This will pre-select the file in the file list for the dialog if the file already exists, and insert the name in the file name field if it doesn't. The file list is created when the JFileChooser object is created but naturally files may be added or deleted over time and when this occurs you will need to reconstruct the file list. Calling the rescanCurrentDirectory() method before you display the dialog will do this for you. You can change the current directory at any time by passing a File object specifying the directory to the setCurrentDirectory() method.

That's enough detail for now. Let's put our customizing code together.

Try It Out – Creating a Customized File Dialog

We will first add a method to the SketchFrame class to create our customized file dialog and return the File object corresponding to the file selected, or null if a file was not selected:

// Display a custom file save dialog
private File showDialog(String dialogTitle,
                        String approveButtonText,
                        String approveButtonTooltip,
                        char approveButtonMnemonic,
                        File file) {                // Current file – if any

  int result = files.showDialog(SketchFrame.this, null);  // Show the dialog
  return (result == files.APPROVE_OPTION) ? files.getSelectedFile() : null;

This method accepts five arguments that are used to customize the dialog – the dialog title, the button label, the button tooltip, the shortcut character for the button, and the File object representing the file for the current sketch. Each of the options is set using one of the methods for the JFileChooser object that we discussed earlier. The last argument is used to select a file in the file list initially. If it is null, no file will be selected from the file list.

Note that we reconstruct the file list for the dialog by calling the rescanCurrentDirectory() method. This is to ensure that we always display an up-to-date list of files. If we didn't do this, the dialog would always display the list of files that were there when we created the JFileChooser object. Any changes to the contents of the directory since then would not be taken into account.

The return value from the showDialog() member of the JFileChooser object files determines whether the approve button was selected or not. If it was, we return the File object from the file chooser that represents the selected file, otherwise we return null. A method calling our showDialog() method can determine whether or not a file was chosen by testing the return value for null.

We can now use this method when we implement handling of a Save menu item action event. A save operation is a little more complicated than you might imagine at first sight, so let's consider it in a little more detail.

Implementing the Save Operation

First of all, what happens in a save operation should depend on whether the current file has been saved before. If it has, the user won't want to see a dialog every time. Once it has been saved the first time, the user will want it written away without displaying the dialog. We can use the modelFile member of the SketchFrame class to determine whether we need to display a dialog or not. We added this earlier to hold a reference to the File object for the sketch. Before a sketch has been saved this will be null, and if it is not null we don't want to show the dialog. When the sketch has been saved, we will store a reference to the File object in modelFile so this will generally hold a reference to the file holding the last version of the sketch that was saved.

Secondly, if the sketch is unchanged – indicated by the sketchChanged member being false – either because it is new and therefore empty or because it hasn't been altered since it was last saved, we really don't need to save it at all.

We can package up these checks for when we need to save and when we display the dialog in another method in the SketchFrame class. We'll call it saveOperation(), and make it a private member of the SketchFrame class:

// Save the sketch if it is necessary
private void saveOperation() {

  File file = modelFile;
  if(file == null) {
    file = showDialog("Save Sketch",
                      "Save the sketch",
                      new File(files.getCurrentDirectory(), filename));
    if(file == null ||  (file.exists() &&    // Check for existence
       JOptionPane.NO_OPTION ==          // Overwrite warning
                                    file.getName()+" exists. Overwrite?",
                                    "Confirm Save As",
          return;                            // No selected file

We first check the sketchChanged flag. If the flag is false, either the sketch is empty, or it hasn't been changed since the last save. Either way there's no point in writing it to disk, so we return immediately. We then initialize the local variable, file, with the reference stored in modelFile. If modelFile was not null, then we skip the entire if and just call the saveSketch() method to write the file to modelFile – we will get to the detail of the saveSketch() method in a moment.

The condition tested in the next if looks rather complicated but this is primarily due to the plethora of arguments in the showConfirmDialog() call and we can break the condition down into its component parts quite easily. There are two logical expressions separated by || so if either is true then we execute a return. The first expression just checks for file being null so if this is the case we return immediately. If the first expression is false, file is not null and the second expression will be evaluated.

This will be true if file references a file that does exist AND the value returned from the showConfirmDialog() method is JOptionPane.NO_OPTION. The dialog just warns of the overwrite potential so if JOptionPane.NO_OPTION is returned then the user has elected not to overwrite the file. Remember that with the || operator, if the left operand is true then the right operand will not be evaluated. Similarly, with the && operator, if the left operand is false then the right operand will not be evaluated. This means that the showConfirmDialog() method will only be executed if file is not null – so the left expression for the || is false – and file references a file that does exist – so the left operand for the && is true. If we don't execute the return, then we fall through the if to call our helper method saveSketch()with file as the argument.

Writing a Sketch to a File

Writing a sketch to a file just means making use of what we learned about writing objects to a file. We have already ensured that a SketchModel object is serializable, so we can write the sketch to an ObjectOutputStream with the following method in SketchFrame:

  // Write a sketch to outFile
  private void saveSketch(File outFile) {
    try {
      ObjectOutputStream out =  new ObjectOutputStream(
                                new BufferedOutputStream(
                                new FileOutputStream(outFile)));
      out.writeObject(theApp.getModel());        // Write the sketch to the
                                                 // stream
      out.close();                               // Flush & close it
    } catch(IOException e) {
                                    "Error writing a sketch file.",
                                    "File Output Error",
      return;                                    // Serious error - return
    if(outFile != modelFile) {             // If we are saving to a new file
                                           // we must update the window
      modelFile = outFile;                       // Save file reference
      filename = modelFile.getName();            // Update the file name
      setTitle(frameTitle + modelFile.getPath());  // Change the window title
    sketchChanged = false;                       // Set as unchanged

The saveSketch() method writes the current SketchModel object to the object output stream that we create from the File object that is passed to it. If an error occurs, an exception of type IOException will be thrown, in which case we write the exception to the standard error output stream, and pop up a dialog indicating that an error has occurred. We assume the user might want to retry the operation so we just return rather than terminate the application.

If the write operation succeeds, then we need to consider whether the data members in the window object relating to the file need to be updated. This will be the case when the File object passed to the saveSketch() method is not the same as the reference in modelFile. If this is so, we update the modelFile and filename members of the window object, and set the window title to reflect the new file name and path. In any event, the sketchChanged flag is reset to false, as the sketch is now safely stored away in the file.

We can now put together the code to handle the Save menu item event.

Try It Out – Saving a Sketch

The code to handle the Save menu item event will go in the actionPerformed() method of the inner class FileAction. We have done all the work, so it amounts to just one statement:

    public void actionPerformed(ActionEvent e) {
      String name = (String)getValue(NAME);
      if(name.equals(saveAction.getValue(NAME))) {
      // Plus the rest of the code for the method...

You can recompile Sketcher and run it again. The Save menu item and toolbar button should now be working. When you select either of them, you should get the dialog displayed below. This version has the look-and-feel of Windows of course. If you are using a different operating system it won't be exactly the same.

Click To expand

All the buttons in the dialog are fully operational. Go ahead and try them out, and then save the sketch using the default name. Next time you save the sketch the dialog won't appear. Be sure to check out the button tooltips. You should see the shortcut key combination as well as our tooltip text.

If you create a few sketch files, you should also get a warning if you attempt to overwrite an existing sketch file with a new one.

Click To expand

You can now save any sketch in a file – regardless of its complexity – with protection against accidentally overwriting existing files. I hope you agree that the save operation was remarkably easy to implement.

How It Works

This just calls our saveOperation() method in the SketchFrame object associated with the FileAction object containing the actionPerformed() method. This carries out the save as we have discussed.

Creating a File Filter

One customizing option we haven't used but you might like to try is to supply a file filter for our sketch files. The default filter in a JFileChooser object accepts any file or directory, but you can add filters of your own, and these implements two abstract methods declared in the FileFilter class:



accept(File file)

Returns true if the file represented by the object file is accepted by the file filter, and false otherwise.


Returns a String object describing the filter – for example, "Sketch files".

The FileFilter class is defined in the javax.swing.filechooser package. Note that there is a FileFilter interface defined in the package that declares just one method, accept(), which is the same as the method in the FileFilter class. This interface is for use with the listFiles() method that is defined in the File class, not for JFileChooser objects, so make sure you don't confuse them. The filter object that you use with a file chooser must have the FileFilter class as a superclass, and your filter class must define the getDescription() method as well as the accept() method.

We can define our own file filter class for Sketcher as:

import javax.swing.filechooser.FileFilter;

public class ExtensionFilter extends FileFilter {
  public ExtensionFilter(String ext, String descr) {
    extension = ext.toLowerCase();                 // Store the extension as
                                                   // lower case
    description = descr;                           // Store the description

  public boolean accept(File file) {

  public String getDescription() {
    return description;  

  private String description;                  // Filter description
  private String extension;                    // File extension

To create a filter for files with the extension .ske we could write:

ExtensionFilter sketchFilter = new ExtensionFilter(".ske",
                                                   "Sketch files (*.ske)");

If you add the source file to the Sketcher program directory, we could try this out by adding a little code to the showDialog() member of the SketchFrame class.

Try It Out – Using a File Filter

We only need to add three lines of code:

  private File showDialog(String dialogTitle,
JavaScript Editor
 Java Tutorials Free JavaScript Editor