Memra

Part 3 recap: the Swing control panel & a state machine

The GUI half: a multi-window Swing control panel with a scrollable log and a menu/button set whose enabled state is a small state machine, run off the EDT so the background controller threads never freeze the UI. Then the capstone checkpoint.

Part 3: a control panel for the controller

Part 3 wraps everything in a Swing GUI (Module 11): a window with a scrollable status log and controls (buttons + a File menu) that start, suspend, resume, open a file, and exit the greenhouse run. The interesting engineering is not the widgets — it is the state machine that decides which controls are enabled, and keeping the GUI responsive while the controller's threads run.

### The panel as a state machine The panel is in exactly one of three states, and each state fixes which controls are usable:

| State | Run | Open | Suspend | Resume | |---|---|---|---|---| | Stopped | enabled | enabled | disabled | disabled | | Running | disabled | disabled | enabled | disabled | | Suspended | disabled | disabled | disabled | enabled |

Every transition calls setEnabled(true/false) on the affected buttons, so the UI can never offer an illegal action (you cannot *Suspend* a stopped controller, cannot *Run* a running one). A single updateButtons(State s) method centralises the rule:

private void updateButtons(State s) {
    runButton.setEnabled(s == State.STOPPED);
    openButton.setEnabled(s == State.STOPPED);
    suspendButton.setEnabled(s == State.RUNNING);
    resumeButton.setEnabled(s == State.SUSPENDED);
}

### Wiring a button (anonymous inner class) Buttons fire ActionEvents to registered ActionListeners. Because a listener is an inner class, it can reach the panel's fields and call updateButtons directly (Module 2.5 / 11.2):

runButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        startController();           // kick off the controller threads
        updateButtons(State.RUNNING);
    }
});

### Keep work OFF the Event Dispatch Thread Swing is single-threaded: all UI updates happen on the Event Dispatch Thread (EDT). Two rules follow (Module 11.4):

  1. Never run long work on the EDTstartController() must launch the controller on a *background* thread (or SwingWorker), or the click handler would block and the window would freeze.
  2. Never touch Swing from a background thread — when a controller thread wants to append a line to the log text area, it must hop back onto the EDT with SwingUtilities.invokeLater:
SwingUtilities.invokeLater(() -> log.append(line + "\n"));

This is where the whole course converges: Module 10's threads do the work, Module 11's EDT rule keeps the UI live, Module 2's inner classes wire the callbacks, and the controller from Lesson 1 is what the panel drives.

Putting the capstone together You now have the full arc: an abstract Event + reusable Controller (the framework), inner-class events that mutate greenhouse state, regex/Scanner parsing of the event files, custom checked exceptions + logging + serialization for fault recovery, reflection to add events without recompiling, threads + synchronized + guarded suspend/resume for concurrency, and a Swing control panel — run safely off the EDT — to drive it all. Every advanced-Java topic in COMP 308 appears here, which is exactly why the exam treats this project as the spine.

NORMAL ~/memra/learn/comp-308/part-3-recap-and-checkpoint utf-8 LF