More GUIs

These are sample GUIs, packaged individually. Each is meant to be downloaded and installed as a Java Application with Existing Sources. See the Using NetBeans document for details.

Regex Matcher

The source archive is RegexMatcher.zip.

This introduces the key listener on text elements; when you type into a text element the controller can sense it. This example presents 3 labeled text fields:
regex
sampleString
report
Type a regular expression into the regex field first. Then as you type into the sampleString field, the report field tells whether what you have typed matches the regular expression, blanks count.

The KeyListener interface is introduced and applied to the sampleString textfield. You can play the same game as when you introduce an ActionListener:
frame.getSampleStringField().addKeyListener(new KeyListener());
The result is that you get 3 interface functions to define. The one you want to define is keyReleased and make sure the others are blank:
    frame.getSampleStringField().addKeyListener(new KeyListener() {
      @Override
      public void keyTyped(KeyEvent ke) {
      }
 
      @Override
      public void keyPressed(KeyEvent ke) {
      }
 
      @Override
      public void keyReleased(KeyEvent ke) {
        String regex = frame.getRegexField().getText();
        String test_str = frame.getSampleStringField().getText();
        String report;
        if (test_str.matches(regex)) {
          report = "yes";
        }
        else {
          report = "no";
        }
        frame.getReportField().setText(report);
      }
    });

You can omit the two unused ones if you then
replace KeyListener by KeyAdapter (a class which
implements the KeyListener interface).
    frame.getSampleStringField().addKeyListener(new KeyAdapter() {
      @Override
      public void keyReleased(KeyEvent ke) {
        String regex = frame.getRegexField().getText();
        String test_str = frame.getSampleStringField().getText();
        String report;
        if (test_str.matches(regex)) {
          report = "yes";
        }
        else {
          report = "no";
        }
        frame.getReportField().setText(report);
      }
    });
The full understanding of what is going on will be explained later in the course.

Flaws

There are 2 flaws in this application.
  1. if you change the regular expression, the report is no longer valid, and so it should be cleared
  2. if the regular expression is not a valid one, the application should pop up a message when something is typed into the sampleString field, but now it just throws an exception seen only in the output window.
Try to fix the first. We will address the second once we know about exception handling.

File Open/Save

The source archive is FileOpenSave.zip.

This application illustrates the following features: Run it like this:
  1. choose File ⇾ Open from the menu
  2. select sample.txt from the File Chooser, opening it and reading the content into the JTextArea
  3. modify the content as you like (or not)
  4. choose File ⇾ SaveAs from the menu
  5. enter a target file name, like output.txt and save
  6. verify the content of the files used through the Files window
  7. close the application and note the message prompt

Frame construction

The overall layout used in this frame is a BorderLayout which gives the application the ability to automatically resize the textarea when the application is resized.
  1. Create a new JFrame Form in the package views with class name TheFrame.
  2. (optional) Go into source mode, delete the main function and then back to design mode.
You can do the next 2 steps in either order. We are promoting here the concept: "drop something in the container first" before changing the layout, because sometimes the layout can layout change can cause the container to collapse.
  1. From Swing Controls in the Palette, drag a TextArea into the middle of the frame. It should expand and take up the entire frame.
  2. Right-click on the frame and select Set Layout ⇾ Border Layout. The TextArea will automatically become the "Center" portion.
Back to the development:
  1. Right-click on the TextArea, changing its variable name to content.
  2. From Swing Containers in the Palette, drag a Panel into the frame at the top of the frame. The layout should "open up" and allow you to drop the Panel into its own area at the top.
  3. Drag and drop a Label onto the Panel. Select and change the variable name to fileName.
  4. Right-click on the Panel and change the layout to FlowLayout (it's useful to add the label before changing the layout).
  5. Locate the JMenuBar in the Palette and drag it anywhere onto the application. NetBeans will automatically place it at the top with two default menu items:
    File    Edit
    
  6. Right-click and delete the Edit menu.
It is best to control adding and setting variable names of Menu Items through the Navigator window.
  1. Locate jMenuBar1 (the File menu), right-click and Change Variable Name to fileMenu.
  2. Right-click jMenuBar1 and Add From Palette 3 Menu items. Change the menu item names in succession to
    open
    saveAs
    other
    
  3. Expand the File menu in the Design window, and, using Edit Text, change the labels successively to:
    Open
    Save As
    Other
    
Finally, from source mode, add these methods as indicated before the TheFrame constructor:

basic.EditorStarterFrame
package views;
...
public class TheFrame extends javax.swing.JFrame {
 
  public JMenu getFileMenu() {
    return fileMenu;
  }
 
  public JMenuItem getOpenMenuItem() {
    return open;
  }
 
  public JMenuItem getSaveAsMenuItem() {
    return saveAs;
  }
 
  public JMenuItem getOtherMenuItem() {
    return other;
  }
 
  public JTextArea getContentTextArea() {
    return content;
  }  
  ...
}

Controller Constuction

  1. Start here, fixing imports.
    fileopensave.ControllerShow Hide

    fileopensave.Controller
    package fileopensave;
     
    public class Controller {
     
      private final TheFrame frame = new TheFrame();
     
      private boolean fileLoaded = false;
     
      private static JFileChooser getFileChooser() {
        JFileChooser chooser = new JFileChooser();
     
        // specify where chooser should open up
        chooser.setCurrentDirectory(new File(System.getProperty("user.dir")));
     
        // define a set of "Editable Files" files by extension
        chooser.addChoosableFileFilter(
                new FileNameExtensionFilter("Editable Files", "txt")
        );
     
        // do not accept "All Files"
        chooser.setAcceptAllFileFilterUsed(false);
     
        return chooser;
      }
     
      public Controller() {
     
        frame.setTitle(getClass().getSimpleName());
        frame.setLocationRelativeTo(null);
        frame.setSize(600, 500);
     
        // event handlers
      }
     
      public static void main(String[] args) {
        Controller app = new Controller();
        app.frame.setVisible(true);
      }
    }
    select
  2. Add Open handler (fixing imports) and test. Starter code:
        frame.getOpenMenuItem().addActionListener(new ActionListener());
    
    Experiment with modifications in the getFileChooser function.
    Open handlerShow Hide

    Open handler
        frame.getOpenMenuItem().addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            JFileChooser chooser = Controller.getFileChooser();
     
            // invoke the chooser dialog for opening a file
            int status = chooser.showOpenDialog(frame);
     
            // test for approval
            if (status != JFileChooser.APPROVE_OPTION) {
              return;
            }
            File file = chooser.getSelectedFile();
            Path path = file.toPath();
     
            try {
              System.out.println("\n--------- Load -------------");
     
              System.out.println("full path: " + path);
     
              Path working = Paths.get(System.getProperty("user.dir"));
     
              System.out.println("working path: " + working);
     
              Path relative = working.relativize(path);
     
              System.out.println("relative path: " + relative);
     
              String content = new String(Files.readAllBytes(path));
              frame.getContentTextArea().setText(content);
              frame.getContentTextArea().setCaretPosition(0);
     
              fileLoaded = true;
            }
            catch (IOException ex) {
              ex.printStackTrace(System.err);
              JOptionPane.showMessageDialog(frame, "Cannot open file " + file);
            }
          }
        });
    select
  3. Add Save As handler:
    Save As handlerShow Hide

    Save As handler
        frame.getSaveAsMenuItem().addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            JFileChooser chooser = Controller.getFileChooser();
     
            // invoke the chooser dialog for opening a file
            int status = chooser.showSaveDialog(frame);
     
            // test for approval
            if (status != JFileChooser.APPROVE_OPTION) {
              return;
            }
     
            File file = chooser.getSelectedFile();
            Path path = file.toPath();
            try {
              System.out.println("\n--------- Save to -------------");
     
              System.out.println("full path: " + path);
     
              Path working = Paths.get(System.getProperty("user.dir"));
     
              System.out.println("working path: " + working);
     
              Path relative = working.relativize(path);
     
              System.out.println("relative path: " + relative);
     
              String content = frame.getContentTextArea().getText();
              Files.write(path, content.getBytes());
            }
            catch (IOException ex) {
              ex.printStackTrace(System.err);
              JOptionPane.showMessageDialog(frame, "Cannot open file " + file);
            }
          }
        });
    select
  4. Add Other handler:
    Other handlerShow Hide

    Other handler
        frame.getOtherMenuItem().addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            System.out.println("\nOther MenuItem Activated");
          }
        });
    select
  5. Add File Menu handler. Start with:
        frame.getFileMenu().addMenuListener(new MenuListener());
    
    File menu handlerShow Hide

    File menu handler
        frame.getFileMenu().addMenuListener(new MenuListener() {
          @Override
          public void menuSelected(MenuEvent e) {
            System.out.println("\nFile Menu: Choose to enable/disable menu items");
            if (!fileLoaded) {
              frame.getOtherMenuItem().setEnabled(false);
            }
            else {
              frame.getOtherMenuItem().setEnabled(true);
            }
          }
     
          @Override
          public void menuDeselected(MenuEvent e) {
          }
     
          @Override
          public void menuCanceled(MenuEvent e) {
          }
        });
    select
  6. Add window closing handler. Start with
        frame.addWindowListener(new WindowListener());
    
    and change WindowListener to WindowAdapter. Experiment with the close operations.
    Window closing handlerShow Hide

    Window closing handler
        frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
     
        frame.addWindowListener(new WindowAdapter() {
          @Override
          public void windowClosing(WindowEvent evt) {
            JOptionPane.showMessageDialog(frame, "OK, I will let you exit");
            System.exit(0);
          }
        });
    select

File Chooser

When files are accessed through a GUI application, a dedicated GUI "file chooser" class is usually used. In the case of Java Swing, the class is:
javax.swing.JFileChooser
It is invoked in one of two ways:
int status = chooser.showOpenDialog(frame);
int status = chooser.showSaveDialog(frame);
Both activate a dialog in which the frame argument is used to block the GUI application until the dialog has completed. Upon completion an integer status is returned indicating what happened:
JFileChooser.APPROVE_OPTION
JFileChooser.CANCEL_OPTION
JFileChooser.ERROR_OPTION
Only the first of these indicates that a successful choice was made, after which we can retrieve the file selected:
File f = chooser.getSelectedFile();
The full code for "Open" looks like this:
int status = chooser.showOpenDialog(frame);
if (status != JFileChooser.APPROVE_OPTION) {
   return;
}
File f = chooser.getSelectedFile();
We use a static support member function to create and set initial properties of the file chooser, invoking the chooser when needed by:
JFileChooser chooser = Controller.getFileChooser();
The member function code is this:
  private static JFileChooser getFileChooser() {
    JFileChooser chooser = new JFileChooser();
 
    // 1. specify where chooser should open up
    chooser.setCurrentDirectory(new File(System.getProperty("user.dir")));
 
    // 2. define a set of "Editable Files" files by extension
    chooser.addChoosableFileFilter(
            new FileNameExtensionFilter("Editable Files", "txt")
    );
 
    // 3. do not accept "All Files"
    chooser.setAcceptAllFileFilterUsed(false);
 
    return chooser;
  }
If you comment out these lines, you'll see what the file chooser does without any management. Our management code is this: The effects are that:
  1. Make the chooser open up the application's working directory. There are a variety of system string properties which can be employed. The default directory is the user.home, the user's home directory.
  2. Direct the user to "see" certain file types according to extension. This adds a drop-down choice in the file chooser to see only files with the desired extensions. We could have set
    FileFilter filter 
     = new FileNameExtensionFilter("Editable Files", "txt", "text", "java");
    The constructor permits multiple arguments, so add others as you like. Be aware that the File Chooser component for different Operating Systems (like the MAC) presents quite different ways to "see" the available files.
  3. The added file filter still allows "All Files" to appear. The last line makes the "All Files" unavailable, forcing the user to the choices we want.

Read/Write with TextArea

We have already discussed the file read/write constructions. In this case it is done to/from the TextArea. Reading is:
File file = chooser.getSelectedFile();
Path path = file.toPath();
 
try {
  System.out.println("\n--------- Load -------------");
 
  System.out.println("full path: " + path);
 
  Path working = Paths.get(System.getProperty("user.dir"));
 
  System.out.println("working path: " + working);
 
  Path relative = working.relativize(path);
 
  System.out.println("relative path: " + relative);
 
  String content = new String(Files.readAllBytes(path));
  frame.getContentTextArea().setText(content);
  frame.getContentTextArea().setCaretPosition(0);
 
  fileLoaded = true;
}
catch (IOException ex) {
  ex.printStackTrace(System.err);
  JOptionPane.showMessageDialog(frame, "Cannot open file " + file);
}
Writing is:
File file = chooser.getSelectedFile();
Path path = file.toPath();
try {
  System.out.println("\n--------- Save to -------------");
 
  System.out.println("full path: " + path);
 
  Path working = Paths.get(System.getProperty("user.dir"));
 
  System.out.println("working path: " + working);
 
  Path relative = working.relativize(path);
 
  System.out.println("relative path: " + relative);
 
  String content = frame.getContentTextArea().getText();
  Files.write(path, content.getBytes());
}
catch (IOException ex) {
  ex.printStackTrace(System.err);
  JOptionPane.showMessageDialog(frame, "Cannot open file " + file);
}
In either case, an error will pop up a dialog warning the user about what went wrong. It will also dump the error into the output for further details.

JMenus, JMenuItems and Listeners

A JMenuItem behaves like a JButton in that it supports an ActionListener. The obvious advantage of a menu item over a button is that need not be take up space in the application.

Another, more subtle advantage of a menu item is that it can more easily be enabled/disabled. To access a menu item you first must access the menu containing the menu item, which can be sensed as we do in the code:
frame.getFileMenu().addMenuListener(new MenuListener() {
  @Override
  public void menuSelected(MenuEvent e) {
    // THE MENU IS ACTIVATED
  }
 
  @Override
  public void menuDeselected(MenuEvent e) {
  }
 
  @Override
  public void menuCanceled(MenuEvent e) {
  }
});
The menuSelected function gives the programmer the opportunity to read the application state variables and determine which menu items within should be enabled or disabled. In contrast, it's much harder to manage the enabling/disabling of a button since every other event might influence whether the button enabled status should change.

For testing purposes, our application enables the "Other" menu item only after the "Open" has successfully completed. The mechanism to control this is the single boolean data member:
private boolean fileLoaded = false;
Upon successfully opening a file, this is reset:
fileLoaded = true;
The menu handler function is simply:
public void menuSelected(MenuEvent e) {
  System.out.println("File Menu: Choose to enable/disable menu items");
  if (!fileLoaded) {
    frame.getOtherMenuItem().setEnabled(false);
  }
  else {
    frame.getOtherMenuItem().setEnabled(true);
  }
}

Window Closing

The last feature of this application is the ability to detect and manage a user attempting to close the application. The code used is:
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
 
frame.addWindowListener(new WindowAdapter(){
  @Override
  public void windowClosing(WindowEvent evt) {
    JOptionPane.showMessageDialog(frame, "OK, I will let you exit");
    System.exit(0);
  }
});
The first step is to tell the frame not to exit when the user tries to close it. This example can do without it, but you need it in general. The choices for the setDefaultCloseOperation are these static final constants defined in the WindowConstants class:
DISPOSE_ON_CLOSE      Make window disappear: visible=false (default).
DO_NOTHING_ON_CLOSE   Do nothing.
EXIT_ON_CLOSE         Exit the application.
HIDE_ON_CLOSE         "Tool-bar" the window.
A JFrame generated by NetBeans' JFrame Form will be set to use the EXIT_ON_CLOSE choice.

Changing this to DO_NOTHING_ON_CLOSE means that the programmer must manage exiting the application, allowing the user to exit only under the right circumstances.

Managing window closing means to write code for the appropriate WindowEvent handler member:
public void windowClosing(WindowEvent evt)
As we did with KeyListener in the Regex Match application, the handler object is based on the WindowAdapter class, which is an implementation of the WindowListener interface. A direct implementation of the WindowListener interface must define 7 handler functions, and so the WindowAdapter offers a significant code simplification.

Window Closing in MAC OS X

MAC OS X gives an alternative way to close the application by pressing Command-Q. By default, Command-Q will bypass the window closing check we have put in. However you can force it to use the closing check by adding this to the main function:
  public static void main(String[] args) {
 
    // handle Command-Q on Mac through window-closing
    System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
 
    Controller app = new Controller();
    app.frame.setVisible(true);
  }


© Robert M. Kline